Skip to content

Commit edece5a

Browse files
test(wot): add unit tests for wot graph service and ingestion
Signed-off-by: Priyanshubhartistm <bhartipriyanshustm@gmail.com>
1 parent a0f71d0 commit edece5a

5 files changed

Lines changed: 425 additions & 3 deletions

File tree

‎test/unit/adapters/redis-adapter.spec.ts‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ describe('RedisAdapter', () => {
3737
del: sandbox.stub(),
3838
hGet: sandbox.stub(),
3939
hSet: sandbox.stub(),
40+
sAdd: sandbox.stub(),
41+
sMembers: sandbox.stub(),
4042
scriptLoad: sandbox.stub(),
4143
evalSha: sandbox.stub(),
4244
isOpen: false,
@@ -260,6 +262,28 @@ describe('RedisAdapter', () => {
260262
})
261263
})
262264

265+
describe('addToSet', () => {
266+
it('calls client.sAdd with key and members and returns the count added', async () => {
267+
client.sAdd.resolves(2)
268+
269+
const result = await adapter.addToSet('test-key', ['member1', 'member2'])
270+
271+
expect(client.sAdd).to.have.been.calledOnceWithExactly('test-key', ['member1', 'member2'])
272+
expect(result).to.equal(2)
273+
})
274+
})
275+
276+
describe('getSetMembers', () => {
277+
it('calls client.sMembers with the key and returns the members', async () => {
278+
client.sMembers.resolves(['member1', 'member2'])
279+
280+
const result = await adapter.getSetMembers('test-key')
281+
282+
expect(client.sMembers).to.have.been.calledOnceWithExactly('test-key')
283+
expect(result).to.deep.equal(['member1', 'member2'])
284+
})
285+
})
286+
263287
describe('eval', () => {
264288
it('loads script on first call and uses evalSha', async () => {
265289
client.scriptLoad.resolves('abc123sha')

‎test/unit/factories/event-strategy-factory.spec.ts‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
IInviteCodeRepository,
77
IUserRepository,
88
} from '../../../src/@types/repositories'
9+
import { ContactListEventStrategy } from '../../../src/handlers/event-strategies/contact-list-event-strategy'
910
import { DefaultEventStrategy } from '../../../src/handlers/event-strategies/default-event-strategy'
1011
import { DeleteEventStrategy } from '../../../src/handlers/event-strategies/delete-event-strategy'
1112
import { DvmJobRequestEventStrategy } from '../../../src/handlers/event-strategies/dvm-job-request-event-strategy'
@@ -43,7 +44,7 @@ describe('eventStrategyFactory', () => {
4344
inviteCodeRepository = {} as any
4445
dvmJobRepository = {} as any
4546
cache = {} as any
46-
settings = () => ({ info: { relay_url: 'wss://test.relay' } }) as any
47+
settings = () => ({ info: { relay_url: 'wss://test.relay' }, wot: { enabled: false } }) as any
4748
event = {} as any
4849
adapter = {} as any
4950

@@ -62,9 +63,9 @@ describe('eventStrategyFactory', () => {
6263
expect(factory([event, adapter])).to.be.an.instanceOf(ReplaceableEventStrategy)
6364
})
6465

65-
it('returns ReplaceableEvent given a contact_list event', () => {
66+
it('returns ContactListEventStrategy given a contact_list event', () => {
6667
event.kind = EventKinds.CONTACT_LIST
67-
expect(factory([event, adapter])).to.be.an.instanceOf(ReplaceableEventStrategy)
68+
expect(factory([event, adapter])).to.be.an.instanceOf(ContactListEventStrategy)
6869
})
6970

7071
it('returns ReplaceableEvent given a replaceable event', () => {
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
import chai from 'chai'
2+
import chaiAsPromised from 'chai-as-promised'
3+
import Sinon from 'sinon'
4+
5+
chai.use(chaiAsPromised)
6+
7+
const { expect } = chai
8+
9+
import { ContactListEventStrategy } from '../../../../src/handlers/event-strategies/contact-list-event-strategy'
10+
import { Event } from '../../../../src/@types/event'
11+
import { IEventRepository } from '../../../../src/@types/repositories'
12+
import { IEventStrategy } from '../../../../src/@types/message-handlers'
13+
import { IWebSocketAdapter } from '../../../../src/@types/adapters'
14+
import { IWotGraphService } from '../../../../src/@types/services'
15+
import { MessageType } from '../../../../src/@types/messages'
16+
import { WebSocketAdapterEvent } from '../../../../src/constants/adapter'
17+
18+
describe('ContactListEventStrategy', () => {
19+
const event: Event = {
20+
id: 'event-id',
21+
pubkey: 'author-pubkey',
22+
kind: 3,
23+
tags: [
24+
['p', 'followed-1'],
25+
['p', 'followed-2'],
26+
['e', 'not-a-follow'],
27+
],
28+
} as any
29+
30+
let webSocket: IWebSocketAdapter
31+
let eventRepository: IEventRepository
32+
let wotGraphService: IWotGraphService
33+
34+
let webSocketEmitStub: Sinon.SinonStub
35+
let eventRepositoryUpsertStub: Sinon.SinonStub
36+
let updateFollowListStub: Sinon.SinonStub
37+
38+
let strategy: IEventStrategy<Event, Promise<void>>
39+
40+
let sandbox: Sinon.SinonSandbox
41+
42+
beforeEach(() => {
43+
sandbox = Sinon.createSandbox()
44+
45+
webSocketEmitStub = sandbox.stub()
46+
webSocket = {
47+
emit: webSocketEmitStub,
48+
} as any
49+
50+
eventRepositoryUpsertStub = sandbox.stub()
51+
eventRepository = {
52+
upsert: eventRepositoryUpsertStub,
53+
} as any
54+
55+
updateFollowListStub = sandbox.stub()
56+
wotGraphService = {
57+
updateFollowList: updateFollowListStub,
58+
} as any
59+
60+
strategy = new ContactListEventStrategy(webSocket, eventRepository, wotGraphService)
61+
})
62+
63+
afterEach(() => {
64+
sandbox.restore()
65+
})
66+
67+
describe('execute', () => {
68+
it('upserts the event', async () => {
69+
eventRepositoryUpsertStub.resolves(1)
70+
updateFollowListStub.resolves()
71+
72+
await strategy.execute(event)
73+
74+
expect(eventRepositoryUpsertStub).to.have.been.calledOnceWithExactly(event)
75+
})
76+
77+
it('updates the wot graph with the p-tagged pubkeys when newly stored', async () => {
78+
eventRepositoryUpsertStub.resolves(1)
79+
updateFollowListStub.resolves()
80+
81+
await strategy.execute(event)
82+
83+
expect(updateFollowListStub).to.have.been.calledOnceWithExactly('author-pubkey', ['followed-1', 'followed-2'])
84+
})
85+
86+
it('broadcasts the event when newly stored', async () => {
87+
eventRepositoryUpsertStub.resolves(1)
88+
updateFollowListStub.resolves()
89+
90+
await strategy.execute(event)
91+
92+
expect(webSocketEmitStub).to.have.been.calledWithExactly(WebSocketAdapterEvent.Message, [
93+
MessageType.OK,
94+
'event-id',
95+
true,
96+
'',
97+
])
98+
expect(webSocketEmitStub).to.have.been.calledWithExactly(WebSocketAdapterEvent.Broadcast, event)
99+
})
100+
101+
it('does not broadcast or update the wot graph when the event is a duplicate', async () => {
102+
eventRepositoryUpsertStub.resolves(0)
103+
104+
await strategy.execute(event)
105+
106+
expect(webSocketEmitStub).to.have.been.calledOnceWithExactly(WebSocketAdapterEvent.Message, [
107+
MessageType.OK,
108+
'event-id',
109+
true,
110+
'duplicate:',
111+
])
112+
expect(updateFollowListStub).not.to.have.been.called
113+
})
114+
115+
it('does not fail the event when the wot graph update fails', async () => {
116+
eventRepositoryUpsertStub.resolves(1)
117+
updateFollowListStub.rejects(new Error('cache unavailable'))
118+
119+
await expect(strategy.execute(event)).to.eventually.be.fulfilled
120+
121+
expect(webSocketEmitStub).to.have.been.calledWithExactly(WebSocketAdapterEvent.Message, [
122+
MessageType.OK,
123+
'event-id',
124+
true,
125+
'',
126+
])
127+
})
128+
129+
it('emits a rejection when the event already exists', async () => {
130+
eventRepositoryUpsertStub.rejects(
131+
new Error('duplicate key value violates unique constraint "events_event_id_unique"'),
132+
)
133+
134+
await strategy.execute(event)
135+
136+
expect(webSocketEmitStub).to.have.been.calledOnceWithExactly(WebSocketAdapterEvent.Message, [
137+
MessageType.OK,
138+
'event-id',
139+
false,
140+
'rejected: event already exists',
141+
])
142+
expect(updateFollowListStub).not.to.have.been.called
143+
})
144+
145+
it('emits an error message on unexpected upsert failure', async () => {
146+
eventRepositoryUpsertStub.rejects(new Error('connection reset'))
147+
148+
await strategy.execute(event)
149+
150+
expect(webSocketEmitStub).to.have.been.calledOnceWithExactly(WebSocketAdapterEvent.Message, [
151+
MessageType.OK,
152+
'event-id',
153+
false,
154+
'error: connection reset',
155+
])
156+
})
157+
})
158+
})

0 commit comments

Comments
 (0)