chirp-sync-transport.test.ts

2.9 KB · raw

1// Subnet-probe rate-limit tests. The full Noise XX handshake side of the2// transport requires a live WebSocket peer, so we exercise the cooldown3// ledger directly — it is the only Phase 2 invariant we want to pin4// regardless of network reachability.56import { afterEach, beforeEach, describe, expect, it } from "vitest";7import {8  _resetSubnetProbeCooldown,9  probeSubnetForPeer,10  subnetProbeOnCooldown,11  SUBNET_PROBE_COOLDOWN_MS,12} from "./chirp-sync-transport";1314const PEER_PUBKEY = "ab".repeat(32);1516describe("subnetProbeOnCooldown", () => {17  beforeEach(() => _resetSubnetProbeCooldown());18  afterEach(() => _resetSubnetProbeCooldown());1920  it("returns false before any probe has happened", () => {21    expect(subnetProbeOnCooldown(PEER_PUBKEY)).toBe(false);22  });2324  it("returns true within the cooldown window after a probe", async () => {25    const now = 1_000_000;26    // Fire-and-forget; the probe itself fails fast because no peer exists.27    // We only care about the ledger side-effect of recording `now`.28    await probeSubnetForPeer(29      "10.0.0",30      49142,31      new Uint8Array(32),32      new Uint8Array(32),33      PEER_PUBKEY,34      { batch: 254, timeoutMs: 1, now },35    );36    expect(subnetProbeOnCooldown(PEER_PUBKEY, now + 1)).toBe(true);37    expect(subnetProbeOnCooldown(PEER_PUBKEY, now + SUBNET_PROBE_COOLDOWN_MS - 1)).toBe(true);38  });3940  it("releases the cooldown after the window expires", async () => {41    const now = 1_000_000;42    await probeSubnetForPeer(43      "10.0.0",44      49142,45      new Uint8Array(32),46      new Uint8Array(32),47      PEER_PUBKEY,48      { batch: 254, timeoutMs: 1, now },49    );50    expect(subnetProbeOnCooldown(PEER_PUBKEY, now + SUBNET_PROBE_COOLDOWN_MS)).toBe(false);51  });5253  it("returns null and skips the scan when on cooldown", async () => {54    const now = 1_000_000;55    await probeSubnetForPeer(56      "10.0.0",57      49142,58      new Uint8Array(32),59      new Uint8Array(32),60      PEER_PUBKEY,61      { batch: 254, timeoutMs: 1, now },62    );63    // Second call within the window must short-circuit before any64    // WebSocket attempt is made. We can't easily observe "no WebSocket"65    // directly here, but null + cooldown=true is the contract.66    const result = await probeSubnetForPeer(67      "10.0.0",68      49142,69      new Uint8Array(32),70      new Uint8Array(32),71      PEER_PUBKEY,72      { batch: 254, timeoutMs: 1, now: now + 1_000 },73    );74    expect(result).toBeNull();75  });7677  it("tracks cooldown independently per peer pubkey", async () => {78    const now = 1_000_000;79    const other = "cd".repeat(32);80    await probeSubnetForPeer(81      "10.0.0",82      49142,83      new Uint8Array(32),84      new Uint8Array(32),85      PEER_PUBKEY,86      { batch: 254, timeoutMs: 1, now },87    );88    expect(subnetProbeOnCooldown(PEER_PUBKEY, now + 1)).toBe(true);89    expect(subnetProbeOnCooldown(other, now + 1)).toBe(false);90  });91});