Skip to content

Commit

Permalink
test: delegated event message handler
Browse files Browse the repository at this point in the history
  • Loading branch information
cameri committed Oct 21, 2022
1 parent ca1c910 commit d65da01
Show file tree
Hide file tree
Showing 3 changed files with 198 additions and 5 deletions.
3 changes: 3 additions & 0 deletions src/@types/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ export interface Event {
tags: Tag[]
sig: string
content: string
}

export interface DelegatedEvent extends Event {
[EventDelegatorMetadataKey]?: Pubkey
}

Expand Down
17 changes: 12 additions & 5 deletions src/handlers/delegated-event-message-handler.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { mergeDeepLeft } from 'ramda'

import { DelegatedEvent, Event } from '../@types/event'
import { EventDelegatorMetadataKey, EventTags } from '../constants/base'
import { createNoticeMessage } from '../utils/messages'
import { Event } from '../@types/event'
import { EventMessageHandler } from './event-message-handler'
import { IMessageHandler } from '../@types/message-handlers'
import { IncomingEventMessage } from '../@types/messages'
Expand All @@ -25,23 +27,28 @@ export class DelegatedEventMessageHandler extends EventMessageHandler implements
}

const [, delegator] = event.tags.find((tag) => tag.length === 4 && tag[0] === EventTags.Delegation)
event[EventDelegatorMetadataKey] = delegator
const delegatedEvent: DelegatedEvent = mergeDeepLeft(
event,
{
[EventDelegatorMetadataKey]: delegator,
}
)

const strategy = this.strategyFactory([event, this.webSocket])
const strategy = this.strategyFactory([delegatedEvent, this.webSocket])

if (typeof strategy?.execute !== 'function') {
return
}

try {
await strategy.execute(event)
await strategy.execute(delegatedEvent)
} catch (error) {
console.error('Error handling message:', message, error)
}
}

protected async isEventValid(event: Event): Promise<string | undefined> {
const reason = super.isEventValid(event)
const reason = await super.isEventValid(event)
if (reason) {
return reason
}
Expand Down
183 changes: 183 additions & 0 deletions test/unit/handlers/delegated-event-message-handler.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
import chai from 'chai'
import chaiAsPromised from 'chai-as-promised'
import EventEmitter from 'events'
import Sinon from 'sinon'

chai.use(chaiAsPromised)

import { IncomingEventMessage, MessageType } from '../../../src/@types/messages'
import { DelegatedEventMessageHandler } from '../../../src/handlers/delegated-event-message-handler'
import { Event } from '../../../src/@types/event'
import { EventMessageHandler } from '../../../src/handlers/event-message-handler'
import { WebSocketAdapterEvent } from '../../../src/constants/adapter'

const { expect } = chai

describe('DelegatedEventMessageHandler', () => {
let webSocket: EventEmitter
let handler: DelegatedEventMessageHandler
let event: Event
let message: IncomingEventMessage
let sandbox: Sinon.SinonSandbox

let originalConsoleWarn: (message?: any, ...optionalParams: any[]) => void | undefined = undefined

beforeEach(() => {
sandbox = Sinon.createSandbox()
originalConsoleWarn = console.warn
console.warn = () => undefined
event = {
content: 'hello',
created_at: 1665546189,
id: 'f'.repeat(64),
kind: 1,
pubkey: 'f'.repeat(64),
sig: 'f'.repeat(128),
tags: [
['delegation', 'delegator', 'rune', 'signature'],
],
}
})

afterEach(() => {
console.warn = originalConsoleWarn
sandbox.restore()
})

describe('handleMessage', () => {
let canAcceptEventStub: Sinon.SinonStub
let isEventValidStub: Sinon.SinonStub
let strategyFactoryStub: Sinon.SinonStub
let onMessageSpy: Sinon.SinonSpy
let strategyExecuteStub: Sinon.SinonStub

beforeEach(() => {
canAcceptEventStub = sandbox.stub(DelegatedEventMessageHandler.prototype, 'canAcceptEvent' as any)
isEventValidStub = sandbox.stub(DelegatedEventMessageHandler.prototype, 'isEventValid' as any)
strategyExecuteStub = sandbox.stub()
strategyFactoryStub = sandbox.stub().returns({
execute: strategyExecuteStub,
})
onMessageSpy = sandbox.fake.returns(undefined)
webSocket = new EventEmitter()
webSocket.on(WebSocketAdapterEvent.Message, onMessageSpy)
message = [MessageType.EVENT, event]
handler = new DelegatedEventMessageHandler(
webSocket as any,
strategyFactoryStub,
() => ({}) as any,
)
})

afterEach(() => {
isEventValidStub.restore()
canAcceptEventStub.restore()
webSocket.removeAllListeners()
})

it('rejects event if it can\'t be accepted', async () => {
canAcceptEventStub.returns('reason')

await handler.handleMessage(message)

expect(canAcceptEventStub).to.have.been.calledOnceWithExactly(event)
expect(onMessageSpy).to.have.been.calledOnceWithExactly(['NOTICE', 'Event rejected: reason'])
expect(strategyFactoryStub).not.to.have.been.called
})

it('rejects event if invalid', async () => {
isEventValidStub.returns('reason')

await handler.handleMessage(message)

expect(isEventValidStub).to.have.been.calledOnceWithExactly(event)
expect(onMessageSpy).not.to.have.been.calledOnceWithExactly()
expect(strategyFactoryStub).not.to.have.been.called
})

it('does not call strategy if none given', async () => {
isEventValidStub.returns(undefined)
canAcceptEventStub.returns(undefined)
strategyFactoryStub.returns(undefined)

await handler.handleMessage(message)

expect(isEventValidStub).to.have.been.calledOnceWithExactly(event)
expect(onMessageSpy).not.to.have.been.calledOnceWithExactly()
expect(strategyFactoryStub).to.have.been.calledOnceWithExactly([
event,
webSocket,
])
expect(strategyExecuteStub).not.to.have.been.called
})

it('calls strategy with event', async () => {
isEventValidStub.returns(undefined)
canAcceptEventStub.returns(undefined)

await handler.handleMessage(message)

expect(isEventValidStub).to.have.been.calledOnceWithExactly(event)
expect(onMessageSpy).not.to.have.been.calledOnceWithExactly()
expect(strategyFactoryStub).to.have.been.calledOnceWithExactly([
event,
webSocket,
])
expect(strategyExecuteStub).to.have.been.calledOnceWithExactly(event)
})

it('does not reject if strategy rejects', async () => {
isEventValidStub.returns(undefined)
canAcceptEventStub.returns(undefined)
strategyExecuteStub.rejects()

return expect(handler.handleMessage(message)).to.eventually.be.fulfilled
})
})

describe('isEventValid', () => {
let parentIsEventValidStub: Sinon.SinonStub

beforeEach(() => {
parentIsEventValidStub = Sinon.stub(EventMessageHandler.prototype, 'isEventValid' as any)
event = {
'id': 'a080fd288b60ac2225ff2e2d815291bd730911e583e177302cc949a15dc2b2dc',
'pubkey': '62903b1ff41559daf9ee98ef1ae67cc52f301bb5ce26d14baba3052f649c3f49',
'created_at': 1660896109,
'kind': 1,
'tags': [
[
'delegation',
'86f0689bd48dcd19c67a19d994f938ee34f251d8c39976290955ff585f2db42e',
'kind=1&created_at>1640995200',
'c33c88ba78ec3c760e49db591ac5f7b129e3887c8af7729795e85a0588007e5ac89b46549232d8f918eefd73e726cb450135314bfda419c030d0b6affe401ec1',
],
],
'content': 'Hello world',
'sig': 'cd4a3cd20dc61dcbc98324de561a07fd23b3d9702115920c0814b5fb822cc5b7c5bcdaf3fa326d24ed50c5b9c8214d66c75bae34e3a84c25e4d122afccb66eb6',
}
})

afterEach(() => {
parentIsEventValidStub.restore()
})

it('returns undefined if event and delegate tag is valid', async () => {
parentIsEventValidStub.resolves(undefined)

expect(await (handler as any).isEventValid(event)).to.be.undefined
})

it('returns reason if event is not valid', () => {
parentIsEventValidStub.resolves('reason')
return expect((handler as any).isEventValid(event)).to.eventually.equal('reason')
})

it('returns reason if delegate signature is not valid', () => {
parentIsEventValidStub.resolves(undefined)

event.tags[0][3] = 'wrong sig'
return expect((handler as any).isEventValid(event)).to.eventually.equal('Event with id a080fd288b60ac2225ff2e2d815291bd730911e583e177302cc949a15dc2b2dc from 62903b1ff41559daf9ee98ef1ae67cc52f301bb5ce26d14baba3052f649c3f49 is invalid delegated event')
})
})
})

0 comments on commit d65da01

Please sign in to comment.