320 lines
10 KiB
JavaScript
320 lines
10 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import { readFile } from "node:fs/promises";
|
|
import { after, before, test } from "node:test";
|
|
|
|
import { createServer } from "vite";
|
|
|
|
let server;
|
|
let claimExclusiveLiveViewer;
|
|
let createRecordedOpenWatchdog;
|
|
let createReentrantViewerDisposer;
|
|
let isLiveRerunPresentationReady;
|
|
let liveTimelineNeedsSynchronization;
|
|
let liveRerunReceiverBindingIdentity;
|
|
let recordedOpenWatchdogTimeoutMs;
|
|
let rerunViewerInitialSource;
|
|
let rerunViewerOpenOptions;
|
|
let resolveRecordedViewerSourceUrl;
|
|
|
|
before(async () => {
|
|
server = await createServer({
|
|
appType: "custom",
|
|
logLevel: "silent",
|
|
server: { middlewareMode: true },
|
|
});
|
|
({
|
|
claimExclusiveLiveViewer,
|
|
createRecordedOpenWatchdog,
|
|
createReentrantViewerDisposer,
|
|
isLiveRerunPresentationReady,
|
|
liveTimelineNeedsSynchronization,
|
|
liveRerunReceiverBindingIdentity,
|
|
recordedOpenWatchdogTimeoutMs,
|
|
rerunViewerInitialSource,
|
|
rerunViewerOpenOptions,
|
|
resolveRecordedViewerSourceUrl,
|
|
} = await server.ssrLoadModule("/src/components/RerunViewport.tsx"));
|
|
});
|
|
|
|
after(async () => {
|
|
await server?.close();
|
|
});
|
|
|
|
const sourceUrl = "/api/v1/observation-sessions/session-atomic/recording.rrd";
|
|
const sha256 = "a".repeat(64);
|
|
const viewerSourceUrl = `${sourceUrl}?generation=${sha256}`;
|
|
|
|
test("only the canonical digest-bound generation URL reaches the native Rerun receiver", () => {
|
|
const descriptor = {
|
|
sourceUrl,
|
|
viewerSourceUrl,
|
|
byteLength: 246_331_680,
|
|
sha256,
|
|
};
|
|
const resolved = resolveRecordedViewerSourceUrl(descriptor, "http://mission-core.test");
|
|
assert.equal(resolved, `http://mission-core.test${viewerSourceUrl}`);
|
|
assert.equal(rerunViewerInitialSource(resolved), resolved);
|
|
|
|
for (const unsafeViewerSourceUrl of [
|
|
sourceUrl,
|
|
`${sourceUrl}?generation=${"b".repeat(64)}`,
|
|
`${viewerSourceUrl}&extra=true`,
|
|
`https://foreign.test${viewerSourceUrl}`,
|
|
]) {
|
|
assert.throws(
|
|
() => resolveRecordedViewerSourceUrl({
|
|
...descriptor,
|
|
viewerSourceUrl: unsafeViewerSourceUrl,
|
|
}, "http://mission-core.test"),
|
|
/Unsafe recorded RRD viewer descriptor/,
|
|
);
|
|
}
|
|
});
|
|
|
|
test("only the live receiver opens on the native following edge", () => {
|
|
assert.deepEqual(rerunViewerOpenOptions(true), { follow_if_http: true });
|
|
assert.equal(rerunViewerOpenOptions(false), null);
|
|
});
|
|
|
|
test("live presentation waits for the exact receiver to expose a usable range", () => {
|
|
assert.equal(isLiveRerunPresentationReady(false, { min: 1, max: 2 }, 1), false);
|
|
assert.equal(isLiveRerunPresentationReady(true, null, 1), false);
|
|
assert.equal(isLiveRerunPresentationReady(true, { min: 2, max: 1 }, 1), false);
|
|
assert.equal(isLiveRerunPresentationReady(true, { min: 1, max: 2 }, 0), false);
|
|
assert.equal(isLiveRerunPresentationReady(true, { min: 1, max: 1 }, 1), true);
|
|
});
|
|
|
|
test("live timeline is retried only after stream_time exists and until it is active", () => {
|
|
assert.equal(liveTimelineNeedsSynchronization(false, undefined, { min: 1, max: 2 }), false);
|
|
assert.equal(liveTimelineNeedsSynchronization(true, undefined, null), false);
|
|
assert.equal(liveTimelineNeedsSynchronization(true, undefined, { min: 1, max: 2 }), true);
|
|
assert.equal(liveTimelineNeedsSynchronization(true, "log_time", { min: 1, max: 2 }), true);
|
|
assert.equal(liveTimelineNeedsSynchronization(true, "stream_time", { min: 1, max: 2 }), false);
|
|
});
|
|
|
|
test("live receiver binding stays stable across recovery authority projections", () => {
|
|
const sourceUrl = "rerun+http://127.0.0.1:9877/proxy";
|
|
const streamId = "acq-001";
|
|
assert.equal(
|
|
liveRerunReceiverBindingIdentity(sourceUrl, streamId, true),
|
|
liveRerunReceiverBindingIdentity(` ${sourceUrl} `, streamId, true),
|
|
);
|
|
assert.notEqual(
|
|
liveRerunReceiverBindingIdentity(sourceUrl, streamId, true),
|
|
liveRerunReceiverBindingIdentity(sourceUrl, "acq-002", true),
|
|
);
|
|
assert.notEqual(
|
|
liveRerunReceiverBindingIdentity(sourceUrl, streamId, true),
|
|
liveRerunReceiverBindingIdentity(sourceUrl, streamId, false),
|
|
);
|
|
});
|
|
|
|
test("recorded admission watchdog is size-aware and exits an incomplete load", () => {
|
|
const smallDelay = recordedOpenWatchdogTimeoutMs(4);
|
|
const currentDelay = recordedOpenWatchdogTimeoutMs(246_331_680);
|
|
const largeDelay = recordedOpenWatchdogTimeoutMs(805_687_122);
|
|
assert.ok(smallDelay >= 120_000);
|
|
assert.ok(currentDelay >= smallDelay);
|
|
assert.ok(largeDelay > currentDelay);
|
|
assert.ok(largeDelay <= 1_800_000);
|
|
assert.ok(largeDelay > 12_000);
|
|
|
|
let scheduledCallback;
|
|
let scheduledDelay;
|
|
let timeoutCount = 0;
|
|
const cancelled = [];
|
|
const watchdog = createRecordedOpenWatchdog({
|
|
byteLength: 246_331_680,
|
|
schedule(callback, timeoutMs) {
|
|
scheduledCallback = callback;
|
|
scheduledDelay = timeoutMs;
|
|
return 17;
|
|
},
|
|
cancel(handle) {
|
|
cancelled.push(handle);
|
|
},
|
|
onTimeout() {
|
|
timeoutCount += 1;
|
|
},
|
|
});
|
|
watchdog.arm();
|
|
assert.equal(watchdog.pending(), true);
|
|
assert.equal(scheduledDelay, currentDelay);
|
|
scheduledCallback();
|
|
assert.equal(timeoutCount, 1);
|
|
assert.equal(watchdog.pending(), false);
|
|
assert.deepEqual(cancelled, []);
|
|
});
|
|
|
|
test("complete recorded admission clears its watchdog", () => {
|
|
let timeoutCount = 0;
|
|
const cancelled = [];
|
|
const watchdog = createRecordedOpenWatchdog({
|
|
byteLength: 805_687_122,
|
|
schedule() {
|
|
return 23;
|
|
},
|
|
cancel(handle) {
|
|
cancelled.push(handle);
|
|
},
|
|
onTimeout() {
|
|
timeoutCount += 1;
|
|
},
|
|
});
|
|
watchdog.arm();
|
|
watchdog.clear();
|
|
assert.equal(watchdog.pending(), false);
|
|
assert.equal(timeoutCount, 0);
|
|
assert.deepEqual(cancelled, [23]);
|
|
});
|
|
|
|
test("deferred viewer start cannot reopen after stale unmount", async () => {
|
|
let resolveStart;
|
|
const start = new Promise((resolve) => {
|
|
resolveStart = resolve;
|
|
});
|
|
let disposed = false;
|
|
let cleanupCount = 0;
|
|
let closeCount = 0;
|
|
let stopCount = 0;
|
|
const diagnostics = [];
|
|
const disposeViewer = createReentrantViewerDisposer(
|
|
() => {
|
|
cleanupCount += 1;
|
|
},
|
|
() => {
|
|
closeCount += 1;
|
|
stopCount += 1;
|
|
},
|
|
);
|
|
const pendingMount = (async () => {
|
|
await start;
|
|
if (disposed) {
|
|
disposeViewer();
|
|
return;
|
|
}
|
|
diagnostics.push("admitted");
|
|
})();
|
|
|
|
disposed = true;
|
|
disposeViewer();
|
|
resolveStart();
|
|
await pendingMount;
|
|
|
|
assert.equal(cleanupCount, 1);
|
|
assert.equal(closeCount, 2);
|
|
assert.equal(stopCount, 2);
|
|
assert.deepEqual(diagnostics, []);
|
|
});
|
|
|
|
test("recorded RRD bytes are never split across LogChannel.send_rrd calls", async () => {
|
|
const source = await readFile(
|
|
new URL("../src/components/RerunViewport.tsx", import.meta.url),
|
|
"utf8",
|
|
);
|
|
assert.doesNotMatch(source, /streamVerifiedRecordedRrd/);
|
|
assert.doesNotMatch(source, /missioncore\/recorded-recording/);
|
|
assert.doesNotMatch(source, /recordedChannel/);
|
|
assert.match(source, /viewer\.start\(\s*rerunViewerInitialSource\(resolvedSource\)/s);
|
|
assert.match(source, /rerunViewerOpenOptions\(followLive\)/);
|
|
assert.match(
|
|
source,
|
|
/recordingOpened = true;[\s\S]*diagnosticLifecycle\.markAdmitted\(\);/,
|
|
);
|
|
assert.match(
|
|
source,
|
|
/viewer\.get_active_recording_id\(\)[\s\S]*admitRecording\(\{[\s\S]*application_id: "nodedc_mission_core_spatial"/,
|
|
);
|
|
assert.match(
|
|
source,
|
|
/const readyToRender = followLive\s*\? liveTimelineSynchronized && isLiveRerunPresentationReady\(/,
|
|
);
|
|
assert.match(
|
|
source,
|
|
/viewer\.get_active_timeline\(event\.recording_id\)[\s\S]*viewer\.set_active_timeline\(event\.recording_id, timeline\)/,
|
|
);
|
|
assert.match(
|
|
source,
|
|
/if \(readyToRender && !readyPublished\)[\s\S]*eventCode: "live_receiver_active_store_admitted"[\s\S]*diagnosticLifecycle\.markAdmitted\(\)/,
|
|
);
|
|
assert.doesNotMatch(
|
|
source,
|
|
/\], \[followLive, liveRecoveryAuthorityIdentity, liveStreamId, sourceUrl\]\);/,
|
|
);
|
|
assert.match(
|
|
source,
|
|
/viewerStartResolved = true;\s*if \(isRecordedSource && !recordedSceneAdmitted\) recordedOpenWatchdog\?\.arm\(\);/,
|
|
);
|
|
assert.match(
|
|
source,
|
|
/if \(readyToRender && !readyPublished\)[\s\S]*clearRecordedAdmissionWatchdog\(\);/,
|
|
);
|
|
});
|
|
|
|
test("one live document owns one native Rerun receiver", async () => {
|
|
const releases = [];
|
|
const releaseFirstClaim = claimExclusiveLiveViewer(() => releases.push("first"));
|
|
const releaseSecondClaim = claimExclusiveLiveViewer(() => releases.push("second"));
|
|
|
|
assert.deepEqual(releases, ["first"]);
|
|
releaseFirstClaim();
|
|
assert.deepEqual(releases, ["first"]);
|
|
releaseSecondClaim();
|
|
|
|
const releaseThirdClaim = claimExclusiveLiveViewer(() => releases.push("third"));
|
|
assert.deepEqual(releases, ["first"]);
|
|
releaseThirdClaim();
|
|
});
|
|
|
|
test("raw replay exercises the same streaming receiver lifecycle as a live scan", async () => {
|
|
const source = await readFile(
|
|
new URL("../src/workspaces/Workspaces.tsx", import.meta.url),
|
|
"utf8",
|
|
);
|
|
assert.match(
|
|
source,
|
|
/const liveRerunSource = !recordedSource && \/\^rerun\\\+https\?:\\\/\\\//,
|
|
);
|
|
assert.match(
|
|
source,
|
|
/const livePresentationActivitySequence = metrics\?\.publishedFrameCount \?\?[\s\S]*liveRerunSource && !streamActive \? 1 : null/,
|
|
);
|
|
assert.match(source, /followLive=\{liveRerunSource\}/);
|
|
assert.match(source, /liveActivitySequence=\{livePresentationActivitySequence\}/);
|
|
assert.match(
|
|
source,
|
|
/sourceUrl\.trim\(\) && pointCloudVisible && !intentionalSourceEnd/,
|
|
);
|
|
assert.match(
|
|
source,
|
|
/const recordedSource = Boolean\(recordedReplay\) \|\| \/\\\.rrd/,
|
|
);
|
|
assert.doesNotMatch(
|
|
source,
|
|
/recordedSource = state\?\.sourceMode === ["']replay["']/,
|
|
);
|
|
});
|
|
|
|
test("the spatial header reports raw replay as an active source", async () => {
|
|
const source = await readFile(
|
|
new URL("../src/App.tsx", import.meta.url),
|
|
"utf8",
|
|
);
|
|
assert.match(
|
|
source,
|
|
/sourceMode === "replay"[\s\S]*\? "Повтор записи"[\s\S]*: "Ожидание эфира"/,
|
|
);
|
|
});
|
|
|
|
test("the complete vendor canvas host is hidden during partial and failed admission", async () => {
|
|
const css = await readFile(new URL("../src/styles/spatial.css", import.meta.url), "utf8");
|
|
assert.match(
|
|
css,
|
|
/\.rerun-viewport:is\(\[data-status="loading"\], \[data-status="error"\]\)\s+\.rerun-viewport__canvas\s*\{[^}]*visibility:\s*hidden;[^}]*pointer-events:\s*none;/s,
|
|
);
|
|
assert.doesNotMatch(
|
|
css,
|
|
/data-status="loading"[^}]*\.rerun-viewport__canvas\s*>\s*div/s,
|
|
);
|
|
});
|