mirror of
https://github.com/Cameri/nostream.git
synced 2025-05-03 22:50:16 +02:00
229 lines
7.6 KiB
TypeScript
229 lines
7.6 KiB
TypeScript
import chai from 'chai'
|
|
import chaiAsPromised from 'chai-as-promised'
|
|
import EventEmitter from 'events'
|
|
import Sinon from 'sinon'
|
|
import sinonChai from 'sinon-chai'
|
|
|
|
chai.use(sinonChai)
|
|
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 { IUserRepository } from '../../../src/@types/repositories'
|
|
import { WebSocketAdapterEvent } from '../../../src/constants/adapter'
|
|
|
|
const { expect } = chai
|
|
|
|
describe('DelegatedEventMessageHandler', () => {
|
|
let webSocket: EventEmitter
|
|
let handler: DelegatedEventMessageHandler
|
|
let userRepository: IUserRepository
|
|
let event: Event
|
|
let message: IncomingEventMessage
|
|
let sandbox: Sinon.SinonSandbox
|
|
|
|
let originalConsoleWarn: any = 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
|
|
let isRateLimitedStub: Sinon.SinonStub
|
|
let isUserAdmitted: Sinon.SinonStub
|
|
|
|
beforeEach(() => {
|
|
canAcceptEventStub = sandbox.stub(DelegatedEventMessageHandler.prototype, 'canAcceptEvent' as any)
|
|
isEventValidStub = sandbox.stub(DelegatedEventMessageHandler.prototype, 'isEventValid' as any)
|
|
isUserAdmitted = sandbox.stub(EventMessageHandler.prototype, 'isUserAdmitted' 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]
|
|
isRateLimitedStub = sandbox.stub(EventMessageHandler.prototype, 'isRateLimited' as any)
|
|
handler = new DelegatedEventMessageHandler(
|
|
webSocket as any,
|
|
strategyFactoryStub,
|
|
userRepository,
|
|
() => ({}) as any,
|
|
() => ({ hit: async () => false }),
|
|
)
|
|
})
|
|
|
|
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(
|
|
[
|
|
MessageType.OK,
|
|
'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
|
|
false,
|
|
'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('rejects event if rate-limited', async () => {
|
|
isRateLimitedStub.resolves(true)
|
|
|
|
await handler.handleMessage(message)
|
|
|
|
expect(isRateLimitedStub).to.have.been.calledOnceWithExactly(event)
|
|
expect(onMessageSpy).to.have.been.calledOnceWithExactly(
|
|
[MessageType.OK, event.id, false, 'rate-limited: slow down'],
|
|
)
|
|
expect(strategyFactoryStub).not.to.have.been.called
|
|
})
|
|
|
|
it('rejects event is user is not admitted', async () => {
|
|
isUserAdmitted.resolves('not admitted')
|
|
|
|
await handler.handleMessage(message)
|
|
|
|
expect(isRateLimitedStub).to.have.been.calledOnceWithExactly(event)
|
|
expect(isUserAdmitted).to.have.been.calledOnceWithExactly(event)
|
|
expect(onMessageSpy).to.have.been.calledOnceWithExactly([
|
|
MessageType.OK,
|
|
event.id,
|
|
false,
|
|
'not admitted',
|
|
])
|
|
})
|
|
|
|
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 () => {
|
|
const error = new Error('mistakes were made')
|
|
isEventValidStub.returns(undefined)
|
|
canAcceptEventStub.returns(undefined)
|
|
strategyExecuteStub.rejects(error)
|
|
|
|
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('invalid: delegation verification failed')
|
|
})
|
|
})
|
|
})
|