From 8bc336ae5d5dccd24451f821557cce0dad501152 Mon Sep 17 00:00:00 2001 From: Jon Staab Date: Mon, 31 Mar 2025 14:06:52 -0700 Subject: [PATCH] Fix some tests --- packages/app/__tests__/tags.test.ts | 2 - packages/app/__tests__/thunk.test.ts | 197 ++++--------------------- packages/app/src/thunk.ts | 7 +- packages/net/__tests__/Publish.test.ts | 36 ++--- packages/net/__tests__/adapter.test.ts | 35 ++--- packages/net/__tests__/request.test.ts | 26 +--- packages/net/src/adapter.ts | 15 +- packages/net/src/policy.ts | 2 - 8 files changed, 69 insertions(+), 251 deletions(-) diff --git a/packages/app/__tests__/tags.test.ts b/packages/app/__tests__/tags.test.ts index 7dcf55d..885170a 100644 --- a/packages/app/__tests__/tags.test.ts +++ b/packages/app/__tests__/tags.test.ts @@ -206,8 +206,6 @@ describe("tags", () => { // a[0] should be the address of the replaceable event expect(a[0][1]).toBe(getAddress(replaceableEvent)) - - console.log(result) }) }) diff --git a/packages/app/__tests__/thunk.test.ts b/packages/app/__tests__/thunk.test.ts index a644603..4d5457e 100644 --- a/packages/app/__tests__/thunk.test.ts +++ b/packages/app/__tests__/thunk.test.ts @@ -1,10 +1,13 @@ import {now} from "@welshman/lib" -import {publish, PublishStatus} from "@welshman/net" -import {NOTE} from "@welshman/util" +import {publish, PublishStatus, MockAdapter} from "@welshman/net" +import {NOTE, makeEvent} from "@welshman/util" +import {Nip01Signer} from "@welshman/signer" +import {LOCAL_RELAY_URL} from "@welshman/relay" +import {getPubkey, makeSecret} from "@welshman/signer" import {EventEmitter} from "events" import {afterEach, beforeEach, describe, expect, it, vi} from "vitest" import {repository, tracker} from "../src/core" -import * as sessionModule from "../src/session" +import {addSession, dropSession} from "../src/session" import { abortThunk, makeThunk, @@ -16,100 +19,31 @@ import { walkThunks, } from "../src/thunk" -// Mock dependencies -vi.mock("@welshman/net", () => ({ - publish: vi.fn().mockReturnValue({emitter: {on: vi.fn()}}), - PublishStatus: { - Pending: "pending", - Success: "success", - Failure: "failure", - Timeout: "timeout", - Aborted: "aborted", - }, -})) +const secret = makeSecret() -vi.mock("../src/session", () => ({ - pubkey: { - get: vi.fn().mockReturnValue("aa".repeat(32)), - }, - getSession: vi.fn(), - getSigner: vi.fn(), -})) - -vi.mock("../src/core", () => ({ - repository: { - publish: vi.fn(), - removeEvent: vi.fn(), - getEvent: vi.fn(), - }, - tracker: { - track: vi.fn(), - }, -})) - -const pubkey = "aa".repeat(32) -const id = "00".repeat(32) -const mockEvent = { - id, - pubkey, - kind: NOTE, - created_at: now(), - content: "test content", - tags: [], -} +const pubkey = getPubkey(secret) const mockRequest = { - event: mockEvent, - relays: ["relay1", "relay2"], + event: prepEvent({...makeEvent(NOTE), pubkey}), + relays: [LOCAL_RELAY_URL], } describe("thunk", () => { beforeEach(() => { vi.useFakeTimers() - vi.clearAllMocks() + addSession({method: 'nip01', secret, pubkey}) }) afterEach(() => { vi.useRealTimers() - vi.resetModules() + vi.clearAllMocks() thunkWorker.clear() - thunkWorker.pause() // clear timeout + thunkWorker.pause() thunkWorker.resume() - }) - - describe("prepEvent", () => { - it("should prepare an event with stamp, own, and hash", () => { - const result = prepEvent(mockEvent) - expect(result).toHaveProperty("id") - expect(result).toHaveProperty("pubkey") - expect(result).toHaveProperty("created_at") - }) - }) - - describe("makeThunk", () => { - it("should create a thunk with required properties", () => { - const thunk = makeThunk(mockRequest) - expect(thunk).toHaveProperty("event") - expect(thunk).toHaveProperty("request") - expect(thunk).toHaveProperty("controller") - expect(thunk).toHaveProperty("result") - expect(thunk).toHaveProperty("status") - }) + dropSession(pubkey) }) describe("mergeThunks", () => { - it("should merge multiple thunks", () => { - const thunk1 = makeThunk(mockRequest) - const thunk2 = makeThunk(mockRequest) - const merged = mergeThunks([thunk1, thunk2]) - - expect(merged).toHaveProperty("thunks") - expect(merged.thunks).toHaveLength(2) - expect(merged).toHaveProperty("controller") - expect(merged).toHaveProperty("result") - expect(merged).toHaveProperty("status") - }) - it("should abort all thunks when merged controller aborts", () => { const thunk1 = makeThunk(mockRequest) const thunk2 = makeThunk(mockRequest) @@ -135,18 +69,21 @@ describe("thunk", () => { describe("publishThunk", () => { it("should create and publish a thunk", async () => { + const publishSpy = vi.spyOn(repository, 'publish') const result = publishThunk(mockRequest) - expect(repository.publish).toHaveBeenCalled() + expect(publishSpy).toHaveBeenCalled() expect(result).toHaveProperty("event") expect(result).toHaveProperty("request") }) it("should handle abort", () => { + const removeEventSpy = vi.spyOn(repository, 'removeEvent') const thunk = publishThunk(mockRequest) + thunk.controller.abort() - expect(repository.removeEvent).toHaveBeenCalledWith(thunk.event.id) + expect(removeEventSpy).toHaveBeenCalledWith(thunk.event.id) }) }) @@ -163,62 +100,18 @@ describe("thunk", () => { describe("abortThunk", () => { it("should abort a thunk and clean up", () => { const thunk = makeThunk(mockRequest) + abortThunk(thunk) expect(repository.removeEvent).toHaveBeenCalledWith(thunk.event.id) }) }) -}) - -describe("thunkWorker", async () => { - beforeEach(() => { - vi.useFakeTimers() - vi.clearAllMocks() - }) - - afterEach(() => { - vi.useRealTimers() - vi.resetModules() - thunkWorker.clear() - }) - - const mockSigner = { - sign: vi.fn().mockResolvedValue({...mockEvent, sig: "test-sig"}), - } - vi.mocked(sessionModule.getSigner).mockReturnValue(mockSigner) - - it("should handle publishing events", async () => { - const thunk = makeThunk(mockRequest) - - thunkWorker.push(thunk) - - await vi.runAllTimersAsync() - - expect(mockSigner.sign).toHaveBeenCalled() - }) - - it("should handle delayed publishing", async () => { - const thunk = makeThunk({...mockRequest, delay: 100}) - const startTime = Date.now() - - thunkWorker.push(thunk) - - await vi.runAllTimersAsync() - - const endTime = Date.now() - // worker delays work by 50ms, so total delay should be 150ms - expect(endTime - startTime).toBe(150) - }) it("should update status during publishing", async () => { - // Create mock emitter - const mockEmitter = new EventEmitter() - vi.mocked(publish).mockReturnValue({ - emitter: mockEmitter, - }) - - const thunk = makeThunk(mockRequest) + const send = vi.fn() + const track = vi.spyOn(tracker, 'track') const statuses: Map = new Map() + const thunk = makeThunk(mockRequest) // Subscribe to status updates thunk.status.subscribe(status => { @@ -233,55 +126,19 @@ describe("thunkWorker", async () => { // Wait for initial async operations await vi.runAllTimersAsync() - // Simulate publish status updates - mockEmitter.emit("*", PublishStatus.Pending, "relay1", "Connecting...") - - await vi.runAllTimersAsync() - - expect(statuses.get("relay1")).toEqual({ - status: PublishStatus.Pending, - message: "Connecting...", - }) - - mockEmitter.emit("*", PublishStatus.Success, "relay1", "Published") - - await vi.runAllTimersAsync() - - expect(statuses.get("relay1")).toEqual({ + expect(statuses.get(LOCAL_RELAY_URL)).toEqual({ status: PublishStatus.Success, - message: "Published", + message: "", }) // Verify tracker was called on success - expect(tracker.track).toHaveBeenCalledWith(thunk.event.id, "relay1") - - // Verify all relays complete resolves the result - mockEmitter.emit("*", PublishStatus.Success, "relay2", "Published") + expect(track).toHaveBeenCalledWith(thunk.event.id, LOCAL_RELAY_URL) await vi.runAllTimersAsync() const finalStatus = await thunk.result expect(finalStatus).toEqual({ - relay1: {status: PublishStatus.Success, message: "Published"}, - relay2: {status: PublishStatus.Success, message: "Published"}, + [LOCAL_RELAY_URL]: {status: PublishStatus.Success, message: ""}, }) }) - - it("should handle publish failures", async () => { - const mockSigner = { - sign: vi.fn().mockRejectedValue("Signing failed"), - } - - vi.mocked(sessionModule.getSigner).mockReturnValue(mockSigner) - - const thunk = makeThunk(mockRequest) - - thunkWorker.push(thunk) - - await vi.runAllTimersAsync() - - expect(mockSigner.sign).toHaveBeenCalled() - - // in case of failure, the worker will just stop its task, event is not removed - }) }) diff --git a/packages/app/src/thunk.ts b/packages/app/src/thunk.ts index c191500..faae94c 100644 --- a/packages/app/src/thunk.ts +++ b/packages/app/src/thunk.ts @@ -26,6 +26,7 @@ export type ThunkRequest = { event: ThunkEvent relays: string[] delay?: number + context?: AdapterContext, } export type ThunkStatus = { @@ -221,7 +222,11 @@ thunkWorker.addGlobalHandler((thunk: Thunk) => { } // Send it off - const pub = new MultiPublish({event: signedEvent, relays: thunk.request.relays}) + const pub = new MultiPublish({ + event: signedEvent, + relays: thunk.request.relays, + context: thunk.request.context, + }) // Copy the signature over since we had deferred it const savedEvent = repository.getEvent(signedEvent.id) as SignedEvent diff --git a/packages/net/__tests__/Publish.test.ts b/packages/net/__tests__/Publish.test.ts index 2a726c0..2f83c04 100644 --- a/packages/net/__tests__/Publish.test.ts +++ b/packages/net/__tests__/Publish.test.ts @@ -1,30 +1,12 @@ import { describe, expect, it, vi, beforeEach, afterEach } from "vitest" import { EventEmitter } from "events" -import { Unicast, Multicast, PublishEvent, PublishStatus, unicast, multicast } from "../src/publish" -import { AbstractAdapter, AdapterEvent } from "../src/adapter" +import { SinglePublish, MultiPublish, PublishEvent, PublishStatus, } from "../src/publish" +import { AbstractAdapter, AdapterEvent, MockAdapter } from "../src/adapter" import { ClientMessageType, RelayMessage } from "../src/message" import { SignedEvent, makeEvent } from "@welshman/util" import { Nip01Signer } from '@welshman/signer' -class MockAdapter extends AbstractAdapter { - constructor(readonly url: string, readonly send) { - super() - } - - get sockets() { - return [] - } - - get urls() { - return [this.url] - } - - receive = (message: RelayMessage) => { - this.emit(AdapterEvent.Receive, message, this.url) - } -} - -describe("Unicast", () => { +describe("SinglePublish", () => { beforeEach(() => { vi.useFakeTimers() }) @@ -39,7 +21,7 @@ describe("Unicast", () => { const signer = Nip01Signer.ephemeral() const event = await signer.sign(makeEvent(1)) - const pub = unicast({ + const pub = new SinglePublish({ relay: '1', context: {getAdapter: () => adapter}, event, @@ -72,7 +54,7 @@ describe("Unicast", () => { const signer = Nip01Signer.ephemeral() const event = await signer.sign(makeEvent(1)) - const pub = unicast({ + const pub = new SinglePublish({ relay: '1', context: {getAdapter: () => adapter}, event, @@ -105,7 +87,7 @@ describe("Unicast", () => { const signer = Nip01Signer.ephemeral() const event = await signer.sign(makeEvent(1)) - const pub = unicast({ + const pub = new SinglePublish({ relay: '1', context: {getAdapter: () => adapter}, event, @@ -139,7 +121,7 @@ describe("Unicast", () => { const signer = Nip01Signer.ephemeral() const event = await signer.sign(makeEvent(1)) - const pub = unicast({ + const pub = new SinglePublish({ relay: '1', context: {getAdapter: () => adapter}, event, @@ -170,7 +152,7 @@ describe("Unicast", () => { }) }) -describe("Multicast", () => { +describe("MultiPublish", () => { beforeEach(() => { vi.useFakeTimers() }) @@ -189,7 +171,7 @@ describe("Multicast", () => { const signer = Nip01Signer.ephemeral() const event = await signer.sign(makeEvent(1)) - const pub = multicast({ + const pub = new MultiPublish({ event, relays: ['1', '2', '3'], context: { diff --git a/packages/net/__tests__/adapter.test.ts b/packages/net/__tests__/adapter.test.ts index 91db9b9..05310ec 100644 --- a/packages/net/__tests__/adapter.test.ts +++ b/packages/net/__tests__/adapter.test.ts @@ -1,10 +1,11 @@ -import { describe, expect, it, vi, beforeEach, afterEach } from "vitest" -import { Socket, SocketEvent } from "../src/socket" -import { Relay, LOCAL_RELAY_URL, isRelayUrl } from "@welshman/util" -import { AdapterEvent, SocketAdapter, LocalAdapter, getAdapter } from "../src/adapter" -import { Pool } from "../src/pool" -import { ClientMessage, RelayMessage } from "../src/message" import EventEmitter from "events" +import { describe, expect, it, vi, beforeEach, afterEach } from "vitest" +import { isRelayUrl } from "@welshman/util" +import { LocalRelay, LOCAL_RELAY_URL } from "@welshman/relay" +import { AdapterEvent, SocketAdapter, LocalAdapter, getAdapter } from "../src/adapter" +import { ClientMessage, RelayMessage } from "../src/message" +import { Socket, SocketEvent } from "../src/socket" +import { Pool } from "../src/pool" vi.mock('isomorphic-ws', () => { const WebSocket = vi.fn(function () { @@ -69,7 +70,7 @@ describe("SocketAdapter", () => { }) describe("LocalAdapter", () => { - let relay: Relay & EventEmitter + let relay: LocalRelay & EventEmitter let adapter: LocalAdapter beforeEach(() => { @@ -78,7 +79,7 @@ describe("LocalAdapter", () => { send: vi.fn(), removeAllListeners: vi.fn() }) - relay = mockRelay as unknown as Relay & EventEmitter + relay = mockRelay as unknown as LocalRelay & EventEmitter adapter = new LocalAdapter(relay) }) @@ -119,11 +120,11 @@ describe("LocalAdapter", () => { describe("getAdapter", () => { let pool: Pool - let relay: Relay + let relay: LocalRelay beforeEach(() => { pool = new Pool() - relay = new Relay() + relay = new LocalRelay() pool.get = vi.fn().mockReturnValue(new Socket("wss://test.relay")) }) @@ -143,20 +144,6 @@ describe("getAdapter", () => { expect(adapter).toBeInstanceOf(SocketAdapter) }) - it("should throw error for invalid relay URL", () => { - expect(() => getAdapter("invalid-url", {})).toThrow("Invalid relay url invalid-url") - }) - - it("should throw error for local relay URL without relay context", () => { - const url = LOCAL_RELAY_URL - expect(() => getAdapter(url, {})).toThrow(`Unable to get local relay for ${url}`) - }) - - it("should throw error for remote relay URL without pool context", () => { - const url = "wss://test.relay" - expect(() => getAdapter(url, {})).toThrow(`Unable to get socket for ${url}`) - }) - it("should use custom adapter if provided", () => { const customAdapter = new SocketAdapter(new Socket("wss://test.relay")) const getCustomAdapter = vi.fn().mockReturnValue(customAdapter) diff --git a/packages/net/__tests__/request.test.ts b/packages/net/__tests__/request.test.ts index f4760ae..85d3de1 100644 --- a/packages/net/__tests__/request.test.ts +++ b/packages/net/__tests__/request.test.ts @@ -2,28 +2,10 @@ import { describe, expect, it, vi, beforeEach, afterEach } from "vitest" import { Nip01Signer } from '@welshman/signer' import { LOCAL_RELAY_URL, makeEvent } from '@welshman/util' import { ClientMessageType, RelayMessage } from "../src/message" -import { AdapterContext, AbstractAdapter, AdapterEvent } from "../src/adapter" -import { unireq, multireq, RequestEvent } from "../src/request" +import { AdapterContext, AbstractAdapter, AdapterEvent, MockAdapter } from "../src/adapter" +import { SingleRequest, MultiRequest, RequestEvent } from "../src/request" import { Tracker } from "../src/tracker" -class MockAdapter extends AbstractAdapter { - constructor(readonly url: string, readonly send) { - super() - } - - get sockets() { - return [] - } - - get urls() { - return [this.url] - } - - receive = (message: RelayMessage) => { - this.emit(AdapterEvent.Receive, message, this.url) - } -} - describe("Unireq", () => { beforeEach(() => { vi.useFakeTimers() @@ -36,7 +18,7 @@ describe("Unireq", () => { it("everything basically works", async () => { const sendSpy = vi.fn() const adapter = new MockAdapter('1', sendSpy) - const req = unireq({ + const req = new SingleRequest({ relay: 'whatever', filter: {kinds: [1]}, context: {getAdapter: () => adapter}, @@ -100,7 +82,7 @@ describe("Multireq", () => { const adapter1 = new MockAdapter('1', send1Spy) const send2Spy = vi.fn() const adapter2 = new MockAdapter('2', send2Spy) - const req = multireq({ + const req = new MultiRequest({ autoClose: true, relays: ['1', '2'], filter: {kinds: [1]}, diff --git a/packages/net/src/adapter.ts b/packages/net/src/adapter.ts index 83fcf86..9af9282 100644 --- a/packages/net/src/adapter.ts +++ b/packages/net/src/adapter.ts @@ -78,16 +78,25 @@ export class LocalAdapter extends AbstractAdapter { } } -export class EmptyAdapter extends AbstractAdapter { +export class MockAdapter extends AbstractAdapter { + constructor( + readonly url: string, + readonly send: (message: ClientMessage) => void, + ) { + super() + } + get sockets() { return [] } get urls() { - return [] + return [this.url] } - send(message: ClientMessage) {} + receive = (message: RelayMessage) => { + this.emit(AdapterEvent.Receive, message, this.url) + } } export type AdapterContext = { diff --git a/packages/net/src/policy.ts b/packages/net/src/policy.ts index d3f78be..0ee53c4 100644 --- a/packages/net/src/policy.ts +++ b/packages/net/src/policy.ts @@ -205,9 +205,7 @@ export const socketPolicyReopenActive = (socket: Socket) => { // If the socket closed and we have no error, reopen it but don't flap if (newStatus === SocketStatus.Closed && pending.size) { - console.log("1") sleep(Math.max(0, 30_000 - (Date.now() - lastOpen))).then(() => { - console.log("2") for (const message of pending.values()) { socket.send(message) }