import { describe, expect, it } from "@suaveplan/testing/runner"; import { GunDBServer } from "../src/server/server.js"; import { FakeGun, makeFakeGunFactory } from "./setup/fake-gun.js"; const VALID_ANNOUNCE = { kind: "peer-announce" as const, peerId: "client-1", addresses: ["ws://1.2.3.4:80"], metadata: { tag: "alpha" }, capabilities: ["chat"], lastSeen: 1_700_000_000_000, signaturePublicKey: "c".repeat(64), signature: "d".repeat(128), }; async function fetchJson( url: string ): Promise<{ readonly status: number; readonly body: unknown }> { const res = await fetch(url); const body = await res.json(); return { status: res.status, body }; } describe("integration: real HTTP boot + health probes", () => { it("health/ready/live respond with 200 while running", { timeout: 10_000, }, async () => { const { factory } = makeFakeGunFactory(); const server = new GunDBServer({ port: 0, host: "127.0.0.1", gunFactory: factory, }); const { address, port } = await server.start(); const base = `http://${address === "::" || address === "0.0.0.0" ? "127.0.0.1" : address}:${port}`; try { const health = await fetchJson(`${base}/health`); expect(health.status).toBe(200); expect((health.body as { status: string }).status).toBe("ok"); const ready = await fetchJson(`${base}/ready`); expect(ready.status).toBe(200); const live = await fetchJson(`${base}/live`); expect(live.status).toBe(200); // Non-health route falls through to the relay 200/OK marker. const fall = await fetch(`${base}/gun`); expect(fall.status).toBe(200); await fall.text(); } finally { await server.stop(); } }); it("two browser-tier clients relay a signed announce via shared graph", { timeout: 10_000, }, async () => { // The injected FakeGun shares storage across calls; both // "clients" inject the same announce-room namespace and the // server observes the inbound frame deterministically. This // demonstrates the wire path without pulling the real `gun` // runtime into the test env. const sharedStore = new Map(); const sharedListeners = new Map< string, Array<(data: unknown, key: string) => void> >(); const serverFactory = (cfg: { readonly peers: readonly string[]; }): FakeGun => new FakeGun( { web: undefined, peers: cfg.peers }, sharedStore, sharedListeners ); const server = new GunDBServer({ port: 0, host: "127.0.0.1", gunFactory: serverFactory, }); await server.start(); try { // Two distinct "clients" inject into the same shared graph // — the server observes each one as a separate peer. const clientA = new FakeGun( { web: undefined, peers: [] }, sharedStore, sharedListeners ); const clientB = new FakeGun( { web: undefined, peers: [] }, sharedStore, sharedListeners ); clientA.inject("suaveplan-discovery", "client-1", { ...VALID_ANNOUNCE, peerId: "client-1", }); clientB.inject("suaveplan-discovery", "client-2", { ...VALID_ANNOUNCE, peerId: "client-2", }); expect(server.activeConnections).toBe(2); const peerIds = server .peers() .map((p) => p.id) .sort(); expect(peerIds).toEqual(["client-1", "client-2"]); } finally { await server.stop(); } }); it("malformed payload from a misbehaving client is dropped (counter only)", { timeout: 10_000, }, async () => { const sharedStore = new Map(); const sharedListeners = new Map< string, Array<(data: unknown, key: string) => void> >(); const serverFactory = (cfg: { readonly peers: readonly string[]; }): FakeGun => new FakeGun( { web: undefined, peers: cfg.peers }, sharedStore, sharedListeners ); const server = new GunDBServer({ port: 0, host: "127.0.0.1", gunFactory: serverFactory, }); await server.start(); try { const badClient = new FakeGun( { web: undefined, peers: [] }, sharedStore, sharedListeners ); badClient.inject("suaveplan-discovery", "evil-1", { kind: "wrong", junk: true, }); badClient.inject("suaveplan-discovery", "evil-2", null); badClient.inject("suaveplan-discovery", "evil-3", { ...VALID_ANNOUNCE, signature: undefined, }); expect(server.activeConnections).toBe(0); expect(server.peers()).toHaveLength(0); } finally { await server.stop(); } }); it("graceful stop closes the HTTP listener", { timeout: 10_000, }, async () => { const { factory } = makeFakeGunFactory(); const server = new GunDBServer({ port: 0, host: "127.0.0.1", gunFactory: factory, }); const { address, port } = await server.start(); const base = `http://${address === "::" || address === "0.0.0.0" ? "127.0.0.1" : address}:${port}`; await server.stop(); // After stop, the socket is closed — connection refused. await expect(fetch(`${base}/health`)).rejects.toBeDefined(); }); });