Add tests for request
This commit is contained in:
@@ -0,0 +1,141 @@
|
|||||||
|
import { describe, expect, it, vi, beforeEach, afterEach } from "vitest"
|
||||||
|
import { Nip01Signer } from '@welshman/signer'
|
||||||
|
import { LOCAL_RELAY_URL, makeEvent } from '@welshman/util'
|
||||||
|
import { ClientMessageType } from "../src/message"
|
||||||
|
import { AdapterContext, AbstractAdapter, AdapterEventType } from "../src/adapter"
|
||||||
|
import { unireq, multireq, RequestEventType } from "../src/request"
|
||||||
|
import { Tracker } from "../src/tracker"
|
||||||
|
|
||||||
|
class MockAdapter extends AbstractAdapter {
|
||||||
|
constructor(readonly send) {
|
||||||
|
super()
|
||||||
|
|
||||||
|
this.sockets = []
|
||||||
|
this.urls = [LOCAL_RELAY_URL]
|
||||||
|
}
|
||||||
|
|
||||||
|
receive = (message: RelayMessage) => {
|
||||||
|
this.emit(AdapterEventType.Receive, message, this.url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("Unireq", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.useFakeTimers()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("everything basically works", async () => {
|
||||||
|
const sendSpy = vi.fn()
|
||||||
|
const adapter = new MockAdapter(sendSpy)
|
||||||
|
const req = unireq({
|
||||||
|
relay: 'whatever',
|
||||||
|
filter: {kinds: [1]},
|
||||||
|
context: {getAdapter: () => adapter},
|
||||||
|
autoClose: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
const duplicateSpy = vi.fn()
|
||||||
|
const invalidSpy = vi.fn()
|
||||||
|
const filteredSpy = vi.fn()
|
||||||
|
const eventSpy = vi.fn()
|
||||||
|
const eoseSpy = vi.fn()
|
||||||
|
const closeSpy = vi.fn()
|
||||||
|
|
||||||
|
req.on(RequestEventType.Duplicate, duplicateSpy)
|
||||||
|
req.on(RequestEventType.Invalid, invalidSpy)
|
||||||
|
req.on(RequestEventType.Filtered, filteredSpy)
|
||||||
|
req.on(RequestEventType.Event, eventSpy)
|
||||||
|
req.on(RequestEventType.Eose, eoseSpy)
|
||||||
|
req.on(RequestEventType.Close, closeSpy)
|
||||||
|
|
||||||
|
await vi.runAllTimers()
|
||||||
|
|
||||||
|
expect(sendSpy).toHaveBeenCalledWith([ClientMessageType.Req, req._id, {kinds: [1]}])
|
||||||
|
|
||||||
|
const signer = Nip01Signer.ephemeral()
|
||||||
|
const event1 = await signer.sign(makeEvent(1))
|
||||||
|
const event2 = await signer.sign(makeEvent(7))
|
||||||
|
const event3 = makeEvent(1)
|
||||||
|
|
||||||
|
adapter.receive(["EVENT", req._id, event1])
|
||||||
|
adapter.receive(["EVENT", req._id, event2])
|
||||||
|
adapter.receive(["EVENT", req._id, event1])
|
||||||
|
adapter.receive(["EVENT", req._id, event3])
|
||||||
|
|
||||||
|
await vi.runAllTimers()
|
||||||
|
|
||||||
|
expect(duplicateSpy).toHaveBeenCalledWith(event1)
|
||||||
|
expect(filteredSpy).toHaveBeenCalledWith(event2)
|
||||||
|
expect(invalidSpy).toHaveBeenCalledWith(event3)
|
||||||
|
expect(eventSpy).toHaveBeenCalledWith(event1)
|
||||||
|
expect(eoseSpy).toHaveBeenCalledTimes(0)
|
||||||
|
|
||||||
|
adapter.receive(["EOSE", req._id])
|
||||||
|
|
||||||
|
expect(eoseSpy).toHaveBeenCalledTimes(1)
|
||||||
|
expect(closeSpy).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("Multireq", () => {
|
||||||
|
it("everything basically works", async () => {
|
||||||
|
const send1Spy = vi.fn()
|
||||||
|
const adapter1 = new MockAdapter(send1Spy)
|
||||||
|
const send2Spy = vi.fn()
|
||||||
|
const adapter2 = new MockAdapter(send2Spy)
|
||||||
|
const req = multireq({
|
||||||
|
autoClose: true,
|
||||||
|
relays: ['1', '2'],
|
||||||
|
filter: {kinds: [1]},
|
||||||
|
context: {
|
||||||
|
getAdapter: (url: string) => url === '1' ? adapter1 : adapter2
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const duplicateSpy = vi.fn()
|
||||||
|
const invalidSpy = vi.fn()
|
||||||
|
const filteredSpy = vi.fn()
|
||||||
|
const eventSpy = vi.fn()
|
||||||
|
const eoseSpy = vi.fn()
|
||||||
|
const closeSpy = vi.fn()
|
||||||
|
|
||||||
|
req.on(RequestEventType.Duplicate, duplicateSpy)
|
||||||
|
req.on(RequestEventType.Invalid, invalidSpy)
|
||||||
|
req.on(RequestEventType.Filtered, filteredSpy)
|
||||||
|
req.on(RequestEventType.Event, eventSpy)
|
||||||
|
req.on(RequestEventType.Eose, eoseSpy)
|
||||||
|
req.on(RequestEventType.Close, closeSpy)
|
||||||
|
|
||||||
|
await vi.runAllTimers()
|
||||||
|
|
||||||
|
expect(send1Spy).toHaveBeenCalledWith([ClientMessageType.Req, req._children[0]._id, {kinds: [1]}])
|
||||||
|
expect(send2Spy).toHaveBeenCalledWith([ClientMessageType.Req, req._children[1]._id, {kinds: [1]}])
|
||||||
|
|
||||||
|
const signer = Nip01Signer.ephemeral()
|
||||||
|
const event1 = await signer.sign(makeEvent(1))
|
||||||
|
const event2 = await signer.sign(makeEvent(7))
|
||||||
|
const event3 = makeEvent(1)
|
||||||
|
const event4 = await signer.sign(makeEvent(1))
|
||||||
|
|
||||||
|
adapter1.receive(["EVENT", req._children[0]._id, event1])
|
||||||
|
adapter1.receive(["EVENT", req._children[0]._id, event2])
|
||||||
|
adapter1.receive(["EVENT", req._children[0]._id, event3])
|
||||||
|
adapter2.receive(["EVENT", req._children[1]._id, event1])
|
||||||
|
adapter2.receive(["EVENT", req._children[1]._id, event4])
|
||||||
|
|
||||||
|
await vi.runAllTimers()
|
||||||
|
|
||||||
|
expect(duplicateSpy).toHaveBeenCalledWith(event1, '2')
|
||||||
|
expect(filteredSpy).toHaveBeenCalledWith(event2, '1')
|
||||||
|
expect(invalidSpy).toHaveBeenCalledWith(event3, '1')
|
||||||
|
expect(eventSpy).toHaveBeenCalledWith(event1, '1')
|
||||||
|
expect(eoseSpy).toHaveBeenCalledTimes(0)
|
||||||
|
|
||||||
|
adapter1.receive(["EOSE", req._children[0]._id])
|
||||||
|
adapter2.receive(["EOSE", req._children[1]._id])
|
||||||
|
|
||||||
|
expect(eoseSpy).toHaveBeenCalledTimes(2)
|
||||||
|
expect(closeSpy).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import {EventEmitter} from "events"
|
import {EventEmitter} from "events"
|
||||||
|
import {verifyEvent as nostrToolsVerifyEvent} from 'nostr-tools'
|
||||||
import {on, call, randomId, yieldThread} from "@welshman/lib"
|
import {on, call, randomId, yieldThread} from "@welshman/lib"
|
||||||
import {Filter, matchFilter, SignedEvent} from "@welshman/util"
|
import {Filter, matchFilter, SignedEvent} from "@welshman/util"
|
||||||
import {RelayMessage, ClientMessageType, isRelayEvent, isRelayEose} from "./message.js"
|
import {RelayMessage, ClientMessageType, isRelayEvent, isRelayEose} from "./message.js"
|
||||||
@@ -7,6 +8,14 @@ import {SocketEventType, SocketStatus} from "./socket.js"
|
|||||||
import {TypedEmitter, Unsubscriber} from "./util.js"
|
import {TypedEmitter, Unsubscriber} from "./util.js"
|
||||||
import {Tracker} from "./tracker.js"
|
import {Tracker} from "./tracker.js"
|
||||||
|
|
||||||
|
export const defaultVerifyEvent = (event: SignedEvent) => {
|
||||||
|
try {
|
||||||
|
return nostrToolsVerifyEvent(event)
|
||||||
|
} catch (e) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export enum RequestEventType {
|
export enum RequestEventType {
|
||||||
Close = "request:event:close",
|
Close = "request:event:close",
|
||||||
Disconnect = "request:event:disconnect",
|
Disconnect = "request:event:disconnect",
|
||||||
@@ -48,6 +57,10 @@ export class Unireq extends (EventEmitter as new () => TypedEmitter<UnireqEvents
|
|||||||
constructor(readonly options: UnireqOptions) {
|
constructor(readonly options: UnireqOptions) {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
|
const tracker = options.tracker || new Tracker()
|
||||||
|
|
||||||
|
const verifyEvent = options.verifyEvent || defaultVerifyEvent
|
||||||
|
|
||||||
// Set up our adapter
|
// Set up our adapter
|
||||||
this._adapter = getAdapter(this.options.relay, this.options.context)
|
this._adapter = getAdapter(this.options.relay, this.options.context)
|
||||||
|
|
||||||
@@ -59,9 +72,9 @@ export class Unireq extends (EventEmitter as new () => TypedEmitter<UnireqEvents
|
|||||||
|
|
||||||
if (id !== this._id) return
|
if (id !== this._id) return
|
||||||
|
|
||||||
if (this.options.tracker?.track(event.id, url)) {
|
if (tracker.track(event.id, url)) {
|
||||||
this.emit(RequestEventType.Duplicate, event)
|
this.emit(RequestEventType.Duplicate, event)
|
||||||
} else if (this.options.verifyEvent?.(event) === false) {
|
} else if (verifyEvent?.(event) === false) {
|
||||||
this.emit(RequestEventType.Invalid, event)
|
this.emit(RequestEventType.Invalid, event)
|
||||||
} else if (!matchFilter(this.options.filter, event)) {
|
} else if (!matchFilter(this.options.filter, event)) {
|
||||||
this.emit(RequestEventType.Filtered, event)
|
this.emit(RequestEventType.Filtered, event)
|
||||||
@@ -145,8 +158,10 @@ export class Multireq extends (EventEmitter as new () => TypedEmitter<MultireqEv
|
|||||||
constructor({relays, ...options}: MultireqOptions) {
|
constructor({relays, ...options}: MultireqOptions) {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
|
const tracker = new Tracker()
|
||||||
|
|
||||||
for (const relay of relays) {
|
for (const relay of relays) {
|
||||||
const req = new Unireq({relay, ...options})
|
const req = new Unireq({relay, tracker, ...options})
|
||||||
|
|
||||||
req.on(RequestEventType.Event, (event: SignedEvent) => {
|
req.on(RequestEventType.Event, (event: SignedEvent) => {
|
||||||
this.emit(RequestEventType.Event, event, relay)
|
this.emit(RequestEventType.Event, event, relay)
|
||||||
|
|||||||
Reference in New Issue
Block a user