Files
Piik/tests/hybrid-media-router.test.ts
T
AAA 4f2f46c82d fix(route): fix verified routing lifecycle invariants (#332)
* Fix verified routing lifecycle invariants

* Compare delivered candidates against stalled routes
2026-08-30 00:12:32 +08:00

2344 lines
75 KiB
TypeScript

import { describe, expect, it, vi } from "vitest";
import type { RoutePolicy, ServerMessage } from "../src/shared/protocol.ts";
import {
HybridMediaRouter,
type AuthenticatedRouteParticipant,
type HybridAuthenticationState,
} from "../src/server/hybrid-media-router.ts";
import { RoomStore, type CreatedRoom } from "../src/server/room-store.ts";
import { SfuResourceAdmission } from "../src/server/sfu-resource-admission.ts";
import { FakeSfuRoomControl } from "./fake-sfu-room-control.ts";
const SHARE_GENERATION = "share_generation_12345678";
function senderDiagnostics(state: "unknown" | "healthy" | "degraded") {
return {
reason:
state === "unknown" ? null : state === "healthy" ? "none" : "bandwidth",
framesPerSecond: null,
bitrateKbps: null,
} as const;
}
function createStore(maxViewersPerRoom = 20) {
return new RoomStore({
leaseMs: 60_000,
maxRooms: 4,
maxViewersPerRoom,
});
}
function connectHost(
store: RoomStore,
room: CreatedRoom,
routePolicy?: RoutePolicy,
clientId = "host_client_12345678",
sessionId = "host_session_12345678",
) {
const connected = store.connectParticipant({
roomId: room.roomId,
role: "host",
token: room.hostToken,
clientId,
sessionId,
});
return {
roomId: room.roomId,
role: "host" as const,
peerId: connected.peerId,
sessionId,
...(routePolicy ? { routePolicy } : {}),
};
}
function connectViewer(
store: RoomStore,
room: CreatedRoom,
suffix: string,
): AuthenticatedRouteParticipant {
const connected = store.connectParticipant({
roomId: room.roomId,
role: "viewer",
...(room.viewerGrant ? { viewerGrant: room.viewerGrant } : {}),
clientId: `viewer_client_${suffix}_12345678`,
sessionId: `viewer_session_${suffix}_12345678`,
});
return {
roomId: room.roomId,
role: "viewer",
peerId: connected.peerId,
sessionId: `viewer_session_${suffix}_12345678`,
};
}
function complete(
router: HybridMediaRouter,
participant: AuthenticatedRouteParticipant,
): HybridAuthenticationState {
const state = router.connectParticipant(participant);
router.completeAuthentication(participant, state);
return state;
}
function preparedFor(
sent: Map<string, ServerMessage[]>,
sessionId: string,
) {
return sent
.get(sessionId)
?.findLast(
(message): message is Extract<ServerMessage, { type: "route-update"; phase: "prepare" }> =>
message.type === "route-update" && message.phase === "prepare",
);
}
function activeAfter(
sent: Map<string, ServerMessage[]>,
sessionId: string,
revision: number,
) {
return sent
.get(sessionId)
?.findLast(
(message): message is Extract<ServerMessage, { type: "route-update"; phase: "active" }> =>
message.type === "route-update" &&
message.phase === "active" &&
message.revision > revision,
);
}
function deferred(): {
promise: Promise<void>;
resolve: () => void;
reject: (reason?: unknown) => void;
} {
let resolve!: () => void;
let reject!: (reason?: unknown) => void;
const promise = new Promise<void>((settle, fail) => {
resolve = settle;
reject = fail;
});
return { promise, resolve, reject };
}
function deferredValue<T>(): {
promise: Promise<T>;
resolve: (value: T) => void;
} {
let resolve!: (value: T) => void;
const promise = new Promise<T>((settle) => {
resolve = settle;
});
return { promise, resolve };
}
function harness(
endpointMediaCopyCapacity: 1 | 2 | 3,
withSfu = false,
prepareTimeoutMs?: number,
sfuIngressCapacity = 2,
drainRetryMs?: number,
hostOfflineCheckMs?: number,
) {
const store = createStore();
const sent = new Map<string, ServerMessage[]>();
const connections = new Map<string, string>();
const admission = withSfu
? new SfuResourceAdmission({
ingressCapacity: sfuIngressCapacity,
egressCapacity: 20,
})
: undefined;
const roomControl = withSfu ? new FakeSfuRoomControl() : undefined;
const onRoutesChanged = vi.fn();
let nextTokenIssueError: Error | null = null;
const router = new HybridMediaRouter({
roomStore: store,
endpointMediaCopyCapacity,
...(admission && roomControl
? {
sfuFallback: {
url: "wss://sfu.example.test",
admission,
roomControl,
...(prepareTimeoutMs ? { prepareTimeoutMs } : {}),
...(drainRetryMs ? { drainRetryMs } : {}),
...(hostOfflineCheckMs ? { hostOfflineCheckMs } : {}),
tokenIssuer: {
async issueToken({ peerId }) {
if (nextTokenIssueError) {
const error = nextTokenIssueError;
nextTokenIssueError = null;
throw error;
}
return `token-${peerId}`;
},
},
},
}
: {}),
sendToSession(sessionId, message) {
const messages = sent.get(sessionId) ?? [];
messages.push(message);
sent.set(sessionId, messages);
},
getConnectionId(roomId, viewerPeerId) {
return connections.get(`${roomId}:${viewerPeerId}`);
},
setConnectionId(roomId, viewerPeerId, connectionId) {
connections.set(`${roomId}:${viewerPeerId}`, connectionId);
},
deleteConnectionId(roomId, viewerPeerId) {
connections.delete(`${roomId}:${viewerPeerId}`);
},
getShareGeneration: () => SHARE_GENERATION,
onRoutesChanged,
});
return {
store,
sent,
connections,
admission,
roomControl,
onRoutesChanged,
failNextTokenIssue() {
nextTokenIssueError = new Error("token issue failed");
},
router,
};
}
async function establishSfuRoom(
store: RoomStore,
sent: Map<string, ServerMessage[]>,
router: HybridMediaRouter,
room: CreatedRoom,
) {
const host = connectHost(store, room);
complete(router, host);
const first = connectViewer(store, room, `sfu-root-${room.roomId}`);
complete(router, first);
await vi.waitFor(() => expect(preparedFor(sent, first.sessionId)).toBeDefined());
const firstDirect = preparedFor(sent, first.sessionId)!;
router.handleRouteReady(first, {
type: "route-ready",
revision: firstDirect.revision,
phase: "prepare",
});
const second = connectViewer(store, room, `sfu-leaf-${room.roomId}`);
complete(router, second);
await vi.waitFor(() =>
expect(preparedFor(sent, first.sessionId)?.candidate.transport).toBe("sfu"),
);
const firstSfu = preparedFor(sent, first.sessionId)!;
router.handleRouteReady(first, {
type: "route-ready",
revision: firstSfu.revision,
phase: "prepare",
});
await vi.waitFor(() =>
expect(preparedFor(sent, second.sessionId)?.candidate.transport).toBe("sfu"),
);
const secondSfu = preparedFor(sent, second.sessionId)!;
router.handleRouteReady(second, {
type: "route-ready",
revision: secondSfu.revision,
phase: "prepare",
});
return { host, first, second };
}
describe("HybridMediaRouter v9 runtime", () => {
it("projects exact endpoint copy context from the current snapshot", async () => {
const { router } = harness(2);
const internal = router as unknown as {
qualityCopyContext(
snapshot: unknown,
hostPeerId: string,
observedPeerId: string,
endpointCapacity: number,
sample: unknown,
): Record<string, unknown>;
};
const edge = (parentPeerId: string, connectionId: string) => ({
kind: "peer", childSessionId: "child-session", parentPeerId,
parentSessionId: "parent-session", transport: "direct", connectionId,
usable: true, physicalActive: true,
});
const operation = (tuple: unknown, transition: unknown) => ({
childPeerId: "candidate", childSessionId: "candidate-session",
demandPeerId: "candidate", reason: "quality-convergence",
baseRevision: 7, factVersion: 1, candidates: [{ tuple, endpointTransition: transition }],
cursor: 0, deadlineAtMs: 20_000, wakeAtMs: 20_000,
current: { tuple, revision: 8, connectionId: "candidate-connection" },
});
const snapshot = (
upstreamByViewer: Map<string, unknown>,
op?: unknown,
hostPublication: unknown = null,
) => ({
revision: 7, paused: false, factVersion: 1, upstreamByViewer,
hostPublication, ...(op ? { operation: op } : {}),
});
const project = (
state: unknown,
observedPeerId: string,
endpointCapacity: number,
sample: unknown,
) => internal.qualityCopyContext(
state, "host", observedPeerId, endpointCapacity, sample,
);
const hostEdges = new Map<string, unknown>([
["a", edge("host", "a-connection")],
["b", edge("host", "b-connection")],
]);
const peerCandidate = {
kind: "peer", parentPeerId: "host", transport: "direct",
};
const sfuSample = {
kind: "sfu", routeRevision: 8,
publicationGeneration: "publication", hostSessionId: "host-session",
};
const publication = {
generation: "publication", hostSessionId: "host-session",
connectionId: "publication-connection", usable: true,
physicalActive: true, resource: {},
};
try {
expect(project(
snapshot(hostEdges, operation(peerCandidate, {
kind: "overlap", producerPeerId: "host",
})),
"host", 2,
{ kind: "peer", childPeerId: "candidate", routeRevision: 8,
connectionId: "candidate-connection" },
)).toMatchObject({
committedCopies: 2, candidateReservedCopies: 1, possibleCopies: 3,
endpointCapacity: 2, operationReason: "quality-convergence",
candidateTransition: "overlap", sampleRole: "candidate",
});
const oneHostEdge = new Map<string, unknown>([
["a", edge("host", "a-connection")],
]);
const sfuCases = [
{
state: snapshot(hostEdges, operation(
{ kind: "sfu", publication: "create" },
{ kind: "overlap", producerPeerId: "host" },
)),
expected: { committedCopies: 2, candidateReservedCopies: 1,
possibleCopies: 3, candidateTransition: "overlap" },
},
{
state: snapshot(oneHostEdge, operation(
{ kind: "sfu", publication: "reuse" }, { kind: "none" },
), publication),
expected: { committedCopies: 2, candidateReservedCopies: 0,
possibleCopies: 2, candidateTransition: "none" },
},
];
for (const testCase of sfuCases) {
expect(project(testCase.state, "host", 2, sfuSample)).toMatchObject({
...testCase.expected, sampleRole: "candidate",
});
}
const relayEdges = new Map<string, unknown>([
["a", edge("relay", "relay-a")],
["b", edge("relay", "relay-b")],
]);
const relaySample = {
kind: "peer", childPeerId: "a", routeRevision: 7,
connectionId: "relay-a",
};
expect(project(snapshot(relayEdges), "relay", 3, relaySample)).toMatchObject({
committedCopies: 2, candidateReservedCopies: 0, possibleCopies: 2,
endpointCapacity: 3, sampleRole: "active",
});
expect(project(
snapshot(relayEdges), "relay", 3,
{ ...relaySample, routeRevision: 6 },
).sampleRole).toBe("unknown");
} finally {
await router.close();
}
});
it("keeps the controller diagnostic label until a departed relay is pruned", async () => {
const { store, sent, router } = harness(2);
try {
const room = await store.createRoom();
const host = connectHost(store, room);
complete(router, host);
const parent = connectViewer(store, room, "diagnostic-label-parent");
complete(router, parent);
const parentPrepare = await vi.waitFor(() => {
const prepared = preparedFor(sent, parent.sessionId);
expect(prepared).toBeDefined();
return prepared!;
});
router.handleRouteReady(parent, {
type: "route-ready",
revision: parentPrepare.revision,
phase: "prepare",
});
router.setViewerRelayCapacity(parent, 1);
const temporaryRoot = connectViewer(
store,
room,
"diagnostic-label-temporary-root",
);
complete(router, temporaryRoot);
const temporaryPrepare = await vi.waitFor(() => {
const prepared = preparedFor(sent, temporaryRoot.sessionId);
expect(prepared?.assignment.upstream).toEqual({
kind: "peer",
peerId: host.peerId,
});
return prepared!;
});
router.handleRouteReady(temporaryRoot, {
type: "route-ready",
revision: temporaryPrepare.revision,
phase: "prepare",
});
const child = connectViewer(store, room, "diagnostic-label-child");
complete(router, child);
const childPrepare = await vi.waitFor(() => {
const prepared = preparedFor(sent, child.sessionId);
expect(prepared?.assignment.upstream).toEqual({
kind: "peer",
peerId: parent.peerId,
});
return prepared!;
});
router.handleRouteReady(child, {
type: "route-ready",
revision: childPrepare.revision,
phase: "prepare",
});
store.disconnectParticipant(
room.roomId,
temporaryRoot.peerId,
temporaryRoot.sessionId,
);
router.disconnectParticipant(
room.roomId,
temporaryRoot.peerId,
temporaryRoot.sessionId,
);
router.removeViewer(room.roomId, temporaryRoot.peerId);
await vi.waitFor(() =>
expect(
router.resolveActiveViewerMediaEdge(
room.roomId,
temporaryRoot.peerId,
),
).toBeUndefined(),
);
const internal = router as unknown as {
debugPeer(roomId: string, peerId: string): string;
};
const label = internal.debugPeer(room.roomId, parent.peerId);
store.disconnectParticipant(
room.roomId,
parent.peerId,
parent.sessionId,
);
router.disconnectParticipant(
room.roomId,
parent.peerId,
parent.sessionId,
);
router.removeViewer(room.roomId, parent.peerId);
expect(internal.debugPeer(room.roomId, parent.peerId)).toBe(label);
const recovery = await vi.waitFor(() => {
const prepared = preparedFor(sent, child.sessionId);
expect(prepared?.revision).toBeGreaterThan(childPrepare.revision);
expect(prepared?.assignment.upstream).toEqual({
kind: "peer",
peerId: host.peerId,
});
return prepared!;
});
router.handleRouteReady(child, {
type: "route-ready",
revision: recovery.revision,
phase: "prepare",
});
await vi.waitFor(() =>
expect(internal.debugPeer(room.roomId, parent.peerId)).toBe(
"viewer-unknown",
),
);
} finally {
await router.close();
}
});
it("maps an exact Host active failure to its committed direct edge", async () => {
const { store, sent, router } = harness(2);
try {
const room = await store.createRoom();
const host = connectHost(store, room);
complete(router, host);
const viewer = connectViewer(store, room, "host-edge-failure");
complete(router, viewer);
await vi.waitFor(() =>
expect(preparedFor(sent, viewer.sessionId)).toBeDefined(),
);
const prepared = preparedFor(sent, viewer.sessionId)!;
router.handleRouteReady(viewer, {
type: "route-ready",
revision: prepared.revision,
phase: "prepare",
});
const active = router.resolveActiveViewerMediaEdge(
room.roomId,
viewer.peerId,
)!;
router.handleRouteFailed(host, {
type: "route-failed",
revision: active.revision,
phase: "active",
connectionId: active.connectionId,
});
await vi.waitFor(() =>
expect(
sent
.get(viewer.sessionId)
?.findLast((message) => message.type === "route-status"),
).toMatchObject({
state: "failed",
reason: "route-exhausted",
}),
);
} finally {
await router.close();
}
});
it("rebinds a replacement Host and clears offline Viewer media ownership", async () => {
const { store, sent, connections, router } = harness(2);
try {
const room = await store.createRoom();
const previousHost = connectHost(store, room);
complete(router, previousHost);
const viewer = connectViewer(store, room, "host-rebind-offline-viewer");
complete(router, viewer);
const prepared = await vi.waitFor(() => {
const message = preparedFor(sent, viewer.sessionId);
expect(message).toBeDefined();
return message!;
});
router.handleRouteReady(viewer, {
type: "route-ready",
revision: prepared.revision,
phase: "prepare",
});
router.setViewerRelayCapacity(viewer, 1);
await vi.waitFor(() =>
expect(connections.has(`${room.roomId}:${viewer.peerId}`)).toBe(true),
);
store.disconnectParticipant(room.roomId, viewer.peerId, viewer.sessionId);
router.disconnectParticipant(room.roomId, viewer.peerId, viewer.sessionId);
store.disconnectParticipant(
room.roomId,
previousHost.peerId,
previousHost.sessionId,
);
router.disconnectParticipant(
room.roomId,
previousHost.peerId,
previousHost.sessionId,
);
const resumedHost = connectHost(
store,
room,
undefined,
"replacement-host-client",
"replacement-host-session",
);
expect(resumedHost.peerId).not.toBe(previousHost.peerId);
complete(router, resumedHost);
expect(connections.has(`${room.roomId}:${viewer.peerId}`)).toBe(false);
const reconnectedViewer = connectViewer(
store,
room,
"host-rebind-offline-viewer",
);
expect(reconnectedViewer.peerId).toBe(viewer.peerId);
router.connectParticipant(reconnectedViewer);
expect(
router
.routeDiagnosticSnapshot(room.roomId)
.children.find((child) => child.effectiveCapacity === 1),
).toBeDefined();
} finally {
await router.close();
}
});
it("accepts active failure for a direct connection adopted during rebuild", async () => {
const { store, sent, router } = harness(2);
try {
const room = await store.createRoom();
const host = connectHost(store, room);
complete(router, host);
const viewer = connectViewer(store, room, "rebuilt-edge-failure");
complete(router, viewer);
await vi.waitFor(() =>
expect(preparedFor(sent, viewer.sessionId)).toBeDefined(),
);
const prepared = preparedFor(sent, viewer.sessionId)!;
router.handleRouteReady(viewer, {
type: "route-ready",
revision: prepared.revision,
phase: "prepare",
});
const active = router.resolveActiveViewerMediaEdge(
room.roomId,
viewer.peerId,
)!;
const rebuiltConnectionId = "rebuilt_connection_12345678";
expect(
router.peerSignalAuthorization({
roomId: room.roomId,
sourcePeerId: host.peerId,
sourceSessionId: host.sessionId,
targetPeerId: viewer.peerId,
targetSessionId: viewer.sessionId,
connectionId: rebuiltConnectionId,
signalKind: "description",
descriptionType: "offer",
}),
).toBe(true);
expect(
router.resolveActiveViewerMediaEdge(room.roomId, viewer.peerId),
).toMatchObject({
revision: active.revision,
connectionId: rebuiltConnectionId,
});
router.handleRouteFailed(viewer, {
type: "route-failed",
revision: active.revision,
phase: "active",
connectionId: rebuiltConnectionId,
});
await vi.waitFor(() =>
expect(
sent
.get(viewer.sessionId)
?.findLast((message) => message.type === "route-status"),
).toMatchObject({
state: "failed",
reason: "route-exhausted",
}),
);
} finally {
await router.close();
}
});
it("commits P2P quality convergence after client relative approval", async () => {
const { store, sent, router } = harness(2);
try {
const room = await store.createRoom();
const host = connectHost(store, room, {
peerOnly: false,
topologyOptimization: true,
});
complete(router, host);
const first = connectViewer(store, room, "quality-first");
complete(router, first);
await vi.waitFor(() => expect(preparedFor(sent, first.sessionId)).toBeDefined());
const firstPrepare = preparedFor(sent, first.sessionId)!;
expect(firstPrepare.candidate.qualityProbe).toBe(false);
router.handleRouteReady(first, {
type: "route-ready",
revision: firstPrepare.revision,
phase: "prepare",
});
const second = connectViewer(store, room, "quality-second");
complete(router, second);
await vi.waitFor(() => expect(preparedFor(sent, second.sessionId)).toBeDefined());
const secondPrepare = preparedFor(sent, second.sessionId)!;
router.handleRouteReady(second, {
type: "route-ready",
revision: secondPrepare.revision,
phase: "prepare",
});
router.setViewerRelayCapacity(second, 3);
const firstEdge = router.resolveActiveViewerMediaEdge(room.roomId, first.peerId)!;
const secondEdge = router.resolveActiveViewerMediaEdge(room.roomId, second.peerId)!;
expect(firstEdge.upstream).toEqual({ kind: "peer", peerId: host.peerId });
expect(secondEdge.upstream).toEqual({ kind: "peer", peerId: host.peerId });
const debug = vi.spyOn(
router as unknown as {
debug(
roomId: string,
event: string,
details: Record<string, unknown>,
): void;
},
"debug",
);
expect(
router.observeSenderQualityEvidence(host, {
type: "sender-quality-evidence",
childPeerId: second.peerId,
connectionId: secondEdge.connectionId,
rtpStatsId: "second-rtp",
trackIdentifier: "track",
sampleTimestampMs: 100,
routeRevision: secondEdge.revision,
state: "healthy",
diagnostics: senderDiagnostics("healthy"),
}),
).toBe(true);
expect(debug).toHaveBeenCalledWith(
room.roomId,
"sender-quality-evidence",
expect.objectContaining({
routeRevision: secondEdge.revision,
committedCopies: 2,
candidateReservedCopies: 0,
possibleCopies: 2,
endpointCapacity: 2,
operationReason: null,
candidateTransition: null,
sampleRole: "active",
}),
);
expect(
router.observeSenderQualityEvidence(host, {
type: "sender-quality-evidence",
childPeerId: first.peerId,
connectionId: firstEdge.connectionId,
rtpStatsId: "first-rtp",
trackIdentifier: "track",
sampleTimestampMs: 100,
routeRevision: firstEdge.revision,
state: "healthy",
diagnostics: senderDiagnostics("healthy"),
}),
).toBe(true);
for (let window = 0; window < 3; window += 1) {
expect(
router.observeSenderQualityEvidence(host, {
type: "sender-quality-evidence",
childPeerId: first.peerId,
connectionId: firstEdge.connectionId,
rtpStatsId: "first-rtp",
trackIdentifier: "track",
sampleTimestampMs: 101 + window,
routeRevision: firstEdge.revision,
state: "degraded",
diagnostics: senderDiagnostics("degraded"),
}),
).toBe(true);
}
await vi.waitFor(() =>
expect(preparedFor(sent, first.sessionId)?.revision).toBeGreaterThan(
firstEdge.revision,
),
);
const qualityPrepare = preparedFor(sent, first.sessionId)!;
expect(qualityPrepare.candidate.qualityProbe).toBe(true);
expect(
router.observeSenderQualityEvidence(second, {
type: "sender-quality-evidence",
childPeerId: first.peerId,
connectionId: qualityPrepare.candidate.connectionId,
rtpStatsId: "candidate-rtp",
trackIdentifier: "track",
sampleTimestampMs: 200,
routeRevision: qualityPrepare.revision,
state: "healthy",
diagnostics: senderDiagnostics("healthy"),
}),
).toBe(true);
expect(debug).toHaveBeenCalledWith(
room.roomId,
"sender-quality-evidence",
expect.objectContaining({
routeRevision: qualityPrepare.revision,
committedCopies: 0,
candidateReservedCopies: 1,
possibleCopies: 1,
endpointCapacity: 2,
operationReason: "quality-convergence",
candidateTransition: "none",
sampleRole: "candidate",
}),
);
router.handleRouteReady(first, {
type: "route-ready",
revision: qualityPrepare.revision,
phase: "prepare",
});
expect(
router.resolveActiveViewerMediaEdge(room.roomId, first.peerId)?.upstream,
).toEqual({ kind: "peer", peerId: host.peerId });
router.handleRouteReady(first, {
type: "route-ready",
revision: qualityPrepare.revision,
phase: "prepare",
qualityApproved: true,
});
expect(
router.resolveActiveViewerMediaEdge(room.roomId, first.peerId)?.upstream,
).toEqual({ kind: "peer", peerId: second.peerId });
} finally {
await router.close();
}
});
it("keeps peer-only shares out of configured SFU fallback", async () => {
const { store, sent, router } = harness(1, true);
try {
const room = await store.createRoom();
const host = connectHost(store, room, {
peerOnly: true,
topologyOptimization: false,
});
complete(router, host);
const first = connectViewer(store, room, "peer-only-first");
complete(router, first);
await vi.waitFor(() =>
expect(preparedFor(sent, first.sessionId)?.candidate.transport).toBe(
"direct",
),
);
const direct = preparedFor(sent, first.sessionId)!;
router.handleRouteReady(first, {
type: "route-ready",
revision: direct.revision,
phase: "prepare",
});
const blocked = connectViewer(store, room, "peer-only-blocked");
complete(router, blocked);
await vi.waitFor(() =>
expect(
sent
.get(blocked.sessionId)
?.findLast((message) => message.type === "route-status"),
).toMatchObject({ state: "failed", reason: "route-exhausted" }),
);
expect(
[...sent.values()]
.flat()
.some((message) => message.type === "sfu-config"),
).toBe(false);
} finally {
await router.close();
}
});
it("silently keeps working media when quality-only SFU admission is denied", async () => {
const { store, sent, admission, router } = harness(1, true, undefined, 1);
const occupied = {
roomId: "9001",
shareGeneration: "occupied_share_12345678",
publicationGeneration: "occupied_publication_12345678",
};
try {
expect(admission?.reservePublication(occupied)).toBe(true);
const room = await store.createRoom();
const host = connectHost(store, room, {
peerOnly: false,
topologyOptimization: true,
});
complete(router, host);
const viewer = connectViewer(store, room, "quality-sfu-denied");
complete(router, viewer);
await vi.waitFor(() => expect(preparedFor(sent, viewer.sessionId)).toBeDefined());
const prepared = preparedFor(sent, viewer.sessionId)!;
router.handleRouteReady(viewer, {
type: "route-ready",
revision: prepared.revision,
phase: "prepare",
});
const active = router.resolveActiveViewerMediaEdge(room.roomId, viewer.peerId)!;
for (const [index, state] of (
["healthy", "degraded"] as const
).entries()) {
expect(
router.observeSenderQualityEvidence(host, {
type: "sender-quality-evidence",
childPeerId: viewer.peerId,
connectionId: active.connectionId,
rtpStatsId: "viewer-rtp",
trackIdentifier: "track",
sampleTimestampMs: 100 + index,
routeRevision: active.revision,
state,
diagnostics: senderDiagnostics(state),
}),
).toBe(true);
}
await vi.waitFor(() =>
expect(router.routeDiagnosticSnapshot(room.roomId).operation).toBeNull(),
);
expect(
sent
.get(viewer.sessionId)
?.some((message) => message.type === "route-status"),
).toBe(false);
expect(
router.resolveActiveViewerMediaEdge(room.roomId, viewer.peerId),
).toMatchObject({
connectionId: active.connectionId,
upstream: { kind: "peer", peerId: host.peerId },
});
} finally {
if (admission) {
admission.beginDrain(occupied);
admission.completeDrain(occupied);
}
await router.close();
}
});
it("notifies the presence owner after the committed graph changes", async () => {
const { store, sent, router, onRoutesChanged } = harness(1, true);
try {
const room = await store.createRoom();
await establishSfuRoom(
store,
sent,
router,
room,
);
await vi.waitFor(() => {
expect(onRoutesChanged.mock.calls.at(-1)).toEqual([room.roomId]);
});
} finally {
await router.close();
}
});
it("wakes at the derived direct boundary and prepares SFU automatically", async () => {
vi.useFakeTimers();
vi.setSystemTime(0);
const { store, sent, router } = harness(2, true, 300);
try {
const room = await store.createRoom();
const host = connectHost(store, room);
complete(router, host);
const viewer = connectViewer(store, room, "staged-deadline");
complete(router, viewer);
await vi.waitFor(() =>
expect(preparedFor(sent, viewer.sessionId)?.candidate.transport).toBe(
"direct",
),
);
await vi.advanceTimersByTimeAsync(150);
await vi.waitFor(() =>
expect(preparedFor(sent, viewer.sessionId)?.candidate.transport).toBe(
"sfu",
),
);
expect(
sent
.get(viewer.sessionId)
?.some(
(message) =>
message.type === "route-status" && message.state === "failed",
),
).toBe(false);
} finally {
await router.close();
vi.useRealTimers();
}
});
it("keeps an exact transport-connected direct candidate past the boundary", async () => {
vi.useFakeTimers();
vi.setSystemTime(0);
const { store, sent, router } = harness(2, true, 300);
try {
const room = await store.createRoom();
const host = connectHost(store, room);
complete(router, host);
const viewer = connectViewer(store, room, "transport-connected");
complete(router, viewer);
await vi.waitFor(() =>
expect(preparedFor(sent, viewer.sessionId)?.candidate.transport).toBe(
"direct",
),
);
const direct = preparedFor(sent, viewer.sessionId)!;
router.handleRouteTransportConnected(viewer, {
type: "route-transport-connected",
revision: direct.revision,
connectionId: direct.candidate.connectionId,
});
await vi.advanceTimersByTimeAsync(150);
expect(preparedFor(sent, viewer.sessionId)).toEqual(direct);
router.handleRouteReady(viewer, {
type: "route-ready",
revision: direct.revision,
phase: "prepare",
});
await vi.waitFor(() =>
expect(activeAfter(sent, viewer.sessionId, direct.revision - 1)).toMatchObject({
assignment: { upstream: { kind: "peer" } },
}),
);
} finally {
await router.close();
vi.useRealTimers();
}
});
it("uses the direct boundary to bootstrap SFU when every Host slot is full", async () => {
vi.useFakeTimers();
vi.setSystemTime(0);
const { store, sent, router } = harness(2, true, 300);
try {
const room = await store.createRoom();
const host = connectHost(store, room);
complete(router, host);
const firstRoot = connectViewer(store, room, "first-root");
complete(router, firstRoot);
await vi.waitFor(() =>
expect(preparedFor(sent, firstRoot.sessionId)?.candidate.transport).toBe(
"direct",
),
);
const firstPrepare = preparedFor(sent, firstRoot.sessionId)!;
router.handleRouteReady(firstRoot, {
type: "route-ready",
revision: firstPrepare.revision,
phase: "prepare",
});
router.setViewerRelayCapacity(firstRoot, 2);
const secondRoot = connectViewer(store, room, "second-root");
complete(router, secondRoot);
await vi.waitFor(() =>
expect(preparedFor(sent, secondRoot.sessionId)?.candidate.transport).toBe(
"direct",
),
);
const secondPrepare = preparedFor(sent, secondRoot.sessionId)!;
router.handleRouteReady(secondRoot, {
type: "route-ready",
revision: secondPrepare.revision,
phase: "prepare",
});
const waiting = connectViewer(store, room, "waiting");
complete(router, waiting);
await vi.waitFor(() =>
expect(preparedFor(sent, waiting.sessionId)?.candidate.transport).toBe(
"direct",
),
);
await vi.advanceTimersByTimeAsync(150);
await vi.waitFor(() =>
expect(preparedFor(sent, secondRoot.sessionId)?.candidate.transport).toBe(
"sfu",
),
);
const secondBootstrap = preparedFor(sent, secondRoot.sessionId)!;
router.handleRouteFailed(secondRoot, {
type: "route-failed",
revision: secondBootstrap.revision,
phase: "prepare",
connectionId: secondBootstrap.candidate.connectionId,
});
await vi.waitFor(() =>
expect(preparedFor(sent, firstRoot.sessionId)?.candidate.transport).toBe(
"sfu",
),
);
expect(
sent
.get(secondRoot.sessionId)
?.some(
(message) =>
message.type === "route-status" && message.state === "failed",
),
).toBe(false);
const firstBootstrap = preparedFor(sent, firstRoot.sessionId)!;
router.handleRouteFailed(firstRoot, {
type: "route-failed",
revision: firstBootstrap.revision,
phase: "prepare",
connectionId: firstBootstrap.candidate.connectionId,
});
await vi.waitFor(() =>
expect(
sent
.get(waiting.sessionId)
?.findLast((message) => message.type === "route-status"),
).toMatchObject({
state: "failed",
reason: "route-exhausted",
}),
);
for (const root of [firstRoot, secondRoot]) {
expect(
sent
.get(root.sessionId)
?.some(
(message) =>
message.type === "route-status" && message.state === "failed",
),
).toBe(false);
}
expect(
sent
.get(waiting.sessionId)
?.some(
(message) =>
message.type === "route-status" && message.state === "failed",
),
).toBe(true);
} finally {
await router.close();
vi.useRealTimers();
}
});
it("does not recreate SFU after an exhausted reuse and carrier teardown", async () => {
vi.useFakeTimers();
vi.setSystemTime(0);
const { store, sent, admission, roomControl, router } = harness(
2,
true,
300,
);
const sfuConfigCount = () =>
[...sent.values()]
.flat()
.filter((message) => message.type === "sfu-config").length;
const ownPrepare = (participant: AuthenticatedRouteParticipant) =>
sent
.get(participant.sessionId)
?.findLast(
(
message,
): message is Extract<
ServerMessage,
{ type: "route-update"; phase: "prepare" }
> =>
message.type === "route-update" &&
message.phase === "prepare" &&
message.candidate.childPeerId === participant.peerId,
);
try {
const room = await store.createRoom();
const host = connectHost(store, room);
complete(router, host);
const firstRoot = connectViewer(store, room, "reuse-loop-first-root");
complete(router, firstRoot);
await vi.waitFor(() =>
expect(preparedFor(sent, firstRoot.sessionId)?.candidate.transport).toBe(
"direct",
),
);
const firstDirect = preparedFor(sent, firstRoot.sessionId)!;
router.handleRouteReady(firstRoot, {
type: "route-ready",
revision: firstDirect.revision,
phase: "prepare",
});
router.setViewerRelayCapacity(firstRoot, 1);
const secondRoot = connectViewer(store, room, "reuse-loop-second-root");
complete(router, secondRoot);
await vi.waitFor(() =>
expect(preparedFor(sent, secondRoot.sessionId)?.candidate.transport).toBe(
"direct",
),
);
const secondDirect = preparedFor(sent, secondRoot.sessionId)!;
router.handleRouteReady(secondRoot, {
type: "route-ready",
revision: secondDirect.revision,
phase: "prepare",
});
const departedParent = connectViewer(
store,
room,
"reuse-loop-departed-parent",
);
complete(router, departedParent);
await vi.waitFor(() =>
expect(
preparedFor(sent, departedParent.sessionId)?.assignment.upstream,
).toEqual({ kind: "peer", peerId: firstRoot.peerId }),
);
const parentDirect = preparedFor(sent, departedParent.sessionId)!;
router.handleRouteReady(departedParent, {
type: "route-ready",
revision: parentDirect.revision,
phase: "prepare",
});
router.setViewerRelayCapacity(departedParent, 1);
const demand = connectViewer(store, room, "reuse-loop-demand");
complete(router, demand);
await vi.waitFor(() =>
expect(preparedFor(sent, demand.sessionId)?.assignment.upstream).toEqual(
{ kind: "peer", peerId: departedParent.peerId },
),
);
const demandDirect = preparedFor(sent, demand.sessionId)!;
router.handleRouteReady(demand, {
type: "route-ready",
revision: demandDirect.revision,
phase: "prepare",
});
const departureAt = Date.now();
store.disconnectParticipant(
room.roomId,
departedParent.peerId,
departedParent.sessionId,
);
router.disconnectParticipant(
room.roomId,
departedParent.peerId,
departedParent.sessionId,
);
router.removeViewer(room.roomId, departedParent.peerId);
router.setViewerRelayCapacity(secondRoot, 1);
await vi.waitFor(() => {
const recovery = ownPrepare(demand);
expect(recovery?.revision).toBeGreaterThan(demandDirect.revision);
expect(recovery?.candidate).toMatchObject({
childPeerId: demand.peerId,
transport: "direct",
});
});
await vi.advanceTimersByTimeAsync(
Math.max(0, departureAt + 150 - Date.now()),
);
const carrier = await vi.waitFor(() => {
const value = [firstRoot, secondRoot].find(
(viewer) =>
ownPrepare(viewer)?.candidate.transport === "sfu",
);
expect(value).toBeDefined();
return value!;
});
const bootstrap = ownPrepare(carrier)!;
router.handleRouteReady(carrier, {
type: "route-ready",
revision: bootstrap.revision,
phase: "prepare",
});
await vi.waitFor(() =>
expect(preparedFor(sent, demand.sessionId)?.candidate.transport).toBe(
"sfu",
),
);
expect(roomControl?.created).toHaveLength(1);
const configCountAfterBootstrap = sfuConfigCount();
expect(configCountAfterBootstrap).toBeGreaterThanOrEqual(3);
await vi.advanceTimersByTimeAsync(
Math.max(0, departureAt + 300 - Date.now()),
);
await vi.waitFor(() =>
expect(ownPrepare(carrier)?.candidate.transport).toBe(
"direct",
),
);
expect(router.routeDiagnosticSnapshot(room.roomId).operation).toMatchObject({
reason: "direct-convergence",
});
const carrierDirect = ownPrepare(carrier)!;
router.handleRouteReady(carrier, {
type: "route-ready",
revision: carrierDirect.revision,
phase: "prepare",
});
await vi.waitFor(() =>
expect(
router.resolveActiveViewerMediaEdge(room.roomId, carrier.peerId)
?.upstream,
).toEqual({ kind: "peer", peerId: host.peerId }),
);
await vi.waitFor(() => expect(roomControl?.deleted).toHaveLength(1));
await vi.advanceTimersByTimeAsync(300);
expect(roomControl?.created).toHaveLength(1);
expect(sfuConfigCount()).toBe(configCountAfterBootstrap);
expect(admission?.usage()).toEqual({ ingress: 0, egress: 0 });
} finally {
await router.close();
vi.useRealTimers();
}
});
it("reports a late final SFU bootstrap ready only to the waiting demand", async () => {
vi.useFakeTimers();
vi.setSystemTime(0);
const { store, sent, router } = harness(2, true, 300);
try {
const room = await store.createRoom();
const host = connectHost(store, room);
complete(router, host);
const firstRoot = connectViewer(store, room, "late-ready-first-root");
complete(router, firstRoot);
await vi.waitFor(() =>
expect(preparedFor(sent, firstRoot.sessionId)?.candidate.transport).toBe(
"direct",
),
);
const firstPrepare = preparedFor(sent, firstRoot.sessionId)!;
router.handleRouteReady(firstRoot, {
type: "route-ready",
revision: firstPrepare.revision,
phase: "prepare",
});
router.setViewerRelayCapacity(firstRoot, 2);
const secondRoot = connectViewer(store, room, "late-ready-second-root");
complete(router, secondRoot);
await vi.waitFor(() =>
expect(preparedFor(sent, secondRoot.sessionId)?.candidate.transport).toBe(
"direct",
),
);
const secondPrepare = preparedFor(sent, secondRoot.sessionId)!;
router.handleRouteReady(secondRoot, {
type: "route-ready",
revision: secondPrepare.revision,
phase: "prepare",
});
const waiting = connectViewer(store, room, "late-ready-waiting");
complete(router, waiting);
await vi.waitFor(() =>
expect(preparedFor(sent, waiting.sessionId)?.candidate.transport).toBe(
"direct",
),
);
await vi.advanceTimersByTimeAsync(150);
await vi.waitFor(() =>
expect(preparedFor(sent, secondRoot.sessionId)?.candidate.transport).toBe(
"sfu",
),
);
const firstCarrier = preparedFor(sent, secondRoot.sessionId)!;
router.handleRouteFailed(secondRoot, {
type: "route-failed",
revision: firstCarrier.revision,
phase: "prepare",
connectionId: firstCarrier.candidate.connectionId,
});
await vi.waitFor(() =>
expect(preparedFor(sent, firstRoot.sessionId)?.candidate.transport).toBe(
"sfu",
),
);
const finalCarrier = preparedFor(sent, firstRoot.sessionId)!;
vi.setSystemTime(451);
router.handleRouteReady(firstRoot, {
type: "route-ready",
revision: finalCarrier.revision,
phase: "prepare",
});
await vi.waitFor(() =>
expect(
sent
.get(waiting.sessionId)
?.filter(
(message) =>
message.type === "route-status" &&
message.state === "failed" &&
message.reason === "route-exhausted",
),
).toHaveLength(1),
);
for (const carrier of [firstRoot, secondRoot]) {
expect(
sent
.get(carrier.sessionId)
?.some(
(message) =>
message.type === "route-status" && message.state === "failed",
),
).toBe(false);
}
} finally {
await router.close();
vi.useRealTimers();
}
});
it("reports bounded route exhaustion without raw error text", async () => {
vi.useFakeTimers();
vi.setSystemTime(0);
const { store, sent, router } = harness(2, true, 300);
try {
const room = await store.createRoom();
const host = connectHost(store, room);
complete(router, host);
const viewer = connectViewer(store, room, "route-exhausted");
complete(router, viewer);
await vi.waitFor(() =>
expect(preparedFor(sent, viewer.sessionId)?.candidate.transport).toBe(
"direct",
),
);
await vi.advanceTimersByTimeAsync(150);
await vi.waitFor(() =>
expect(preparedFor(sent, viewer.sessionId)?.candidate.transport).toBe(
"sfu",
),
);
await vi.advanceTimersByTimeAsync(150);
await vi.waitFor(() =>
expect(
sent
.get(viewer.sessionId)
?.findLast((message) => message.type === "route-status"),
).toMatchObject({
type: "route-status",
state: "failed",
reason: "route-exhausted",
}),
);
expect(
sent
.get(viewer.sessionId)
?.some(
(message) =>
message.type === "error" &&
message.message.includes("media route"),
),
).toBe(false);
} finally {
await router.close();
vi.useRealTimers();
}
});
it("reports typed exhaustion when the exact candidate fails", async () => {
const { store, sent, router } = harness(1);
try {
const room = await store.createRoom();
const host = connectHost(store, room);
complete(router, host);
const viewer = connectViewer(store, room, "candidate-failed");
complete(router, viewer);
await vi.waitFor(() =>
expect(preparedFor(sent, viewer.sessionId)).toBeDefined(),
);
const prepared = preparedFor(sent, viewer.sessionId)!;
router.handleRouteFailed(viewer, {
type: "route-failed",
revision: prepared.revision,
phase: "prepare",
connectionId: prepared.candidate.connectionId,
});
await vi.waitFor(() =>
expect(
sent
.get(viewer.sessionId)
?.findLast((message) => message.type === "route-status"),
).toMatchObject({
state: "failed",
reason: "route-exhausted",
}),
);
} finally {
await router.close();
}
});
it("reports typed exhaustion when reconciliation has no candidate", async () => {
const { store, sent, router } = harness(1);
try {
const room = await store.createRoom();
const host = connectHost(store, room);
complete(router, host);
const first = connectViewer(store, room, "only-slot");
complete(router, first);
await vi.waitFor(() =>
expect(preparedFor(sent, first.sessionId)).toBeDefined(),
);
const firstPrepare = preparedFor(sent, first.sessionId)!;
router.handleRouteReady(first, {
type: "route-ready",
revision: firstPrepare.revision,
phase: "prepare",
});
const blocked = connectViewer(store, room, "no-candidate");
complete(router, blocked);
await vi.waitFor(() =>
expect(
sent
.get(blocked.sessionId)
?.findLast((message) => message.type === "route-status"),
).toMatchObject({
state: "failed",
reason: "route-exhausted",
}),
);
} finally {
await router.close();
}
});
it("reports only SFU admission waiting while bounded capacity is unavailable", async () => {
vi.useFakeTimers();
vi.setSystemTime(0);
const { store, sent, admission, router } = harness(2, true, 300);
const capacityFences = [
{
roomId: "9001",
shareGeneration: "capacity_share_a",
publicationGeneration: "capacity_publication_a",
},
{
roomId: "9002",
shareGeneration: "capacity_share_b",
publicationGeneration: "capacity_publication_b",
},
] as const;
try {
for (const fence of capacityFences) {
expect(admission?.reservePublication(fence)).toBe(true);
}
const room = await store.createRoom();
const host = connectHost(store, room);
complete(router, host);
const viewer = connectViewer(store, room, "sfu-admission-wait");
complete(router, viewer);
await vi.waitFor(() =>
expect(preparedFor(sent, viewer.sessionId)?.candidate.transport).toBe(
"direct",
),
);
await vi.advanceTimersByTimeAsync(150);
await vi.waitFor(() =>
expect(
sent
.get(viewer.sessionId)
?.findLast((message) => message.type === "route-status"),
).toMatchObject({
type: "route-status",
state: "waiting",
reason: "sfu-admission",
}),
);
} finally {
for (const fence of capacityFences) {
admission?.beginDrain(fence);
admission?.completeDrain(fence);
}
await router.close();
vi.useRealTimers();
}
});
it("physically removes an exact subscription without releasing its generation charge", async () => {
const { store, sent, admission, roomControl, router } = harness(
1,
true,
undefined,
2,
);
let closed = false;
try {
const activeRoom = await store.createRoom();
const active = await establishSfuRoom(
store,
sent,
router,
activeRoom,
);
await vi.waitFor(() =>
expect(admission?.usage()).toEqual({ ingress: 1, egress: 2 }),
);
expect(
admission?.reservePublication({
roomId: "9001",
shareGeneration: "waiter_share_12345678",
publicationGeneration: "waiter_publication_12345678",
}),
).toBe(true);
const reservePublication = vi.spyOn(admission!, "reservePublication");
const waitingRoom = await store.createRoom();
const waitingHost = connectHost(store, waitingRoom);
complete(router, waitingHost);
const waitingViewer = connectViewer(store, waitingRoom, "waiter");
complete(router, waitingViewer);
await vi.waitFor(() =>
expect(preparedFor(sent, waitingViewer.sessionId)).toBeDefined(),
);
const direct = preparedFor(sent, waitingViewer.sessionId)!;
router.handleRouteFailed(waitingViewer, {
type: "route-failed",
revision: direct.revision,
phase: "prepare",
connectionId: direct.candidate.connectionId,
});
await vi.waitFor(() =>
expect(
sent
.get(waitingViewer.sessionId)
?.findLast((message) => message.type === "route-status"),
).toMatchObject({ state: "waiting", reason: "sfu-admission" }),
);
const attemptsBeforeRelease = reservePublication.mock.calls.length;
const drainGate = deferred();
roomControl!.subscriptionDrainBarrier = drainGate.promise;
store.disconnectParticipant(
activeRoom.roomId,
active.first.peerId,
active.first.sessionId,
);
router.disconnectParticipant(
activeRoom.roomId,
active.first.peerId,
active.first.sessionId,
);
router.removeViewer(activeRoom.roomId, active.first.peerId);
await vi.waitFor(() =>
expect(router.routeDiagnosticSnapshot(activeRoom.roomId).children).toHaveLength(1),
);
await vi.waitFor(() =>
expect(roomControl?.subscriptionDrainAttempts).toContainEqual(
expect.objectContaining({ viewerPeerId: active.first.peerId }),
),
);
expect(admission?.usage()).toEqual({ ingress: 2, egress: 2 });
drainGate.resolve();
await vi.waitFor(() =>
expect(roomControl?.drainedSubscriptions).toContainEqual(
expect.objectContaining({ viewerPeerId: active.first.peerId }),
),
);
expect(admission?.usage()).toEqual({ ingress: 2, egress: 2 });
expect(reservePublication).toHaveBeenCalledTimes(attemptsBeforeRelease);
await router.close();
closed = true;
expect(admission?.usage()).toEqual({ ingress: 0, egress: 0 });
} finally {
if (!closed) await router.close();
}
});
it("orders exact prepare, commits only child proof, and rolls back above P", async () => {
const { store, sent, router } = harness(2);
const room = await store.createRoom();
const host = connectHost(store, room);
complete(router, host);
const first = connectViewer(store, room, "first");
complete(router, first);
await vi.waitFor(() => expect(preparedFor(sent, first.sessionId)).toBeDefined());
const firstPrepare = preparedFor(sent, first.sessionId)!;
expect(firstPrepare.candidate).toMatchObject({
childPeerId: first.peerId,
transport: "direct",
});
expect(preparedFor(sent, host.sessionId)?.candidate).toEqual(
firstPrepare.candidate,
);
router.handleRouteReady(host, {
type: "route-ready",
revision: firstPrepare.revision,
phase: "prepare",
});
expect(activeAfter(sent, first.sessionId, firstPrepare.revision)).toBeUndefined();
router.handleRouteReady(first, {
type: "route-ready",
revision: firstPrepare.revision,
phase: "prepare",
});
await vi.waitFor(() =>
expect(
sent
.get(first.sessionId)
?.findLast(
(message) =>
message.type === "route-update" &&
message.phase === "active" &&
message.revision === firstPrepare.revision,
),
).toBeDefined(),
);
const second = connectViewer(store, room, "second");
complete(router, second);
await vi.waitFor(() => expect(preparedFor(sent, second.sessionId)).toBeDefined());
const secondPrepare = preparedFor(sent, second.sessionId)!;
router.handleRouteFailed(second, {
type: "route-failed",
revision: secondPrepare.revision,
phase: "prepare",
connectionId: secondPrepare.candidate.connectionId,
});
await vi.waitFor(() =>
expect(activeAfter(sent, second.sessionId, secondPrepare.revision)).toBeDefined(),
);
expect(activeAfter(sent, second.sessionId, secondPrepare.revision)!.revision).toBeGreaterThan(
secondPrepare.revision,
);
await router.close();
});
it("creates one Host publication and reuses exact Viewer subscriptions", async () => {
const { store, sent, admission, router } = harness(1, true);
const room = await store.createRoom();
const host = connectHost(store, room);
complete(router, host);
const first = connectViewer(store, room, "sfu_first");
complete(router, first);
const firstDirect = await vi.waitFor(() => {
const message = preparedFor(sent, first.sessionId);
expect(message).toBeDefined();
return message!;
});
router.handleRouteReady(first, {
type: "route-ready",
revision: firstDirect.revision,
phase: "prepare",
});
const second = connectViewer(store, room, "sfu_second");
complete(router, second);
await vi.waitFor(() =>
expect(
preparedFor(sent, first.sessionId)?.candidate.transport,
).toBe("sfu"),
);
const firstSfu = preparedFor(sent, first.sessionId)!;
router.handleRouteReady(first, {
type: "route-ready",
revision: firstSfu.revision,
phase: "prepare",
});
await vi.waitFor(() =>
expect(preparedFor(sent, second.sessionId)?.candidate.transport).toBe("sfu"),
);
const secondSfu = preparedFor(sent, second.sessionId)!;
router.handleRouteReady(second, {
type: "route-ready",
revision: secondSfu.revision,
phase: "prepare",
});
await vi.waitFor(() =>
expect(admission?.usage()).toEqual({ ingress: 1, egress: 2 }),
);
const hostConfigs = sent
.get(host.sessionId)
?.filter((message) => message.type === "sfu-config");
expect(hostConfigs).toHaveLength(1);
await router.close();
});
it("discards an SFU preparation whose cursor becomes stale while awaiting LiveKit", async () => {
const { store, sent, admission, roomControl, router } = harness(1, true);
try {
const room = await store.createRoom();
const host = connectHost(store, room);
complete(router, host);
const root = connectViewer(store, room, "prepare-stale-root");
complete(router, root);
const direct = await vi.waitFor(() => {
const prepared = preparedFor(sent, root.sessionId);
expect(prepared?.candidate.transport).toBe("direct");
return prepared!;
});
router.handleRouteReady(root, {
type: "route-ready",
revision: direct.revision,
phase: "prepare",
});
const gate = deferred();
roomControl!.createBarrier = gate.promise;
router.setViewerRelayCapacity(root, 0);
const waiting = connectViewer(store, room, "prepare-stale-waiting");
complete(router, waiting);
await vi.waitFor(() =>
expect(admission?.usage().ingress).toBe(1),
);
store.disconnectParticipant(room.roomId, root.peerId, root.sessionId);
router.disconnectParticipant(room.roomId, root.peerId, root.sessionId);
gate.resolve();
await vi.waitFor(() =>
expect(admission?.usage()).toEqual({ ingress: 0, egress: 0 }),
);
expect(router.routeDiagnosticSnapshot(room.roomId).operation).toBeNull();
} finally {
await router.close();
}
});
it("reschedules the regular Host-offline check after a read error", async () => {
const { store, sent, admission, roomControl, router } = harness(
1,
true,
undefined,
2,
undefined,
10,
);
try {
const room = await store.createRoom();
const { host } = await establishSfuRoom(store, sent, router, room);
const retry = deferredValue<boolean>();
const hostCheck = vi
.spyOn(roomControl!, "hostParticipantExists")
.mockRejectedValueOnce(new Error("temporary LiveKit read failure"))
.mockReturnValueOnce(retry.promise);
store.disconnectParticipant(room.roomId, host.peerId, host.sessionId);
router.disconnectParticipant(room.roomId, host.peerId, host.sessionId);
await vi.waitFor(() => expect(hostCheck).toHaveBeenCalledTimes(2));
expect(admission?.usage()).toEqual({ ingress: 1, egress: 2 });
} finally {
await router.close();
}
});
it("rechecks Host-offline retirement after a live route operation", async () => {
const { store, sent, admission, roomControl, router } = harness(
1,
true,
undefined,
2,
undefined,
10,
);
try {
const room = await store.createRoom();
const { host, first, second } = await establishSfuRoom(
store,
sent,
router,
room,
);
router.setViewerRelayCapacity(second, 1);
const active = router.resolveActiveViewerMediaEdge(
room.roomId,
first.peerId,
)!;
router.handleRouteFailed(first, {
type: "route-failed",
revision: active.revision,
phase: "active",
connectionId: active.connectionId,
});
await vi.waitFor(() => {
const prepared = preparedFor(sent, first.sessionId);
expect(prepared?.revision).toBeGreaterThan(active.revision);
expect(prepared?.candidate.transport).toBe("direct");
return prepared!;
});
const retry = deferredValue<boolean>();
const hostCheck = vi
.spyOn(roomControl!, "hostParticipantExists")
.mockResolvedValueOnce(false)
.mockReturnValueOnce(retry.promise);
store.disconnectParticipant(room.roomId, host.peerId, host.sessionId);
router.disconnectParticipant(room.roomId, host.peerId, host.sessionId);
await vi.waitFor(() => expect(hostCheck).toHaveBeenCalledTimes(2));
expect(
router.resolveActiveViewerMediaEdge(room.roomId, first.peerId),
).toBeUndefined();
retry.resolve(false);
await vi.waitFor(() =>
expect(admission?.usage()).toEqual({ ingress: 0, egress: 0 }),
);
expect(hostCheck).toHaveBeenCalledTimes(2);
} finally {
await router.close();
}
});
it("ignores an old Host-offline result after a new SFU generation commits", async () => {
const { store, sent, admission, roomControl, router } = harness(
1,
true,
undefined,
2,
undefined,
10,
);
try {
const room = await store.createRoom();
const { host, first, second } = await establishSfuRoom(
store,
sent,
router,
room,
);
const oldRevision = Math.max(
router.resolveActiveViewerMediaEdge(room.roomId, first.peerId)!.revision,
router.resolveActiveViewerMediaEdge(room.roomId, second.peerId)!.revision,
);
const delayed = deferredValue<boolean>();
const hostCheck = vi
.spyOn(roomControl!, "hostParticipantExists")
.mockReturnValueOnce(delayed.promise);
store.disconnectParticipant(room.roomId, host.peerId, host.sessionId);
router.disconnectParticipant(room.roomId, host.peerId, host.sessionId);
await vi.waitFor(() => expect(hostCheck).toHaveBeenCalledTimes(1));
const replacementHost = connectHost(
store,
room,
undefined,
"new-physical-host-client",
"new-physical-host-session",
);
complete(router, replacementHost);
const handled = new Set<string>();
for (let index = 0; index < 3; index += 1) {
const next = await vi.waitFor(() => {
const prepared = [first, second]
.map((viewer) => ({
viewer,
prepared: preparedFor(sent, viewer.sessionId),
}))
.find(
({ prepared }) =>
prepared &&
prepared.revision > oldRevision &&
!handled.has(prepared.candidate.connectionId),
);
expect(prepared).toBeDefined();
return prepared!;
});
handled.add(next.prepared!.candidate.connectionId);
router.handleRouteReady(next.viewer, {
type: "route-ready",
revision: next.prepared!.revision,
phase: "prepare",
});
}
await vi.waitFor(() =>
expect(admission?.usage()).toEqual({ ingress: 1, egress: 2 }),
);
const currentFence = roomControl!.created.at(-1)!;
expect(currentFence.publicationGeneration).not.toBe(
roomControl!.created[0]!.publicationGeneration,
);
delayed.resolve(false);
await Promise.resolve();
await Promise.resolve();
expect(admission?.usage()).toEqual({ ingress: 1, egress: 2 });
expect(roomControl!.deleted).not.toContainEqual(currentFence);
} finally {
await router.close();
}
});
it("resolves exact direct and peer-relayed Viewer evidence sources", async () => {
const { store, sent, router } = harness(1);
try {
const room = await store.createRoom();
const host = connectHost(store, room);
complete(router, host);
const root = connectViewer(store, room, "evidence-root");
complete(router, root);
await vi.waitFor(() =>
expect(preparedFor(sent, root.sessionId)).toBeDefined(),
);
const rootPrepare = preparedFor(sent, root.sessionId)!;
expect(
router.resolveActiveViewerMediaEdge(room.roomId, root.peerId),
).toBeUndefined();
router.handleRouteReady(root, {
type: "route-ready",
revision: rootPrepare.revision,
phase: "prepare",
});
await vi.waitFor(() =>
expect(
router.resolveActiveViewerMediaEdge(room.roomId, root.peerId),
).toEqual({
revision: rootPrepare.revision,
connectionId: rootPrepare.candidate.connectionId,
upstream: { kind: "peer", peerId: host.peerId },
}),
);
router.setViewerRelayCapacity(root, 1);
const child = connectViewer(store, room, "evidence-child");
complete(router, child);
await vi.waitFor(() =>
expect(preparedFor(sent, child.sessionId)).toBeDefined(),
);
const childPrepare = preparedFor(sent, child.sessionId)!;
expect(childPrepare.candidate.transport).toBe("direct");
router.handleRouteReady(child, {
type: "route-ready",
revision: childPrepare.revision,
phase: "prepare",
});
await vi.waitFor(() =>
expect(
router.resolveActiveViewerMediaEdge(room.roomId, child.peerId),
).toEqual({
revision: childPrepare.revision,
connectionId: childPrepare.candidate.connectionId,
upstream: { kind: "peer", peerId: root.peerId },
}),
);
} finally {
await router.close();
}
});
it("retries a failed physical drain before reusing the exact SFU identity", async () => {
const { store, sent, admission, roomControl, router } = harness(
1,
true,
1_000,
2,
10,
);
try {
const room = await store.createRoom();
const { first } = await establishSfuRoom(store, sent, router, room);
await vi.waitFor(() =>
expect(
router.resolveActiveViewerMediaEdge(room.roomId, first.peerId)
?.upstream,
).toEqual({ kind: "sfu" }),
);
router.setViewerRelayCapacity(first, 1);
const child = connectViewer(store, room, "sfu-peer-child");
complete(router, child);
await vi.waitFor(() =>
expect(preparedFor(sent, child.sessionId)?.candidate.transport).toBe(
"direct",
),
);
await new Promise((resolve) => setTimeout(resolve, 510));
await vi.waitFor(() =>
expect(preparedFor(sent, child.sessionId)?.candidate.transport).toBe(
"sfu",
),
);
const sfuPrepare = preparedFor(sent, child.sessionId)!;
router.handleRouteReady(child, {
type: "route-ready",
revision: sfuPrepare.revision,
phase: "prepare",
});
await vi.waitFor(() =>
expect(
router.resolveActiveViewerMediaEdge(room.roomId, child.peerId)
?.upstream,
).toEqual({ kind: "sfu" }),
);
await vi.waitFor(() =>
expect(preparedFor(sent, child.sessionId)?.candidate.transport).toBe(
"direct",
),
);
const directPrepare = preparedFor(sent, child.sessionId)!;
const drainGate = deferred();
const drainSubscription = vi
.spyOn(roomControl!, "drainSubscription")
.mockImplementationOnce(() => drainGate.promise);
router.handleRouteReady(child, {
type: "route-ready",
revision: directPrepare.revision,
phase: "prepare",
});
await vi.waitFor(() =>
expect(
router.resolveActiveViewerMediaEdge(room.roomId, child.peerId)
?.upstream,
).toEqual({ kind: "peer", peerId: first.peerId }),
);
await vi.waitFor(() =>
expect(drainSubscription).toHaveBeenCalledWith(
expect.objectContaining({ viewerPeerId: child.peerId }),
),
);
expect(admission?.usage()).toEqual({ ingress: 1, egress: 3 });
router.handleRouteFailed(child, {
type: "route-failed",
revision: directPrepare.revision,
phase: "active",
connectionId: directPrepare.candidate.connectionId,
});
await vi.waitFor(() =>
expect(router.routeDiagnosticSnapshot(room.roomId).operation).not.toBeNull(),
);
expect(preparedFor(sent, child.sessionId)?.revision).toBe(
directPrepare.revision,
);
drainGate.reject(new Error("transient participant drain failure"));
await vi.waitFor(() => expect(drainSubscription).toHaveBeenCalledTimes(2));
const reusedSfu = await vi.waitFor(() => {
const prepared = preparedFor(sent, child.sessionId);
expect(prepared?.revision).toBeGreaterThan(directPrepare.revision);
expect(prepared?.candidate.transport).toBe("sfu");
return prepared!;
});
router.handleRouteReady(child, {
type: "route-ready",
revision: reusedSfu.revision,
phase: "prepare",
});
await vi.waitFor(() =>
expect(
router.resolveActiveViewerMediaEdge(room.roomId, child.peerId)
?.upstream,
).toEqual({ kind: "sfu" }),
);
expect(roomControl!.drainedSubscriptions).toContainEqual(
expect.objectContaining({ viewerPeerId: child.peerId }),
);
expect(admission?.usage()).toEqual({ ingress: 1, egress: 3 });
expect(
router.resolveActiveViewerMediaEdge(room.roomId, first.peerId)
?.upstream,
).toEqual({ kind: "sfu" });
} finally {
await router.close();
}
});
it("serializes fresh SFU configuration without making refresh one-shot", async () => {
const { store, sent, router } = harness(1, true);
try {
const room = await store.createRoom();
const { first } = await establishSfuRoom(store, sent, router, room);
const active = router.resolveActiveViewerMediaEdge(
room.roomId,
first.peerId,
);
expect(active?.upstream).toEqual({ kind: "sfu" });
const configCount = () =>
sent
.get(first.sessionId)
?.filter((message) => message.type === "sfu-config").length ?? 0;
const initialCount = configCount();
router.refreshSfu(first, 0);
await vi.waitFor(() => expect(configCount()).toBe(initialCount + 1));
router.refreshSfu(first, active!.revision);
await vi.waitFor(() => expect(configCount()).toBe(initialCount + 2));
} finally {
await router.close();
}
});
it("fails the exact active SFU route when fresh configuration cannot be issued", async () => {
const { store, sent, router, failNextTokenIssue } = harness(1, true);
try {
const room = await store.createRoom();
const { first } = await establishSfuRoom(store, sent, router, room);
const active = router.resolveActiveViewerMediaEdge(
room.roomId,
first.peerId,
)!;
expect(active.upstream).toEqual({ kind: "sfu" });
failNextTokenIssue();
router.refreshSfu(first, active.revision);
await vi.waitFor(() =>
expect(
router.resolveActiveViewerMediaEdge(room.roomId, first.peerId),
).not.toEqual(active),
);
} finally {
await router.close();
}
});
it("emits typed exhaustion and clears diagnostics on departure and room deletion", async () => {
const { store, sent, router } = harness(1);
const room = await store.createRoom();
const host = connectHost(store, room);
complete(router, host);
const viewer = connectViewer(store, room, "diagnostic");
complete(router, viewer);
await vi.waitFor(() =>
expect(preparedFor(sent, viewer.sessionId)).toBeDefined(),
);
const prepared = preparedFor(sent, viewer.sessionId)!;
expect(router.routeDiagnosticSnapshot(room.roomId).children).toHaveLength(1);
router.handleRouteFailed(viewer, {
type: "route-failed",
revision: prepared.revision,
phase: "prepare",
connectionId: prepared.candidate.connectionId,
});
await vi.waitFor(() =>
expect(
sent
.get(viewer.sessionId)
?.findLast((message) => message.type === "route-status"),
).toMatchObject({
state: "failed",
reason: "route-exhausted",
}),
);
router.removeViewer(room.roomId, viewer.peerId);
expect(router.routeDiagnosticSnapshot(room.roomId)).toEqual({
children: [],
operation: null,
});
const replacement = connectViewer(store, room, "diagnostic-replacement");
complete(router, replacement);
expect(router.routeDiagnosticSnapshot(room.roomId).children).toHaveLength(1);
router.stopRoom(room.roomId);
expect(router.routeDiagnosticSnapshot(room.roomId)).toEqual({
children: [],
operation: null,
});
complete(router, host);
expect(
router.routeDiagnosticSnapshot(room.roomId).children.length,
).toBeGreaterThan(0);
router.deleteRoom(room.roomId);
expect(router.routeDiagnosticSnapshot(room.roomId)).toEqual({
children: [],
operation: null,
});
await router.close();
});
it("preserves connected Viewer capacity across sharing generations", async () => {
const { store, sent, router } = harness(1);
const room = await store.createRoom();
const host = connectHost(store, room);
complete(router, host);
const root = connectViewer(store, room, "retained-capacity-root");
complete(router, root);
await vi.waitFor(() => expect(preparedFor(sent, root.sessionId)).toBeDefined());
const initialRoot = preparedFor(sent, root.sessionId)!;
router.handleRouteReady(root, {
type: "route-ready",
revision: initialRoot.revision,
phase: "prepare",
});
router.setViewerRelayCapacity(root, 1);
router.stopRoom(room.roomId);
sent.set(root.sessionId, []);
complete(router, host);
await vi.waitFor(() => expect(preparedFor(sent, root.sessionId)).toBeDefined());
const restartedRoot = preparedFor(sent, root.sessionId)!;
router.handleRouteReady(root, {
type: "route-ready",
revision: restartedRoot.revision,
phase: "prepare",
});
const child = connectViewer(store, room, "retained-capacity-child");
complete(router, child);
await vi.waitFor(() => expect(preparedFor(sent, child.sessionId)).toBeDefined());
expect(preparedFor(sent, child.sessionId)?.assignment.upstream).toEqual({
kind: "peer",
peerId: root.peerId,
});
await router.close();
});
it("emits typed exhaustion when reconciliation has no candidate", async () => {
const { store, sent, router } = harness(1);
const room = await store.createRoom();
const host = connectHost(store, room);
complete(router, host);
const first = connectViewer(store, room, "only-slot");
complete(router, first);
await vi.waitFor(() =>
expect(preparedFor(sent, first.sessionId)).toBeDefined(),
);
const firstPrepare = preparedFor(sent, first.sessionId)!;
router.handleRouteReady(first, {
type: "route-ready",
revision: firstPrepare.revision,
phase: "prepare",
});
const blocked = connectViewer(store, room, "no-candidate");
complete(router, blocked);
await vi.waitFor(() =>
expect(
sent
.get(blocked.sessionId)
?.findLast((message) => message.type === "route-status"),
).toMatchObject({
state: "failed",
reason: "route-exhausted",
}),
);
await router.close();
});
});