mirror of
https://github.com/Cameri/nostream.git
synced 2025-09-21 14:10:40 +02:00
test: add unit test for factories
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
export enum EventKinds {
|
export enum EventKinds {
|
||||||
SET_METADATA = 0,
|
SET_METADATA = 0,
|
||||||
TEXT_NODE = 1,
|
TEXT_NOTE = 1,
|
||||||
RECOMMEND_SERVER = 2,
|
RECOMMEND_SERVER = 2,
|
||||||
CONTACT_LIST = 3,
|
CONTACT_LIST = 3,
|
||||||
ENCRYPTED_DIRECT_MESSAGE = 4,
|
ENCRYPTED_DIRECT_MESSAGE = 4,
|
||||||
@@ -10,6 +10,8 @@ export enum EventKinds {
|
|||||||
REPLACEABLE_LAST = 19999,
|
REPLACEABLE_LAST = 19999,
|
||||||
EPHEMERAL_FIRST = 20000,
|
EPHEMERAL_FIRST = 20000,
|
||||||
EPHEMERAL_LAST = 29999,
|
EPHEMERAL_LAST = 29999,
|
||||||
|
PARAMETERIZED_REPLACEABLE_FIRST = 30000,
|
||||||
|
PARAMETERIZED_REPLACEABLE_LAST = 39999,
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum EventTags {
|
export enum EventTags {
|
||||||
|
@@ -31,6 +31,6 @@ export const messageHandlerFactory = (
|
|||||||
case MessageType.CLOSE:
|
case MessageType.CLOSE:
|
||||||
return new UnsubscribeMessageHandler(adapter,)
|
return new UnsubscribeMessageHandler(adapter,)
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unknown message type: ${String(message[0])}`)
|
throw new Error(`Unknown message type: ${String(message[0]).substring(0, 64)}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -222,11 +222,11 @@ export class EventRepository implements IEventRepository {
|
|||||||
.merge(omit(['event_pubkey', 'event_kind', 'event_deduplication'])(row))
|
.merge(omit(['event_pubkey', 'event_kind', 'event_deduplication'])(row))
|
||||||
.where('events.event_created_at', '<', row.event_created_at)
|
.where('events.event_created_at', '<', row.event_created_at)
|
||||||
|
|
||||||
const promise = query.then(prop('rowCount') as () => number)
|
return {
|
||||||
|
then: <T1, T2>(onfulfilled: (value: number) => T1 | PromiseLike<T1>, onrejected: (reason: any) => T2 | PromiseLike<T2>) => query.then(prop('rowCount') as () => number).then(onfulfilled, onrejected),
|
||||||
promise.toString = () => query.toString()
|
catch: <T>(onrejected: (reason: any) => T | PromiseLike<T>) => query.catch(onrejected),
|
||||||
|
toString: (): string => query.toString(),
|
||||||
return promise
|
} as Promise<number>
|
||||||
}
|
}
|
||||||
|
|
||||||
public deleteByPubkeyAndIds(pubkey: string, ids: EventId[]): Promise<number> {
|
public deleteByPubkeyAndIds(pubkey: string, ids: EventId[]): Promise<number> {
|
||||||
|
@@ -167,15 +167,18 @@ export const isEventSignatureValid = async (event: Event): Promise<boolean> => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const isReplaceableEvent = (event: Event): boolean => {
|
export const isReplaceableEvent = (event: Event): boolean => {
|
||||||
return event.kind === 0 || event.kind === 3 || (event.kind >= 10000 && event.kind < 20000)
|
return event.kind === EventKinds.SET_METADATA
|
||||||
|
|| event.kind === EventKinds.CONTACT_LIST
|
||||||
|
|| (event.kind >= EventKinds.REPLACEABLE_FIRST && event.kind <= EventKinds.REPLACEABLE_LAST)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const isEphemeralEvent = (event: Event): boolean => {
|
export const isEphemeralEvent = (event: Event): boolean => {
|
||||||
return event.kind >= 20000 && event.kind < 30000
|
return event.kind >= EventKinds.EPHEMERAL_FIRST && event.kind <= EventKinds.EPHEMERAL_LAST
|
||||||
}
|
}
|
||||||
|
|
||||||
export const isParameterizedReplaceableEvent = (event: Event): boolean => {
|
export const isParameterizedReplaceableEvent = (event: Event): boolean => {
|
||||||
return event.kind >= 30000 && event.kind < 40000
|
return event.kind >= EventKinds.PARAMETERIZED_REPLACEABLE_FIRST
|
||||||
|
&& event.kind <= EventKinds.PARAMETERIZED_REPLACEABLE_LAST
|
||||||
}
|
}
|
||||||
|
|
||||||
export const isDeleteEvent = (event: Event): boolean => {
|
export const isDeleteEvent = (event: Event): boolean => {
|
||||||
|
10
test/unit/factories/app-factory.spec.ts
Normal file
10
test/unit/factories/app-factory.spec.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { expect } from 'chai'
|
||||||
|
|
||||||
|
import { App } from '../../../src/app/app'
|
||||||
|
import { appFactory } from '../../../src/factories/app-factory'
|
||||||
|
|
||||||
|
describe('appFactory', () => {
|
||||||
|
it('returns an App', () => {
|
||||||
|
expect(appFactory()).to.be.an.instanceOf(App)
|
||||||
|
})
|
||||||
|
})
|
46
test/unit/factories/delegated-event-strategy-factory.spec.ts
Normal file
46
test/unit/factories/delegated-event-strategy-factory.spec.ts
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import { expect } from 'chai'
|
||||||
|
|
||||||
|
import { DefaultEventStrategy } from '../../../src/handlers/event-strategies/default-event-strategy'
|
||||||
|
import { delegatedEventStrategyFactory } from '../../../src/factories/delegated-event-strategy-factory'
|
||||||
|
import { EphemeralEventStrategy } from '../../../src/handlers/event-strategies/ephemeral-event-strategy'
|
||||||
|
import { Event } from '../../../src/@types/event'
|
||||||
|
import { EventKinds } from '../../../src/constants/base'
|
||||||
|
import { Factory } from '../../../src/@types/base'
|
||||||
|
import { IEventRepository } from '../../../src/@types/repositories'
|
||||||
|
import { IEventStrategy } from '../../../src/@types/message-handlers'
|
||||||
|
import { IWebSocketAdapter } from '../../../src/@types/adapters'
|
||||||
|
|
||||||
|
describe('delegatedEventStrategyFactory', () => {
|
||||||
|
let eventRepository: IEventRepository
|
||||||
|
let event: Event
|
||||||
|
let adapter: IWebSocketAdapter
|
||||||
|
let factory: Factory<IEventStrategy<Event, Promise<void>>, [Event, IWebSocketAdapter]>
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
eventRepository = {} as any
|
||||||
|
event = {} as any
|
||||||
|
adapter = {} as any
|
||||||
|
|
||||||
|
factory = delegatedEventStrategyFactory(eventRepository)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns EphemeralEventStrategy given a set_metadata event', () => {
|
||||||
|
event.kind = EventKinds.EPHEMERAL_FIRST
|
||||||
|
expect(factory([event, adapter])).to.be.an.instanceOf(EphemeralEventStrategy)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns DefaultEventStrategy given a text_note event', () => {
|
||||||
|
event.kind = EventKinds.TEXT_NOTE
|
||||||
|
expect(factory([event, adapter])).to.be.an.instanceOf(DefaultEventStrategy)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns undefined given a replaceable event', () => {
|
||||||
|
event.kind = EventKinds.REPLACEABLE_FIRST
|
||||||
|
expect(factory([event, adapter])).to.be.undefined
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns undefined given a delete event', () => {
|
||||||
|
event.kind = EventKinds.DELETE
|
||||||
|
expect(factory([event, adapter])).to.be.undefined
|
||||||
|
})
|
||||||
|
})
|
64
test/unit/factories/event-strategy-factory.spec.ts
Normal file
64
test/unit/factories/event-strategy-factory.spec.ts
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import { expect } from 'chai'
|
||||||
|
|
||||||
|
import { DefaultEventStrategy } from '../../../src/handlers/event-strategies/default-event-strategy'
|
||||||
|
import { DeleteEventStrategy } from '../../../src/handlers/event-strategies/delete-event-strategy'
|
||||||
|
import { EphemeralEventStrategy } from '../../../src/handlers/event-strategies/ephemeral-event-strategy'
|
||||||
|
import { Event } from '../../../src/@types/event'
|
||||||
|
import { EventKinds } from '../../../src/constants/base'
|
||||||
|
import { eventStrategyFactory } from '../../../src/factories/event-strategy-factory'
|
||||||
|
import { Factory } from '../../../src/@types/base'
|
||||||
|
import { IEventRepository } from '../../../src/@types/repositories'
|
||||||
|
import { IEventStrategy } from '../../../src/@types/message-handlers'
|
||||||
|
import { IWebSocketAdapter } from '../../../src/@types/adapters'
|
||||||
|
import { ParameterizedReplaceableEventStrategy } from '../../../src/handlers/event-strategies/parameterized-replaceable-event-strategy'
|
||||||
|
import { ReplaceableEventStrategy } from '../../../src/handlers/event-strategies/replaceable-event-strategy'
|
||||||
|
|
||||||
|
describe('eventStrategyFactory', () => {
|
||||||
|
let eventRepository: IEventRepository
|
||||||
|
let event: Event
|
||||||
|
let adapter: IWebSocketAdapter
|
||||||
|
let factory: Factory<IEventStrategy<Event, Promise<void>>, [Event, IWebSocketAdapter]>
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
eventRepository = {} as any
|
||||||
|
event = {} as any
|
||||||
|
adapter = {} as any
|
||||||
|
|
||||||
|
factory = eventStrategyFactory(eventRepository)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns ReplaceableEvent given a set_metadata event', () => {
|
||||||
|
event.kind = EventKinds.SET_METADATA
|
||||||
|
expect(factory([event, adapter])).to.be.an.instanceOf(ReplaceableEventStrategy)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns ReplaceableEvent given a contact_list event', () => {
|
||||||
|
event.kind = EventKinds.CONTACT_LIST
|
||||||
|
expect(factory([event, adapter])).to.be.an.instanceOf(ReplaceableEventStrategy)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns ReplaceableEvent given a replaceable event', () => {
|
||||||
|
event.kind = EventKinds.REPLACEABLE_FIRST
|
||||||
|
expect(factory([event, adapter])).to.be.an.instanceOf(ReplaceableEventStrategy)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns EphemeralEventStrategy given an ephemeral event', () => {
|
||||||
|
event.kind = EventKinds.EPHEMERAL_FIRST
|
||||||
|
expect(factory([event, adapter])).to.be.an.instanceOf(EphemeralEventStrategy)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns DeleteEventStrategy given a delete event', () => {
|
||||||
|
event.kind = EventKinds.DELETE
|
||||||
|
expect(factory([event, adapter])).to.be.an.instanceOf(DeleteEventStrategy)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns ParameterizedReplaceableEventStrategy given a delete event', () => {
|
||||||
|
event.kind = EventKinds.PARAMETERIZED_REPLACEABLE_FIRST
|
||||||
|
expect(factory([event, adapter])).to.be.an.instanceOf(ParameterizedReplaceableEventStrategy)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns DefaultEventStrategy given a text_note event', () => {
|
||||||
|
event.kind = EventKinds.TEXT_NOTE
|
||||||
|
expect(factory([event, adapter])).to.be.an.instanceOf(DefaultEventStrategy)
|
||||||
|
})
|
||||||
|
})
|
77
test/unit/factories/message-handler-factory.spec.ts
Normal file
77
test/unit/factories/message-handler-factory.spec.ts
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
import { expect } from 'chai'
|
||||||
|
|
||||||
|
import { IncomingMessage, 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 { EventTags } from '../../../src/constants/base'
|
||||||
|
import { IEventRepository } from '../../../src/@types/repositories'
|
||||||
|
import { IWebSocketAdapter } from '../../../src/@types/adapters'
|
||||||
|
import { messageHandlerFactory } from '../../../src/factories/message-handler-factory'
|
||||||
|
import { SubscribeMessageHandler } from '../../../src/handlers/subscribe-message-handler'
|
||||||
|
import { UnsubscribeMessageHandler } from '../../../src/handlers/unsubscribe-message-handler'
|
||||||
|
|
||||||
|
describe('messageHandlerFactory', () => {
|
||||||
|
let event: Event
|
||||||
|
let eventRepository: IEventRepository
|
||||||
|
let message: IncomingMessage
|
||||||
|
let adapter: IWebSocketAdapter
|
||||||
|
let factory
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
eventRepository = {} as any
|
||||||
|
adapter = {} as any
|
||||||
|
event = {
|
||||||
|
tags: [],
|
||||||
|
} as any
|
||||||
|
factory = messageHandlerFactory(eventRepository)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns EventMessageHandler when given an EVENT message', () => {
|
||||||
|
message = [
|
||||||
|
MessageType.EVENT,
|
||||||
|
event,
|
||||||
|
]
|
||||||
|
|
||||||
|
expect(factory([message, adapter])).to.be.an.instanceOf(EventMessageHandler)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns DelegatedEventMessageHandler when given an EVENT message with delegated event', () => {
|
||||||
|
event.tags = [
|
||||||
|
[EventTags.Delegation, '', '', ''],
|
||||||
|
]
|
||||||
|
message = [
|
||||||
|
MessageType.EVENT,
|
||||||
|
event,
|
||||||
|
]
|
||||||
|
|
||||||
|
expect(factory([message, adapter])).to.be.an.instanceOf(DelegatedEventMessageHandler)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns SubscribeMessageHandler when given a REQ message', () => {
|
||||||
|
message = [
|
||||||
|
MessageType.REQ,
|
||||||
|
'',
|
||||||
|
{},
|
||||||
|
] as any
|
||||||
|
|
||||||
|
expect(factory([message, adapter])).to.be.an.instanceOf(SubscribeMessageHandler)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns UnsubscribeMessageHandler when given a REQ message', () => {
|
||||||
|
message = [
|
||||||
|
MessageType.CLOSE,
|
||||||
|
'',
|
||||||
|
]
|
||||||
|
|
||||||
|
expect(factory([message, adapter])).to.be.an.instanceOf(UnsubscribeMessageHandler)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('throws when given an invalid message', () => {
|
||||||
|
message = []
|
||||||
|
|
||||||
|
expect(() => factory([message, adapter])).to.throw(Error, 'Unknown message type: undefined')
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
})
|
40
test/unit/factories/websocket-adapter-factory.spec.ts
Normal file
40
test/unit/factories/websocket-adapter-factory.spec.ts
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import { expect } from 'chai'
|
||||||
|
import { IncomingMessage } from 'http'
|
||||||
|
import Sinon from 'sinon'
|
||||||
|
import WebSocket from 'ws'
|
||||||
|
|
||||||
|
import { IEventRepository } from '../../../src/@types/repositories'
|
||||||
|
import { IWebSocketServerAdapter } from '../../../src/@types/adapters'
|
||||||
|
import { WebSocketAdapter } from '../../../src/adapters/web-socket-adapter'
|
||||||
|
import { webSocketAdapterFactory } from '../../../src/factories/websocket-adapter-factory'
|
||||||
|
|
||||||
|
describe('webSocketAdapterFactory', () => {
|
||||||
|
let onStub: Sinon.SinonStub
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
onStub = Sinon.stub()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
onStub.reset()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns a WebSocketAdapter', () => {
|
||||||
|
const eventRepository: IEventRepository = {} as any
|
||||||
|
|
||||||
|
const client: WebSocket = {
|
||||||
|
on: onStub,
|
||||||
|
} as any
|
||||||
|
onStub.returns(client)
|
||||||
|
const request: IncomingMessage = {
|
||||||
|
headers: {
|
||||||
|
'sec-websocket-key': Buffer.from('key', 'utf-8').toString('base64'),
|
||||||
|
},
|
||||||
|
} as any
|
||||||
|
const webSocketServerAdapter: IWebSocketServerAdapter = {} as any
|
||||||
|
|
||||||
|
expect(
|
||||||
|
webSocketAdapterFactory(eventRepository)([client, request, webSocketServerAdapter])
|
||||||
|
).to.be.an.instanceOf(WebSocketAdapter)
|
||||||
|
})
|
||||||
|
})
|
25
test/unit/factories/worker-factory.spec.ts
Normal file
25
test/unit/factories/worker-factory.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { expect } from 'chai'
|
||||||
|
import Sinon from 'sinon'
|
||||||
|
|
||||||
|
import * as databaseClientModule from '../../../src/database/client'
|
||||||
|
|
||||||
|
import { AppWorker } from '../../../src/app/worker'
|
||||||
|
import { workerFactory } from '../../../src/factories/worker-factory'
|
||||||
|
|
||||||
|
describe('workerFactory', () => {
|
||||||
|
let getDbClientStub: Sinon.SinonStub
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
getDbClientStub = Sinon.stub(databaseClientModule, 'getDbClient')
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
getDbClientStub.restore()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns an AppWorker', () => {
|
||||||
|
const worker = workerFactory()
|
||||||
|
expect(worker).to.be.an.instanceOf(AppWorker)
|
||||||
|
worker.close()
|
||||||
|
})
|
||||||
|
})
|
@@ -21,7 +21,7 @@ describe('NIP-01', () => {
|
|||||||
const event: Partial<Event> = {
|
const event: Partial<Event> = {
|
||||||
pubkey: 'pubkey',
|
pubkey: 'pubkey',
|
||||||
created_at: 1000,
|
created_at: 1000,
|
||||||
kind: EventKinds.TEXT_NODE,
|
kind: EventKinds.TEXT_NOTE,
|
||||||
tags: [['tag name', 'tag content']],
|
tags: [['tag name', 'tag content']],
|
||||||
content: 'content',
|
content: 'content',
|
||||||
}
|
}
|
||||||
@@ -30,7 +30,7 @@ describe('NIP-01', () => {
|
|||||||
0,
|
0,
|
||||||
'pubkey',
|
'pubkey',
|
||||||
1000,
|
1000,
|
||||||
EventKinds.TEXT_NODE,
|
EventKinds.TEXT_NOTE,
|
||||||
[['tag name', 'tag content']],
|
[['tag name', 'tag content']],
|
||||||
'content',
|
'content',
|
||||||
]
|
]
|
||||||
|
Reference in New Issue
Block a user