Initial Commit

This commit is contained in:
2026-05-27 23:47:53 +01:00
parent fbc70f96e6
commit 9b28db724f
18103 changed files with 1600554 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
import { describe, expect, it } from "@suaveplan/testing/runner";
describe("network-transport-standby dep-graph", () => {
it("must not have circular dependencies", { timeout: 5000 }, async () => {
const mod = await import("../src/index.js");
expect(mod).toBeDefined();
expect(mod.StandbyPlugin).toBeDefined();
expect(mod.StandbyTransport).toBeDefined();
expect(mod.StandbyPool).toBeDefined();
expect(mod.NET_STANDBY_CODES).toBeDefined();
});
});

View File

@@ -0,0 +1,92 @@
import { describe, expect, it } from "@suaveplan/testing/runner";
import { StandbyPool } from "../src/pool/pool.js";
import { StandbyTransport } from "../src/transport/transport.js";
import type { StandbyPoolConfig } from "../src/types/types.js";
import { makeFakeFactory } from "./setup/fake-rtc.js";
const cfg: StandbyPoolConfig = {
minSlots: 4,
maxSlots: 8,
warmupTimeoutMs: 500,
healthCheckIntervalMs: 0,
qualityThreshold: 30,
maxReconnectAttempts: 3,
reconnectBaseDelayMs: 5,
reconnectMaxDelayMs: 50,
};
describe("integration — manager-style promotion", () => {
it("warms 4 slots then promotes one in <100ms", {
timeout: 5000,
}, async () => {
const { factory } = makeFakeFactory();
const transport = new StandbyTransport({
pool: { config: cfg, rtcFactory: factory },
});
await transport.start();
expect(transport.pool.warmSlots().length).toBe(4);
// Now simulate a manager calling promote — this is the same
// path `network-plugin-transport-manager` would drive when
// selecting a transport for a peer.
const start = Date.now();
const slot = await transport.pool.promote("peer-X");
const elapsed = Date.now() - start;
expect(slot.state).toBe("active");
expect(slot.peerId).toBe("peer-X");
expect(elapsed).toBeLessThan(100);
// Pool top-up should not have shrunk the pool below min.
expect(
transport.pool.warmSlots().length +
transport.pool.activeSlots().length
).toBeGreaterThanOrEqual(cfg.minSlots);
await transport.close();
});
it("failure-mode: unhealthy slot is evicted and replaced", {
timeout: 5000,
}, async () => {
const { factory, registry } = makeFakeFactory();
const pool = new StandbyPool({
config: { ...cfg, qualityThreshold: 80 },
rtcFactory: factory,
});
await pool.start();
const target = pool.warmSlots()[0]!;
const conn = registry.get(target.id)!;
// Drag quality below threshold.
const out = pool.recordSample(target.id, {
rttMs: 1_000,
jitterMs: 200,
lossRate: 1,
});
expect(out).toBeNull();
expect(conn.closed).toBe(true);
// Let top-up finish.
await new Promise((r) => setTimeout(r, 20));
expect(pool.warmSlots().length).toBeGreaterThanOrEqual(
cfg.minSlots - 1
);
await pool.dispose();
});
it("transport survives pool eviction of an active slot", {
timeout: 5000,
}, async () => {
const { factory } = makeFakeFactory();
const t = new StandbyTransport({
pool: { config: cfg, rtcFactory: factory },
});
await t.start();
const events: string[] = [];
t.onPeerEvent((_, ev) => events.push(ev));
await t.connect("peer-Y", "addr");
const slot = t.pool.activeSlots()[0]!;
t.pool.evict(slot.id, "unhealthy");
expect(events).toContain("disconnected");
expect(t.getConnectedPeers()).not.toContain("peer-Y");
await t.close();
});
});

View File

@@ -0,0 +1,75 @@
import type { RtcConnectionLike } from "../../src/types/types.js";
/**
* Deterministic real implementation of `RtcConnectionLike` for tests.
* NOT a mock — every method does real work over an in-memory channel
* pair so the test exercises the same code paths as production.
*/
export class FakeRtcConnection implements RtcConnectionLike {
public connectionState = "new";
public onmessage:
| ((this: RtcConnectionLike, ev: { data: unknown }) => unknown)
| null = null;
public onstatechange: ((this: RtcConnectionLike) => unknown) | null = null;
readonly id: string;
readonly sent: Array<string | Uint8Array> = [];
closed = false;
public constructor(id: string) {
this.id = id;
}
public send(data: string | Uint8Array): void {
if (this.closed || this.connectionState === "failed") {
throw new Error("fake-rtc.send-after-close");
}
this.sent.push(data);
}
public close(): void {
if (this.closed) {
return;
}
this.closed = true;
this.connectionState = "closed";
this.onstatechange?.call(this);
}
public deliver(data: unknown): void {
this.onmessage?.call(this, { data });
}
public transitionTo(state: string): void {
this.connectionState = state;
this.onstatechange?.call(this);
}
}
/**
* Factory builder that returns a `RtcConnectionFactory` over a registry
* mapped by slot id so tests can find and manipulate the underlying
* connection.
*/
export function makeFakeFactory(opts?: {
failOn?: (slotId: string) => boolean;
delayMs?: number;
}): {
factory: (slotId: string) => Promise<RtcConnectionLike>;
registry: Map<string, FakeRtcConnection>;
} {
const registry = new Map<string, FakeRtcConnection>();
const factory = async (slotId: string): Promise<RtcConnectionLike> => {
if (opts?.delayMs && opts.delayMs > 0) {
await new Promise((r) => setTimeout(r, opts.delayMs));
}
if (opts?.failOn?.(slotId)) {
throw new Error("fake-rtc.factory-failure");
}
const conn = new FakeRtcConnection(slotId);
conn.connectionState = "connected";
registry.set(slotId, conn);
return conn;
};
return { factory, registry };
}

View File

@@ -0,0 +1,7 @@
import { webcrypto } from "node:crypto";
type WritableGlobal = { crypto?: Crypto };
const g = globalThis as WritableGlobal;
if (g.crypto === undefined) {
g.crypto = webcrypto as unknown as Crypto;
}