UNPKG

nostr-relaypool

Version:

A Nostr RelayPool implementation in TypeScript using only nostr-tools library as a dependency.

249 lines (221 loc) 5.89 kB
/* eslint-env jest */ import { signEvent, generatePrivateKey, getEventHash, getPublicKey, type Event, } from "nostr-tools"; import type {Relay} from "./relay"; import {relayInit} from "./relay"; import {InMemoryRelayServer} from "./in-memory-relay-server"; import WebSocket from "ws"; let relay: Relay; let _relayServer: InMemoryRelayServer; beforeAll(() => { _relayServer = new InMemoryRelayServer(8089); }); beforeEach(async () => { relay = relayInit("ws://localhost:8089/", undefined, true); relay.connect(); _relayServer.clear(); }); afterEach(async () => { await relay.close(); _relayServer.clear(); }); afterAll(async () => { await _relayServer.close(); }); test("connectivity", () => { return expect( new Promise((resolve) => { relay.on("connect", () => { resolve(true); }); relay.on("error", () => { resolve(false); }); }) ).resolves.toBe(true); }); async function publishAndGetEvent( options: {content?: string} = {} ): Promise<Event> { const sk = generatePrivateKey(); const pk = getPublicKey(sk); const unsignedEvent = { kind: 27572, pubkey: pk, created_at: Math.floor(Date.now() / 1000), tags: [], content: options.content || "nostr-tools test suite", }; const eventId = getEventHash(unsignedEvent); const event: Event = { sig: signEvent(unsignedEvent, sk), id: eventId, ...unsignedEvent, }; // console.log("publishing event", event); relay.publish(event); return new Promise((resolve) => relay // @ts-ignore .sub([{ids: [event.id]}]) .on("event", (event: Event) => { resolve(event); }) ); } test("querying", async () => { const event: Event & {id: string} = await publishAndGetEvent(); var resolve1: (success: boolean) => void; var resolve2: (success: boolean) => void; const promiseAll = Promise.all([ new Promise((resolve) => { resolve(true); resolve1 = resolve; }), new Promise((resolve) => { resolve2 = resolve; resolve(true); }), ]); const sub = relay.sub([ { kinds: [event.kind], }, ]); sub.on("event", (event: Event) => { expect(event).toHaveProperty("id", event.id); resolve1(true); }); sub.on("eose", () => { resolve2(true); }); return expect(promiseAll).resolves.toEqual([true, true]); }); test("listening (twice) and publishing", async () => { const sk = generatePrivateKey(); const pk = getPublicKey(sk); var resolve1: (success: boolean) => void; var resolve2: (success: boolean) => void; const sub = relay.sub([ { kinds: [27572], authors: [pk], }, ]); sub.on("event", (event: Event) => { expect(event).toHaveProperty("pubkey", pk); expect(event).toHaveProperty("kind", 27572); expect(event).toHaveProperty("content", "nostr-tools test suite"); resolve1(true); }); sub.on("event", (event: Event) => { expect(event).toHaveProperty("pubkey", pk); expect(event).toHaveProperty("kind", 27572); expect(event).toHaveProperty("content", "nostr-tools test suite"); resolve2(true); }); const event = { kind: 27572, pubkey: pk, created_at: Math.floor(Date.now() / 1000), tags: [], content: "nostr-tools test suite", }; // @ts-ignore event.id = getEventHash(event); // @ts-ignore event.sig = signEvent(event, sk); // @ts-ignore relay.publish(event); return expect( Promise.all([ new Promise((resolve) => { resolve1 = resolve; }), new Promise((resolve) => { resolve2 = resolve; }), ]) ).resolves.toEqual([true, true]); }); test("two subscriptions", async () => { const sk = generatePrivateKey(); const pk = getPublicKey(sk); const event = { kind: 27572, pubkey: pk, created_at: Math.floor(Date.now() / 1000), tags: [], content: "nostr-tools test suite", }; // @ts-ignore event.id = getEventHash(event); // @ts-ignore event.sig = signEvent(event, sk); await expect( new Promise((resolve) => { const sub = relay.sub([ { kinds: [27572], authors: [pk], }, ]); sub.on("event", (event: Event) => { expect(event).toHaveProperty("pubkey", pk); expect(event).toHaveProperty("kind", 27572); expect(event).toHaveProperty("content", "nostr-tools test suite"); resolve(true); }); // @ts-ignore relay.publish(event); }) ).resolves.toEqual(true); await expect( new Promise((resolve) => { const sub = relay.sub([ { kinds: [27572], authors: [pk], }, ]); sub.on("event", (event: Event) => { expect(event).toHaveProperty("pubkey", pk); expect(event).toHaveProperty("kind", 27572); expect(event).toHaveProperty("content", "nostr-tools test suite"); resolve(true); }); // @ts-ignore relay.publish(event); }) ).resolves.toEqual(true); }); test("autoreconnect", async () => { expect(relay.status).toBe(WebSocket.CONNECTING); await publishAndGetEvent(); expect(relay.status).toBe(WebSocket.OPEN); _relayServer.disconnectAll(); await new Promise((resolve) => setTimeout(resolve, 0)); expect(relay.status).toBeGreaterThanOrEqual(WebSocket.CLOSING); await publishAndGetEvent(); }); // jest -t 'relay memory' --testTimeout 1000000 --logHeapUsage // PASS ./relay.test.ts (93.914 s, 480 MB heap size) test.skip("relay memory usage", async () => { // @ts-ignore relay.relay.logging = false; await publishAndGetEvent({content: "x".repeat(20 * 1024 * 1024)}); for (let i = 0; i < 300; i++) { await new Promise((resolve) => { const sub = relay.sub([{}]); sub.on("event", (event: Event) => { sub.unsub(); resolve(true); }); }); } });