Files
NODEDC_MISSION_CORE/apps/control-station/test/observationSources.test.mjs
T

2084 lines
71 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import { after, before, test } from "node:test";
import { createElement } from "react";
import { renderToStaticMarkup } from "react-dom/server";
import { createServer } from "vite";
let server;
let xgridsK1Manifest;
let xgridsK1ObservationSources;
let openObservationSource;
let shouldRestartObservationSource;
let consumeCameraLeaseRetry;
let resetCameraLeaseRetryBudget;
let cameraTransportRecoveryIsCurrent;
let cameraTransportCanOpen;
let cameraPendingQueueCanAccept;
let cameraTransportCloseRecoveryMessage;
let cameraPlaybackRecoveryFailureStage;
let createCameraStartupWatchdog;
let cameraStartupWatchdogRecoveryMessage;
let CAMERA_FIRST_MEDIA_TIMEOUT_MS;
let CAMERA_FIRST_PLAYABLE_FRAME_TIMEOUT_MS;
let liveCameraPlaybackAuthorityIdentity;
let initialCameraPlaybackRecoveryState;
let reduceCameraPlaybackRecovery;
let cameraTransportCallbackIsCurrent;
let cameraBrowserTransportIdentity;
let liveRerunRecoveryAuthorityIdentity;
let initialObservationWindowRect;
let FloatingObservationWindow;
let ObservationTimeline;
let shouldCaptureWorkspacePointer;
let normalizeTimelineRange;
let timelineOffsetSeconds;
let formatTimelineDuration;
let normalizeAccumulationSeconds;
let formatAccumulationDuration;
let resolveRerunSourceUrl;
let resolveRecordedBlueprintUrl;
let fetchRecordedBlueprintRrd;
let recordedCameraJournalContract;
let resolveRecordedPerceptionUrl;
let fetchRecordedPerceptionRrd;
let resolveRecordedPerceptionViewerSourceUrl;
let probeRecordedPerceptionViewerSource;
let resolveRecordedPointColorsUrl;
let fetchRecordedPointColorsRrd;
let recordedPointColorKey;
let isRecordedPlaybackFullyBuffered;
let recordedObservationSources;
let selectRecordedMediaEpoch;
let recordedMediaLocalTime;
before(async () => {
server = await createServer({
appType: "custom",
logLevel: "silent",
server: { middlewareMode: true },
});
({ xgridsK1Manifest } = await server.ssrLoadModule(
"@xgrids-k1/frontend/manifest.ts",
));
({ xgridsK1ObservationSources } = await server.ssrLoadModule(
"@xgrids-k1/frontend/observationSources.ts",
));
({ openObservationSource, shouldRestartObservationSource } = await server.ssrLoadModule(
"/src/core/observation/layoutPolicy.ts",
));
({
consumeCameraLeaseRetry,
resetCameraLeaseRetryBudget,
cameraTransportRecoveryIsCurrent,
cameraTransportCanOpen,
cameraPendingQueueCanAccept,
cameraTransportCloseRecoveryMessage,
cameraPlaybackRecoveryFailureStage,
createCameraStartupWatchdog,
cameraStartupWatchdogRecoveryMessage,
CAMERA_FIRST_MEDIA_TIMEOUT_MS,
CAMERA_FIRST_PLAYABLE_FRAME_TIMEOUT_MS,
} = await server.ssrLoadModule("/src/components/MseFmp4WebSocketPlayer.tsx"));
({
liveCameraPlaybackAuthorityIdentity,
initialCameraPlaybackRecoveryState,
reduceCameraPlaybackRecovery,
cameraTransportCallbackIsCurrent,
cameraBrowserTransportIdentity,
} = await server.ssrLoadModule(
"/src/core/observation/liveCameraRecovery.ts",
));
({
FloatingObservationWindow,
initialObservationWindowRect,
shouldCaptureWorkspacePointer,
} = await server.ssrLoadModule("/src/components/FloatingObservationWindow.tsx"));
({ liveRerunRecoveryAuthorityIdentity } = await server.ssrLoadModule(
"/src/core/observation/liveReceiverWatchdog.ts",
));
({
ObservationTimeline,
normalizeTimelineRange,
timelineOffsetSeconds,
formatTimelineDuration,
normalizeAccumulationSeconds,
formatAccumulationDuration,
} =
await server.ssrLoadModule("/src/components/ObservationTimeline.tsx"));
({
resolveRerunSourceUrl,
resolveRecordedBlueprintUrl,
fetchRecordedBlueprintRrd,
recordedCameraJournalContract,
resolveRecordedPerceptionUrl,
fetchRecordedPerceptionRrd,
resolveRecordedPerceptionViewerSourceUrl,
probeRecordedPerceptionViewerSource,
resolveRecordedPointColorsUrl,
fetchRecordedPointColorsRrd,
recordedPointColorKey,
isRecordedPlaybackFullyBuffered,
} = await server.ssrLoadModule(
"/src/components/RerunViewport.tsx",
));
({ recordedObservationSources } = await server.ssrLoadModule(
"/src/core/observation/recordedObservationSources.ts",
));
({ selectRecordedMediaEpoch, recordedMediaLocalTime } = await server.ssrLoadModule(
"/src/components/RecordedFmp4Player.tsx",
));
});
test("follow toggles retain the current recorded camera journal", () => {
const before = recordedCameraJournalContract({
activeView: "spatial", viewResetGeneration: 0, planView: false, followTrajectory: false,
});
const afterFollowToggle = recordedCameraJournalContract({
activeView: "spatial", viewResetGeneration: 0, planView: false, followTrajectory: true,
});
const afterPlanToggle = recordedCameraJournalContract({
activeView: "spatial", viewResetGeneration: 0, planView: true, followTrajectory: true,
});
const afterReset = recordedCameraJournalContract({
activeView: "spatial", viewResetGeneration: 1, planView: false, followTrajectory: false,
});
assert.equal(afterFollowToggle, before);
assert.notEqual(afterPlanToggle, before);
assert.notEqual(afterReset, before);
});
test("observation camera windows tile from the bottom-right above the live timeline", () => {
const bounds = { width: 1280, height: 720 };
const left = initialObservationWindowRect(0, 2, bounds);
const right = initialObservationWindowRect(1, 2, bounds);
assert.equal(left.y, right.y);
assert.ok(left.x + left.width < right.x);
assert.equal(right.x + right.width, bounds.width - 18);
assert.ok(right.y + right.height <= bounds.height - 64);
});
test("live camera window keeps its player mounted before viewport measurement", () => {
const source = {
id: "k1:sensor.camera.right",
sourceId: "sensor.camera.right",
label: "Правая камера",
description: "Live camera",
modality: "video",
availability: "streaming",
transport: "websocket",
endpointLabel: "Локальный видеопоток",
previewUrl: null,
delivery: {
id: "camera-preview-4",
kind: "mse-fmp4-websocket",
url: "/camera-preview/4",
mediaType: 'video/mp4; codecs="avc1.641028"',
},
activation: {
groupId: "k1:device-session:camera.preview.decoder",
maxActive: 1,
selected: true,
controllable: false,
},
binding: {
deviceId: "k1-a",
deviceSessionId: "device-session",
acquisitionId: "acquisition-quick",
},
capabilities: {
defaultVisible: true,
overlay: true,
resizable: true,
timelineMode: "live-only",
},
};
const noop = () => undefined;
const markup = renderToStaticMarkup(createElement(FloatingObservationWindow, {
source,
index: 0,
count: 1,
boundsRef: { current: null },
maximized: false,
active: true,
onRectChange: noop,
onMaximizedChange: noop,
onActivate: noop,
onClose: noop,
}));
assert.match(markup, /<video/);
assert.match(markup, /Правая камера/);
});
test("observation camera windows stack without overlap when the viewport is narrow", () => {
const bounds = { width: 420, height: 700 };
const lower = initialObservationWindowRect(0, 2, bounds);
const upper = initialObservationWindowRect(1, 2, bounds);
assert.equal(lower.x, upper.x);
assert.ok(upper.y + upper.height < lower.y);
for (const rect of [lower, upper]) {
assert.ok(rect.x >= 0 && rect.y >= 0);
assert.ok(rect.x + rect.width <= bounds.width);
assert.ok(rect.y + rect.height <= bounds.height - 64);
}
});
test("observation camera tiling remains in bounds on a constrained viewport", () => {
const bounds = { width: 260, height: 280 };
const rects = Array.from({ length: 2 }, (_, index) => (
initialObservationWindowRect(index, 2, bounds)
));
for (const rect of rects) {
assert.ok(rect.width > 0 && rect.height > 0);
assert.ok(rect.x >= 0 && rect.y >= 0);
assert.ok(rect.x + rect.width <= bounds.width);
assert.ok(rect.y + rect.height <= bounds.height - 64);
}
assert.ok(rects[1].y + rects[1].height <= rects[0].y);
});
test("floating observation interaction captures resize and movable header pointers", () => {
const target = (matches) => ({ closest: (selector) => matches.includes(selector) });
assert.equal(
shouldCaptureWorkspacePointer(0, target([".nodedc-workspace-window__resize"])),
true,
);
assert.equal(
shouldCaptureWorkspacePointer(0, target([".nodedc-workspace-window__head"])),
true,
);
assert.equal(
shouldCaptureWorkspacePointer(
0,
target([".nodedc-workspace-window__head", "button, input, select, textarea, a"]),
),
false,
);
assert.equal(
shouldCaptureWorkspacePointer(2, target([".nodedc-workspace-window__resize"])),
false,
);
});
test("live camera registers sourceopen before assigning the MSE object URL", async () => {
const source = await readFile(
new URL("../src/components/MseFmp4WebSocketPlayer.tsx", import.meta.url),
"utf8",
);
const listenerIndex = source.indexOf(
'mediaSource.addEventListener("sourceopen", onSourceOpen, { once: true });',
);
const sourceAssignmentIndex = source.indexOf("video.src = objectUrl;");
assert.ok(listenerIndex >= 0, "sourceopen listener must remain explicit");
assert.ok(sourceAssignmentIndex >= 0, "MediaSource object URL assignment must remain explicit");
assert.ok(
listenerIndex < sourceAssignmentIndex,
"sourceopen listener must exist before video.src can open the MediaSource",
);
});
test("live camera reports each browser startup boundary without device actions", async () => {
const source = await readFile(
new URL("../src/components/MseFmp4WebSocketPlayer.tsx", import.meta.url),
"utf8",
);
for (const eventCode of [
"live_camera_player_effect_started",
"live_camera_playback_authority_missing",
"live_camera_media_source_open",
"live_camera_websocket_open",
]) {
assert.ok(
source.includes(`eventCode: "${eventCode}"`),
`missing bounded player diagnostic ${eventCode}`,
);
}
});
test("recorded observation timeline clamps relative seek time without epoch precision in the UI", () => {
const range = normalizeTimelineRange({
min: 0,
max: 12_500_000_000,
});
assert.ok(range);
assert.equal(timelineOffsetSeconds(range, range.min + 2_250_000_000), 2.25);
assert.equal(timelineOffsetSeconds(range, range.min - 1), 0);
assert.equal(timelineOffsetSeconds(range, range.max + 1), 12.5);
assert.equal(formatTimelineDuration(62.125), "01:02.125");
});
test("recorded observation timeline rejects empty and non-finite ranges", () => {
assert.equal(normalizeTimelineRange(null), null);
assert.equal(normalizeTimelineRange({ min: 10, max: 10 }), null);
assert.equal(normalizeTimelineRange({ min: Number.NaN, max: 10 }), null);
});
test("accumulation control normalizes UI values and distinguishes a single frame", () => {
assert.equal(normalizeAccumulationSeconds(-3), 0);
assert.equal(normalizeAccumulationSeconds(12.6), 13);
assert.equal(normalizeAccumulationSeconds(999), 120);
assert.equal(normalizeAccumulationSeconds(Number.NaN), 0);
assert.equal(formatAccumulationDuration(0), "Кадр");
assert.equal(formatAccumulationDuration(12), "12 с");
});
test("spatial timeline renders synchronized accumulation and playback controls", () => {
const markup = renderToStaticMarkup(createElement(ObservationTimeline, {
active: true,
sourceCount: 2,
mode: "recorded",
seekable: true,
rangeNs: { min: 0, max: 20_000_000_000 },
currentNs: 5_000_000_000,
accumulationSeconds: 12,
onAccumulationChange: () => undefined,
onAccumulationCommit: () => undefined,
onSeek: () => undefined,
}));
assert.match(markup, /data-accumulation="true"/);
assert.match(markup, /Накопление/);
assert.match(markup, /aria-label="Окно накопления облака точек"/);
assert.match(markup, /aria-valuetext="12 с"/);
assert.match(markup, /aria-label="Позиция воспроизведения"/);
assert.equal((markup.match(/type="range"/g) ?? []).length, 2);
});
test("recorded observation timeline uses compact icon transport and one inline rate trigger", () => {
const idleMarkup = renderToStaticMarkup(createElement(ObservationTimeline, {
active: true,
sourceCount: 2,
mode: "recorded",
seekable: true,
rangeNs: { min: 0, max: 20_000_000_000 },
currentNs: 5_000_000_000,
playing: false,
playbackRate: 1,
onSeek: () => undefined,
onPlayingChange: () => undefined,
onPlaybackRateChange: () => undefined,
}));
const playingMarkup = renderToStaticMarkup(createElement(ObservationTimeline, {
active: true,
sourceCount: 2,
mode: "recorded",
seekable: true,
rangeNs: { min: 0, max: 20_000_000_000 },
currentNs: 5_000_000_000,
playing: true,
playbackRate: 1,
onSeek: () => undefined,
onPlayingChange: () => undefined,
onPlaybackRateChange: () => undefined,
}));
assert.match(idleMarkup, /aria-label="Воспроизвести"/);
assert.match(idleMarkup, /lucide-play/);
assert.match(idleMarkup, /fill="currentColor"/);
assert.match(idleMarkup, /stroke-width="0"/);
assert.match(playingMarkup, /aria-label="Пауза"/);
assert.match(playingMarkup, /lucide-square/);
assert.match(playingMarkup, /fill="currentColor"/);
assert.match(idleMarkup, /nodedc-select-inline/);
assert.match(idleMarkup, />1×<\/span>/);
assert.doesNotMatch(idleMarkup, /nodedc-select-trigger__chevron/);
assert.doesNotMatch(idleMarkup, />Воспроизвести<|>Пауза</);
});
test("Rerun expands only root-relative session recordings onto the current origin", () => {
assert.equal(
resolveRerunSourceUrl(
" /api/v1/observation-sessions/session-1/recording.rrd ",
"http://127.0.0.1:5174",
),
"http://127.0.0.1:5174/api/v1/observation-sessions/session-1/recording.rrd",
);
assert.equal(
resolveRerunSourceUrl("rerun+http://127.0.0.1:9877/proxy", "http://127.0.0.1:5174"),
"rerun+http://127.0.0.1:9877/proxy",
);
assert.equal(
resolveRerunSourceUrl("//different-authority.invalid/session.rrd", "http://127.0.0.1:5174"),
"//different-authority.invalid/session.rrd",
);
});
test("recorded blueprint endpoint is derived only from canonical same-origin RRD sources", () => {
assert.equal(
resolveRecordedBlueprintUrl(
"/api/v1/observation-sessions/session-1/recording.rrd",
"http://127.0.0.1:5174",
),
"http://127.0.0.1:5174/api/v1/observation-sessions/session-1/blueprint.rrd",
);
assert.equal(
resolveRecordedBlueprintUrl(
"https://outside.invalid/api/v1/observation-sessions/session-1/recording.rrd",
"http://127.0.0.1:5174",
),
null,
);
assert.equal(
resolveRecordedBlueprintUrl(
"/api/v1/observation-sessions/../recording.rrd",
"http://127.0.0.1:5174",
),
null,
);
assert.equal(
resolveRecordedBlueprintUrl(
`/api/v1/laboratory/vegetation-shadow/lab-v1-vegetation-shadow-${"a".repeat(64)}`
+ "/canonical-replay.rrd",
"http://127.0.0.1:5174",
"/api/v1/observation-sessions/session-1/blueprint.rrd",
),
"http://127.0.0.1:5174/api/v1/observation-sessions/session-1/blueprint.rrd",
);
});
test("recorded replay becomes ready only after the complete declared timeline is buffered", () => {
assert.equal(isRecordedPlaybackFullyBuffered(null, 20), false);
assert.equal(
isRecordedPlaybackFullyBuffered({ min: 0, max: 19_500_000_000 }, 20),
false,
);
assert.equal(
isRecordedPlaybackFullyBuffered({ min: 0, max: 19_999_500_000 }, 20),
true,
);
assert.equal(
isRecordedPlaybackFullyBuffered({ min: 0, max: 1 }, undefined),
true,
);
assert.equal(
isRecordedPlaybackFullyBuffered({ min: 0, max: 20_000_000_000 }, Number.NaN),
false,
);
});
test("recorded blueprint fetch is bounded, strict and sends only display settings", async () => {
const calls = [];
const cameraLimits = [];
const payload = Uint8Array.from([0x52, 0x52, 0x46, 0x32, 0x01]);
const result = await fetchRecordedBlueprintRrd(
"http://127.0.0.1:5174/api/v1/observation-sessions/session-1/blueprint.rrd",
{
accumulationSeconds: 24,
showGrid: false,
showPoints: true,
showTrajectory: false,
pointSize: 4.5,
colorMode: "height",
palette: "custom",
customColor: "#35d7c1",
},
{ applicationId: "nodedc_mission_core_recorded", recordingId: "recording-001" },
{
origin: "http://127.0.0.1:5174",
blueprintSessionId: "a".repeat(32),
activeView: "perception3d",
viewResetGeneration: 1,
followTrajectory: true,
cameraEye: {
position: [3, 4, 5],
lookTarget: [1, 2, 0],
eyeUp: [0, 0, 1],
},
eyeRelativeToTracking: true,
currentTimeNs: 39_215_263_458,
onCameraMaxOrbitalRadius: value => cameraLimits.push(value),
unifiedCameraShare: 0.73,
perceptionLayers: {
enabled: true,
detections2d: true,
segmentation: false,
cuboids3d: true,
},
fetcher: async (input, init) => {
calls.push({ input: String(input), init, body: JSON.parse(String(init.body)) });
return new Response(payload, {
status: 200,
headers: {
"Content-Type": "application/vnd.rerun.rrd",
"X-MissionCore-Camera-Max-Orbital-Radius": "686.024231",
},
});
},
},
);
assert.deepEqual([...result], [...payload]);
assert.equal(calls[0].init.method, "POST");
assert.equal(calls[0].init.credentials, "same-origin");
assert.deepEqual(calls[0].body, {
application_id: "nodedc_mission_core_recorded",
recording_id: "recording-001",
blueprint_session_id: "a".repeat(32),
accumulation_seconds: 24,
show_grid: false,
show_points: true,
show_trajectory: false,
point_size: 4.5,
color_mode: "height",
palette: "custom",
custom_color: "#35d7c1",
active_view: "perception3d",
view_reset_generation: 1,
follow_trajectory: true,
current_time_ns: 39_215_263_458,
unified_perception: true,
unified_camera_share: 0.73,
semantic_layer: null,
plan_view: false,
eye_position: [3, 4, 5],
eye_look_target: [1, 2, 0],
eye_up: [0, 0, 1],
eye_relative_to_tracking: true,
show_camera_image: true,
show_detections_2d: true,
show_segmentation: false,
show_cuboids_3d: true,
});
assert.deepEqual(cameraLimits, [686.024231]);
await fetchRecordedBlueprintRrd(
"http://127.0.0.1:5174/api/v1/observation-sessions/session-1/blueprint.rrd",
{
accumulationSeconds: 0,
showGrid: true,
showPoints: true,
showTrajectory: true,
pointSize: 2.5,
colorMode: "intensity",
palette: "turbo",
customColor: "#ffffff",
},
{ applicationId: "nodedc_mission_core_recorded", recordingId: "recording-001" },
{
origin: "http://127.0.0.1:5174",
blueprintSessionId: "b".repeat(32),
perceptionLayers: {
enabled: true,
detections2d: false,
segmentation: false,
cuboids3d: true,
},
fetcher: async (input, init) => {
calls.push({ input: String(input), init, body: JSON.parse(String(init.body)) });
return new Response(payload, {
status: 200,
headers: { "Content-Type": "application/vnd.rerun.rrd" },
});
},
},
);
assert.equal(calls[1].body.unified_perception, false);
assert.equal(calls[1].body.unified_camera_share, 0.46);
assert.equal(calls[1].body.show_cuboids_3d, true);
await assert.rejects(
fetchRecordedBlueprintRrd(
"https://outside.invalid/api/v1/observation-sessions/session-1/blueprint.rrd",
{
accumulationSeconds: 24,
showGrid: false,
showPoints: true,
showTrajectory: false,
pointSize: 4.5,
colorMode: "height",
palette: "custom",
customColor: "#35d7c1",
},
{ applicationId: "nodedc_mission_core_recorded", recordingId: "recording-001" },
{
origin: "http://127.0.0.1:5174",
blueprintSessionId: "a".repeat(32),
fetcher: async () => new Response(payload),
},
),
/Unsafe recorded blueprint request/,
);
});
test("recorded point colors use one strict same-origin component overlay", async () => {
const endpoint = resolveRecordedPointColorsUrl(
"/api/v1/observation-sessions/session-1/recording.rrd",
"http://127.0.0.1:5174",
);
assert.equal(
endpoint,
"http://127.0.0.1:5174/api/v1/observation-sessions/session-1/point-colors.rrd",
);
assert.equal(
resolveRecordedPointColorsUrl(
"https://outside.invalid/api/v1/observation-sessions/session-1/recording.rrd",
"http://127.0.0.1:5174",
),
null,
);
assert.equal(
recordedPointColorKey({
colorMode: "intensity",
palette: "turbo",
customColor: "#112233",
}),
"intensity|turbo|-",
);
assert.equal(
recordedPointColorKey({
colorMode: "class",
palette: "turbo",
customColor: "#112233",
}),
"class|turbo|#112233",
);
const calls = [];
const payload = Uint8Array.from([0x52, 0x52, 0x46, 0x32, 0x01]);
const result = await fetchRecordedPointColorsRrd(
endpoint,
{
colorMode: "distance",
palette: "viridis",
customColor: "#35d7c1",
},
{ applicationId: "nodedc_mission_core_recorded", recordingId: "recording-001" },
{
origin: "http://127.0.0.1:5174",
fetcher: async (input, init) => {
calls.push({ input: String(input), init, body: JSON.parse(String(init.body)) });
return new Response(payload, {
status: 200,
headers: {
"Content-Type": "application/vnd.rerun.rrd",
"Content-Length": String(payload.byteLength),
},
});
},
},
);
assert.deepEqual([...result], [...payload]);
assert.deepEqual(calls[0].body, {
application_id: "nodedc_mission_core_recorded",
recording_id: "recording-001",
color_mode: "distance",
palette: "viridis",
custom_color: "#35d7c1",
});
});
test("recorded perception fetch admits one complete same-origin RRD or no layer", async () => {
const endpoint = resolveRecordedPerceptionUrl(
"/api/v1/observation-sessions/session-1/recording.rrd",
"http://127.0.0.1:5174",
);
assert.equal(
endpoint,
"http://127.0.0.1:5174/api/v1/observation-sessions/session-1/perception.rrd",
);
const payload = Uint8Array.from([0x52, 0x52, 0x46, 0x32, 0x01]);
const progress = [];
const result = await fetchRecordedPerceptionRrd(
endpoint,
{ applicationId: "nodedc_mission_core_recorded", recordingId: "recording-001" },
{
origin: "http://127.0.0.1:5174",
onProgress: (receivedBytes, totalBytes) => {
progress.push([receivedBytes, totalBytes]);
},
fetcher: async (_input, init) => {
assert.deepEqual(JSON.parse(String(init.body)), {
application_id: "nodedc_mission_core_recorded",
recording_id: "recording-001",
});
return new Response(payload, {
status: 200,
headers: {
"Content-Type": "application/vnd.rerun.rrd",
"Content-Length": String(payload.byteLength),
},
});
},
},
);
assert.deepEqual([...result], [...payload]);
assert.deepEqual(progress[0], [0, payload.byteLength]);
assert.deepEqual(progress.at(-1), [payload.byteLength, payload.byteLength]);
const absent = await fetchRecordedPerceptionRrd(
endpoint,
{ applicationId: "nodedc_mission_core_recorded", recordingId: "recording-001" },
{
origin: "http://127.0.0.1:5174",
fetcher: async () => new Response(null, { status: 204 }),
},
);
assert.equal(absent, null);
});
test("LAB perception sidecar is streamed by native Rerun from one generation-bound URL", async () => {
const endpoint =
`http://127.0.0.1:5174/api/v1/laboratory/vegetation-shadow/` +
`lab-v1-vegetation-shadow-${"a".repeat(64)}/canonical-overlay.rrd`;
const sourceUrl = resolveRecordedPerceptionViewerSourceUrl(
endpoint,
{ applicationId: "nodedc_mission_core_recorded", recordingId: "recording-001" },
"b".repeat(64),
"http://127.0.0.1:5174",
);
assert.equal(
sourceUrl,
`${endpoint}?application_id=nodedc_mission_core_recorded` +
`&recording_id=recording-001&generation=${"b".repeat(64)}`,
);
const overlayGeneration = "c".repeat(64);
const probe = await probeRecordedPerceptionViewerSource(sourceUrl, {
origin: "http://127.0.0.1:5174",
fetcher: async (input, init) => {
assert.equal(String(input), sourceUrl);
assert.equal(init.method, "HEAD");
assert.equal(new Headers(init.headers).get("Range"), null);
return new Response(null, {
status: 200,
headers: {
"Content-Type": "application/vnd.rerun.rrd",
"Content-Length": "186058411",
"ETag": `"${overlayGeneration}"`,
"X-Rerun-Format": "RRF2",
},
});
},
});
assert.deepEqual(probe, {
sourceUrl: `${sourceUrl}&overlay_generation=${overlayGeneration}`,
byteLength: 186_058_411,
});
});
test("LAB native source rejects an unsealed overlay descriptor", async () => {
const endpoint =
`http://127.0.0.1:5174/api/v1/laboratory/vegetation-shadow/` +
`lab-v1-vegetation-shadow-${"a".repeat(64)}/canonical-overlay.rrd`;
const sourceUrl = resolveRecordedPerceptionViewerSourceUrl(
endpoint,
{ applicationId: "nodedc_mission_core_recorded", recordingId: "recording-001" },
"b".repeat(64),
"http://127.0.0.1:5174",
);
await assert.rejects(
probeRecordedPerceptionViewerSource(sourceUrl, {
origin: "http://127.0.0.1:5174",
fetcher: async () => new Response(null, {
status: 200,
headers: {
"Content-Type": "application/vnd.rerun.rrd",
"Content-Length": "186058411",
"ETag": `"${"c".repeat(64)}"`,
"X-Rerun-Format": "RRF1",
},
}),
}),
/Invalid recorded perception viewer response/,
);
});
test("recorded replay creates an isolated source catalog without live device bindings", () => {
const sources = recordedObservationSources({
kind: "rerun-recording",
sessionId: "session-1",
sourceUrl: "/api/v1/observation-sessions/session-1/recording.rrd",
viewerSourceUrl: `/api/v1/observation-sessions/session-1/recording.rrd?generation=${"a".repeat(64)}`,
mediaType: "application/vnd.rerun.rrd",
timeline: "session_time",
timelineStartSeconds: 0,
timelineEndSeconds: 20,
seekable: true,
byteLength: 123,
sha256: "a".repeat(64),
playback: { speed: 1, loop: false },
mediaSources: [{
id: "recorded.camera.abc123",
label: "Записанная камера 1",
modality: "video",
manifestUrl: "/api/v1/observation-sessions/session-1/media/recorded-video-abc123/manifest",
manifestGenerationSha256: "c".repeat(64),
byteLength: 1_024,
mediaType: "video/mp4",
timelineStartSeconds: 0.25,
timelineEndSeconds: 20,
seekable: true,
synchronization: "host-arrival-best-effort",
}],
});
assert.deepEqual(sources.map(({ modality }) => modality), ["point-cloud", "video"]);
assert.equal(sources[1].delivery.kind, "recorded-fmp4-manifest");
assert.equal(sources[1].delivery.manifestGenerationSha256, "c".repeat(64));
assert.deepEqual(sources[1].binding, {});
assert.equal(sources.some(({ provider }) => provider.pluginId.includes("xgrids")), false);
assert.equal(JSON.stringify(sources).includes("192.168"), false);
});
test("recorded camera epoch selection and shared-clock offset are deterministic", () => {
const epochs = [
{ ordinal: 1, timelineStartSeconds: 0.25, timelineEndSeconds: 9 },
{ ordinal: 2, timelineStartSeconds: 10, timelineEndSeconds: 20 },
];
assert.equal(selectRecordedMediaEpoch(epochs, 0), null);
assert.equal(selectRecordedMediaEpoch(epochs, 0.25).ordinal, 1);
assert.equal(selectRecordedMediaEpoch(epochs, 9), epochs[0]);
assert.equal(selectRecordedMediaEpoch(epochs, 9.9), null);
assert.equal(selectRecordedMediaEpoch(epochs, 10).ordinal, 2);
assert.equal(recordedMediaLocalTime(10, 12.5), 2.5);
assert.equal(recordedMediaLocalTime(10, 8), 0);
assert.equal(recordedMediaLocalTime(10, 30, 4), 4);
});
after(async () => {
await server?.close();
});
function model() {
const activeModel = xgridsK1Manifest.spec.models[0];
assert.ok(activeModel, "XGRIDS plugin must declare at least one model");
return activeModel;
}
function cameraRow(sourceId, label) {
return {
stream_id: `camera.preview.${sourceId.split(".").at(-1)}`,
source_id: sourceId,
semantic_channel_id: "camera.preview.live",
label,
sensor_kind: "camera",
modality: "encoded-video",
availability: "available",
endpoint_label: "MSE · fMP4",
activation: {
group_id: "camera.preview.decoder",
max_active: 1,
selected: false,
controllable: true,
},
delivery: null,
};
}
function connectionSupervisor({ dataAuthoritative = false, dataPlaneState = "idle" } = {}) {
const observedAt = "2026-08-06T12:00:00Z";
const target = { ipv4: "192.168.68.52", port: 1883 };
return {
schema_version: "missioncore.k1-connection-supervisor/v1",
revision: 7,
closed: false,
intent: {
intent_id: "intent-001",
requested_mode: "bridge",
expected_device_id: "device-k1-001",
requested_at: observedAt,
},
observed: {
device_network: {
state: "applied",
intent_id: "intent-001",
transport_ref: "ble-k1-001",
connection_mode: "bridge",
target,
source: "ble-read-only-status",
observed_at: observedAt,
},
host_path: {
epoch: 3,
available: true,
fingerprint: "en0:192.168.68.10",
interface: "en0",
source_ipv4: "192.168.68.10",
route_class: "direct",
reason_code: null,
observed_at: observedAt,
},
endpoint: {
target,
tcp_state: "reachable",
intent_id: "intent-001",
host_path_epoch: 3,
reason_code: null,
observed_at: observedAt,
},
device_identity: {
state: "verified",
intent_id: "intent-001",
logical_device_id: "device-k1-001",
compatibility_profile_id: "xgrids.lixelkity-k1.fw-3.0.2.local-network.v2",
connection_mode: "bridge",
source: "mqtt-device-info",
host_path_epoch: 3,
observed_at: observedAt,
},
control_plane: {
state: "healthy",
session_id: "control-session-001",
host_path_epoch: 3,
reason_code: null,
observed_at: observedAt,
},
data_plane: {
state: dataAuthoritative ? "healthy" : dataPlaneState,
session_id: dataAuthoritative || dataPlaneState !== "idle" ? "data-session-001" : null,
host_path_epoch: dataAuthoritative || dataPlaneState !== "idle" ? 3 : null,
reason_code: null,
observed_at: dataAuthoritative || dataPlaneState !== "idle" ? observedAt : null,
},
},
lease: {
state: "reachable",
generation: 4,
intent_id: "intent-001",
host_path_epoch: 3,
connection_mode: "bridge",
target,
logical_device_id: "device-k1-001",
reason_code: null,
observed_at: observedAt,
},
authority: {
network_mutation_allowed: false,
control_allowed: true,
acquisition_start_allowed: true,
data_ingest_authoritative: dataAuthoritative,
physical_motion_allowed: false,
reason_codes: [],
},
last_known: null,
allowed_actions: ["stop-acquisition"],
};
}
function connectionLifecycle() {
return {
schema_version: "missioncore.xgrids-k1-connection-lifecycle/v1",
revision: 8,
desired_mode: "bridge",
configured_mode: "bridge",
active_mode: "bridge",
mode_change: {
state: "ready",
from: "bridge",
to: "bridge",
},
mode_selection: {
allowed: false,
reason_codes: ["connection-mode-selection-acquisition-active"],
automatic_retry: false,
},
active_binding_key: "binding-intent-001-bridge",
active_binding: {
binding_key: "binding-intent-001-bridge",
intent_id: "intent-001",
transport_ref: "ble-k1-001",
connection_mode: "bridge",
target_ipv4: "192.168.68.52",
target_port: 1883,
host_path_epoch: 3,
control_session_id: "control-session-001",
logical_device_id: "device-k1-001",
compatibility_profile_id: "xgrids.lixelkity-k1.fw-3.0.2.local-network.v2",
},
connection_ready: true,
ready_to_start: true,
operation: null,
allowed_actions: ["stop-acquisition"],
automatic_retry: false,
};
}
function declaredState(cameraRows = [
cameraRow("sensor.camera.left", "K1 · камера слева"),
cameraRow("sensor.camera.right", "K1 · камера справа"),
]) {
const activeModel = model();
return {
phase: "connected",
source_mode: "idle",
connection_mode: "bridge",
k1_ip: "192.168.7.10",
foxglove_ws_url: "ws://192.168.7.10:8765",
foxglove_viewer_url: "http://192.168.7.10:8765/vendor-viewer",
compatibility: {
profile_id: "xgrids.lixelkity-k1.fw-3.0.2.local-network.v2",
camera_preview: "rtsp://192.168.7.10:8554/vendor-preview",
},
device_ref: {
device_id: "device-k1-001",
model_id: activeModel.id,
identity_stability: "stable",
identity_basis: "hardware-identifier",
},
device_session: {
device_session_id: "device-session-001",
device_id: "device-k1-001",
compatibility_profile_id: "xgrids.lixelkity-k1.fw-3.0.2.local-network.v2",
connectivity: "connected",
},
connection_supervisor: connectionSupervisor(),
connection_lifecycle: connectionLifecycle(),
application_control_session: {
verified_control: {
logical_device_id: "device-k1-001",
compatibility_profile_id: "xgrids.lixelkity-k1.fw-3.0.2.local-network.v2",
control_session_id: "control-session-001",
source: "mqtt-device-info",
},
},
sensor_catalog: {
schema_version: "missioncore.sensor-catalog/v1alpha2",
revision: "test-profile",
streams: [
{ stream_id: "spatial.point-cloud.live", modality: "point-cloud", availability: "observed" },
...cameraRows,
],
},
camera_preview: {
phase: "idle",
revision: 1,
generation: 0,
active_source_id: null,
delivery: null,
},
};
}
function pointCloudStreamingState() {
const state = declaredState();
return {
...state,
phase: "streaming",
source_mode: "live",
connection_supervisor: connectionSupervisor({ dataAuthoritative: true }),
rerun_grpc_url: "rerun+http://127.0.0.1:9877/proxy",
acquisition: {
acquisition_id: "acquisition-001",
device_id: "device-k1-001",
device_session_id: "device-session-001",
compatibility_profile_id: "xgrids.lixelkity-k1.fw-3.0.2.local-network.v2",
control_mode: "operator-manual",
requested_streams: ["spatial.point-cloud.live"],
target_host: "127.0.0.1",
duration_seconds: 0,
evidence_policy: "required",
state: "acquiring",
state_revision: 3,
},
};
}
function cameraStreamingState(sourceId) {
const state = pointCloudStreamingState();
const delivery = {
id: `preview-generation-7:${sourceId}`,
kind: "mse-fmp4-websocket",
url: "/api/v1/device-plugins/xgrids-k1/camera-preview/ws?generation=7",
media_type: 'video/mp4; codecs="avc1.640028"',
};
state.sensor_catalog = {
...state.sensor_catalog,
streams: state.sensor_catalog.streams.map((stream) =>
stream.source_id === sourceId
? {
...stream,
availability: "streaming",
activation: { ...stream.activation, selected: true },
delivery,
}
: stream),
};
state.camera_preview = {
phase: "streaming",
revision: 4,
generation: 7,
active_source_id: sourceId,
delivery,
};
state.connection_recovery = {
schema_version: "missioncore.xgrids-k1-active-stream-recovery/v1",
state: "inactive",
generation: 0,
acquisition_id: null,
attempt: 0,
started_at_utc: null,
elapsed_ms: null,
reason_code: null,
force_finish_allowed: false,
automatic_read_only_rebind: false,
automatic_command_retry: false,
start_performed: false,
stop_performed: false,
ble_operation_performed: false,
network_mutation_performed: false,
runtime_producer_generation: null,
camera_recovery: "inactive",
camera_media_state: "ready",
camera_media_ready: true,
camera_epoch: {
generation: 7,
init_committed: true,
init_committed_age_ms: 100,
first_media_committed: true,
first_media_committed_age_ms: 80,
committed_media_segment_count: 2,
last_media_segment_age_ms: 20,
},
};
return state;
}
function cameraRecoveringState(sourceId, recoveryOverrides = {}, stateOverrides = {}) {
const state = cameraStreamingState(sourceId);
return {
...state,
snapshot_runtime_id: "runtime-camera-recovery-001",
snapshot_revision: 29,
producer_generation: 17,
phase: "reconnecting",
connection_supervisor: connectionSupervisor({ dataPlaneState: "lost" }),
connection_recovery: {
schema_version: "missioncore.xgrids-k1-active-stream-recovery/v1",
state: "reconnecting",
generation: 6,
acquisition_id: state.acquisition.acquisition_id,
attempt: 2,
started_at_utc: "2026-08-11T21:19:59Z",
elapsed_ms: 16_869,
reason_code: "host-route-unavailable",
force_finish_allowed: true,
automatic_read_only_rebind: true,
automatic_command_retry: false,
start_performed: false,
stop_performed: false,
ble_operation_performed: false,
network_mutation_performed: false,
runtime_producer_generation: 17,
camera_recovery: "owned",
camera_media_state: "pending-first-media",
camera_media_ready: false,
camera_epoch: {
generation: 7,
init_committed: true,
init_committed_age_ms: 250,
first_media_committed: false,
first_media_committed_age_ms: null,
committed_media_segment_count: 0,
last_media_segment_age_ms: null,
},
...recoveryOverrides,
},
...stateOverrides,
};
}
function collectUrlLikeStrings(value, found = []) {
if (typeof value === "string") {
if (value.includes("://")) found.push(value);
return found;
}
if (Array.isArray(value)) {
for (const item of value) collectUrlLikeStrings(item, found);
return found;
}
if (value && typeof value === "object") {
for (const item of Object.values(value)) collectUrlLikeStrings(item, found);
}
return found;
}
test("K1 maps zero, one or N catalog cameras without model-specific source ids", () => {
const zero = xgridsK1ObservationSources(declaredState([]), model());
const one = xgridsK1ObservationSources(
declaredState([cameraRow("rig.front", "Передняя камера")]),
model(),
);
const many = xgridsK1ObservationSources(declaredState(), model());
assert.deepEqual(zero.map(({ modality }) => modality), ["point-cloud"]);
assert.deepEqual(one.map(({ sourceId }) => sourceId), ["sensor.lidar.primary", "rig.front"]);
assert.deepEqual(many.map(({ sourceId }) => sourceId), [
"sensor.lidar.primary",
"sensor.camera.left",
"sensor.camera.right",
]);
assert.equal(new Set(many.map(({ id }) => id)).size, many.length);
assert.ok(many.every(({ provider }) => provider.pluginId && provider.modelId));
assert.ok(many.every(({ binding }) => binding.deviceId === "device-k1-001"));
});
test("descriptor ids remain stable while point cloud and selected camera start streaming", () => {
const declared = xgridsK1ObservationSources(declaredState(), model());
const streaming = xgridsK1ObservationSources(
cameraStreamingState("sensor.camera.left"),
model(),
);
assert.deepEqual(streaming.map(({ id }) => id), declared.map(({ id }) => id));
assert.equal(declared[0].availability, "available");
assert.equal(streaming[0].availability, "streaming");
assert.equal(streaming[0].previewUrl, "rerun+http://127.0.0.1:9877/proxy");
});
test("legacy selected-device fields cannot attest or stream observation sources", () => {
const state = cameraStreamingState("sensor.camera.left");
delete state.connection_supervisor;
delete state.application_control_session;
const sources = xgridsK1ObservationSources(state, model());
const pointCloud = sources.find(({ modality }) => modality === "point-cloud");
const left = sources.find(({ sourceId }) => sourceId === "sensor.camera.left");
assert.ok(pointCloud && left);
assert.equal(pointCloud.availability, "unverified");
assert.equal(pointCloud.previewUrl, null);
assert.equal(pointCloud.binding.deviceId, null);
assert.equal(pointCloud.binding.deviceSessionId, null);
assert.equal(left.availability, "unverified");
assert.equal(left.activation.selected, false);
assert.equal(left.activation.controllable, false);
assert.equal(left.delivery, null);
});
test("data-plane loss keeps control identity but withdraws all live deliveries", () => {
const state = cameraStreamingState("sensor.camera.left");
state.connection_supervisor = connectionSupervisor({ dataPlaneState: "lost" });
const sources = xgridsK1ObservationSources(state, model());
const pointCloud = sources.find(({ modality }) => modality === "point-cloud");
const left = sources.find(({ sourceId }) => sourceId === "sensor.camera.left");
assert.ok(pointCloud && left);
assert.equal(pointCloud.availability, "degraded");
assert.equal(pointCloud.previewUrl, null);
assert.equal(pointCloud.binding.deviceId, "device-k1-001");
assert.equal(left.activation.selected, true);
assert.equal(left.availability, "degraded");
assert.equal(left.delivery, null);
});
test("only the authoritative selected camera receives browser delivery", () => {
const sources = xgridsK1ObservationSources(
cameraStreamingState("sensor.camera.left"),
model(),
);
const cameras = sources.filter(({ modality }) => modality === "video");
const [left, right] = cameras;
assert.equal(cameras.length, 2);
assert.equal(left.activation.selected, true);
assert.equal(left.activation.maxActive, 1);
assert.equal(left.availability, "streaming");
assert.equal(left.delivery.kind, "mse-fmp4-websocket");
assert.equal(left.transport, "websocket");
assert.equal(right.activation.selected, false);
assert.equal(right.availability, "available");
assert.equal(right.delivery, null);
assert.equal(left.activation.groupId, right.activation.groupId);
assert.match(left.activation.groupId, /device-session-001/);
});
test("ordinary camera delivery requires ready media facts from the exact current epoch", () => {
const pending = cameraStreamingState("sensor.camera.left");
pending.connection_recovery = {
...pending.connection_recovery,
camera_media_state: "pending-first-media",
camera_media_ready: false,
camera_epoch: {
generation: 7,
init_committed: true,
init_committed_age_ms: 20,
first_media_committed: false,
first_media_committed_age_ms: null,
committed_media_segment_count: 0,
last_media_segment_age_ms: null,
},
};
const staleEpoch = cameraStreamingState("sensor.camera.left");
staleEpoch.connection_recovery = {
...staleEpoch.connection_recovery,
camera_epoch: {
...staleEpoch.connection_recovery.camera_epoch,
generation: 6,
},
};
for (const state of [pending, staleEpoch]) {
const camera = xgridsK1ObservationSources(state, model()).find(
({ sourceId }) => sourceId === "sensor.camera.left",
);
assert.ok(camera);
assert.equal(camera.activation.selected, false);
assert.equal(camera.delivery, null);
assert.equal(camera.availability, "available");
assert.equal(liveCameraPlaybackAuthorityIdentity(camera), null);
}
});
test("switching left to right keeps ids stable and never exposes both deliveries", () => {
const left = xgridsK1ObservationSources(
cameraStreamingState("sensor.camera.left"),
model(),
).filter(({ modality }) => modality === "video");
const right = xgridsK1ObservationSources(
cameraStreamingState("sensor.camera.right"),
model(),
).filter(({ modality }) => modality === "video");
assert.deepEqual(right.map(({ id }) => id), left.map(({ id }) => id));
assert.deepEqual(left.filter(({ delivery }) => delivery).map(({ sourceId }) => sourceId), [
"sensor.camera.left",
]);
assert.deepEqual(right.filter(({ delivery }) => delivery).map(({ sourceId }) => sourceId), [
"sensor.camera.right",
]);
});
test("camera descriptors never leak vendor RTSP endpoints or device IP addresses", () => {
const sources = xgridsK1ObservationSources(
cameraStreamingState("sensor.camera.left"),
model(),
);
const cameras = sources.filter(({ modality }) => modality === "video");
assert.ok(cameras.every(({ previewUrl }) => previewUrl === null));
assert.deepEqual(collectUrlLikeStrings(sources), [
"rerun+http://127.0.0.1:9877/proxy",
]);
const serialized = JSON.stringify(cameras);
assert.doesNotMatch(serialized, /rtsp:\/\//i);
assert.doesNotMatch(serialized, /192\.168\.7\.10/);
assert.doesNotMatch(serialized, /vendor-(?:preview|viewer)/);
});
test("unattested, duplicate and unsafe camera entries fail closed", () => {
const duplicate = cameraRow("sensor.camera.left", "Duplicate");
const state = cameraStreamingState("sensor.camera.left");
state.compatibility.profile_id = null;
state.device_session.compatibility_profile_id = null;
state.connection_supervisor.observed.device_identity.state = "mismatch";
state.application_control_session.verified_control = null;
state.sensor_catalog.streams.push(duplicate);
state.camera_preview.delivery = {
...state.camera_preview.delivery,
url: "ws://192.168.7.10:9000/leak",
};
const cameras = xgridsK1ObservationSources(state, model()).filter(
({ modality }) => modality === "video",
);
assert.deepEqual(cameras.map(({ sourceId }) => sourceId), ["sensor.camera.right"]);
assert.equal(cameras[0].availability, "unverified");
assert.equal(cameras[0].activation.controllable, false);
assert.equal(cameras[0].delivery, null);
});
test("camera delivery rejects literal and encoded endpoint or credential leaks", () => {
const unsafeUrls = [
"/api/preview?upstream=rtsp://camera.local/live",
"/api/preview?upstream=rtsp%3A%2F%2Fcamera.local%2Flive",
"/api/preview?upstream=rtsp%253A%252F%252Fcamera.local%252Flive",
"/api/preview?endpoint=192.0.2.52:8554",
"/api/preview?endpoint=192%2E168%2E68%2E52",
"/api/preview?password=not-for-the-browser",
"/api/preview?%70%61%73%73%77%6f%72%64=not-for-the-browser",
"/api/preview/camera:secret@device",
"/%2f%2fevil.example/preview",
];
for (const url of unsafeUrls) {
const state = cameraStreamingState("sensor.camera.left");
state.camera_preview.delivery = { ...state.camera_preview.delivery, url };
state.sensor_catalog.streams = state.sensor_catalog.streams.map((stream) =>
stream.source_id === "sensor.camera.left"
? { ...stream, delivery: { ...stream.delivery, url } }
: stream,
);
const left = xgridsK1ObservationSources(state, model()).find(
({ sourceId }) => sourceId === "sensor.camera.left",
);
assert.ok(left, `left camera descriptor missing for ${url}`);
assert.equal(left.delivery, null, `unsafe delivery escaped for ${url}`);
}
});
test("authoritative camera transport retries indefinitely with a capped backoff", () => {
let budget = resetCameraLeaseRetryBudget("delivery-7");
for (const expectedDelay of [400, 1_000, 2_000, 5_000, 5_000, 5_000]) {
const retry = consumeCameraLeaseRetry(budget, "delivery-7");
assert.equal(retry.delay, expectedDelay);
budget = retry.budget;
}
assert.equal(budget.count, 4);
budget = resetCameraLeaseRetryBudget("delivery-7");
const retryAfterManualReset = consumeCameraLeaseRetry(budget, "delivery-7");
assert.equal(retryAfterManualReset.delay, 400);
assert.equal(retryAfterManualReset.budget.count, 1);
});
test("camera startup watchdog replaces an open-but-silent MSE or WebSocket", () => {
const scheduled = new Map();
const timeouts = [];
let nextHandle = 1;
const watchdog = createCameraStartupWatchdog({
schedule(callback, timeoutMs) {
const handle = nextHandle;
nextHandle += 1;
scheduled.set(handle, { callback, timeoutMs });
return handle;
},
cancel(handle) {
scheduled.delete(handle);
},
onTimeout(stage) {
timeouts.push(stage);
},
});
watchdog.armFirstMedia();
watchdog.armFirstMedia();
assert.equal(watchdog.pendingStage(), "first-media");
assert.equal(scheduled.size, 1);
const [{ callback, timeoutMs }] = scheduled.values();
assert.equal(timeoutMs, CAMERA_FIRST_MEDIA_TIMEOUT_MS);
callback();
assert.deepEqual(timeouts, ["first-media"]);
assert.equal(watchdog.pendingStage(), null);
assert.match(
cameraStartupWatchdogRecoveryMessage("first-media"),
/не передаёт медиаданные; восстанавливаем/,
);
});
test("continuing media extends the first-playable-frame deadline", () => {
const scheduled = new Map();
const cancelled = [];
const timeouts = [];
let nextHandle = 10;
const watchdog = createCameraStartupWatchdog({
schedule(callback, timeoutMs) {
const handle = nextHandle;
nextHandle += 1;
scheduled.set(handle, { callback, timeoutMs });
return handle;
},
cancel(handle) {
cancelled.push(handle);
scheduled.delete(handle);
},
onTimeout(stage) {
timeouts.push(stage);
},
});
watchdog.armFirstMedia();
watchdog.markMediaReceived();
const playableHandle = nextHandle - 1;
assert.deepEqual(cancelled, [10]);
assert.equal(watchdog.pendingStage(), "first-playable-frame");
assert.equal(scheduled.get(playableHandle).timeoutMs, CAMERA_FIRST_PLAYABLE_FRAME_TIMEOUT_MS);
watchdog.markMediaReceived();
const extendedPlayableHandle = nextHandle - 1;
assert.notEqual(extendedPlayableHandle, playableHandle);
assert.deepEqual(cancelled, [10, playableHandle]);
assert.equal(
scheduled.get(extendedPlayableHandle).timeoutMs,
CAMERA_FIRST_PLAYABLE_FRAME_TIMEOUT_MS,
);
scheduled.get(extendedPlayableHandle).callback();
assert.deepEqual(timeouts, ["first-playable-frame"]);
assert.match(
cameraStartupWatchdogRecoveryMessage("first-playable-frame"),
/первый кадр не воспроизводится; пересоздаём decoder/,
);
});
test("playing clears the camera startup watchdog", () => {
const scheduled = new Map();
const cancelled = [];
const watchdog = createCameraStartupWatchdog({
schedule(callback) {
scheduled.set(21, callback);
return 21;
},
cancel(handle) {
cancelled.push(handle);
scheduled.delete(handle);
},
onTimeout() {
assert.fail("a playing transport must not time out");
},
});
watchdog.armFirstMedia();
watchdog.markMediaReceived();
watchdog.markPlaying();
assert.equal(watchdog.pendingStage(), null);
assert.deepEqual(cancelled, [21, 21]);
assert.equal(scheduled.size, 0);
});
test("browser append queue absorbs the complete bounded server backlog", () => {
assert.equal(
cameraPendingQueueCanAccept(8 * 1024 * 1024, 64, 1024 * 1024),
true,
);
assert.equal(
cameraPendingQueueCanAccept(11 * 1024 * 1024, 95, 1024 * 1024),
true,
);
assert.equal(
cameraPendingQueueCanAccept(12 * 1024 * 1024, 64, 1),
false,
);
assert.equal(
cameraPendingQueueCanAccept(8 * 1024 * 1024, 96, 1),
false,
);
});
test("server slow-reader close is an automatic browser-only camera recovery", () => {
assert.match(
cameraTransportCloseRecoveryMessage(4_008),
/отстал от эфира; восстанавливаем текущую камеру/,
);
assert.match(cameraTransportCloseRecoveryMessage(1_008), /переподключаемся/);
assert.match(cameraTransportCloseRecoveryMessage(1_000), /восстанавливаем/);
assert.match(cameraTransportCloseRecoveryMessage(1_011), /восстанавливаем/);
});
test("camera transport recovery events keep exact diagnostic causes", () => {
assert.equal(
cameraPlaybackRecoveryFailureStage({ type: "heartbeat" }),
"camera-heartbeat-reopen",
);
assert.equal(
cameraPlaybackRecoveryFailureStage({ type: "document-visible" }),
"camera-visibility-reopen",
);
assert.equal(
cameraPlaybackRecoveryFailureStage({ type: "network-online" }),
"camera-network-online-reopen",
);
assert.equal(
cameraPlaybackRecoveryFailureStage({ type: "page-restore", persisted: true }),
"camera-page-restore-reopen",
);
});
test("a laptop sleep gap reopens only the exact authoritative live camera transport", () => {
const source = xgridsK1ObservationSources(
cameraStreamingState("sensor.camera.right"),
model(),
).find(({ sourceId }) => sourceId === "sensor.camera.right");
assert.ok(source);
const authority = liveCameraPlaybackAuthorityIdentity(source);
assert.ok(authority);
const initial = initialCameraPlaybackRecoveryState(authority, 1_000);
const wake = reduceCameraPlaybackRecovery(initial, { type: "heartbeat" }, {
activeAuthorityIdentity: authority,
now: 7_000,
documentVisible: true,
networkOnline: true,
transportHealthy: false,
});
assert.equal(wake.reopen, true);
assert.equal(wake.state.lastReopenAt, 7_000);
const staleSource = reduceCameraPlaybackRecovery(initial, { type: "heartbeat" }, {
activeAuthorityIdentity: `${authority}:replaced`,
now: 7_000,
documentVisible: true,
networkOnline: true,
});
assert.equal(staleSource.reopen, false);
});
test("visibility, pageshow and online wake burst owns one replacement decoder", () => {
const authority = "camera-authority-acquisition-1";
let state = initialCameraPlaybackRecoveryState(authority, 1_000);
({ state } = reduceCameraPlaybackRecovery(state, { type: "document-hidden" }, {
activeAuthorityIdentity: authority,
now: 2_000,
documentVisible: false,
networkOnline: true,
}));
const visible = reduceCameraPlaybackRecovery(state, { type: "document-visible" }, {
activeAuthorityIdentity: authority,
now: 8_000,
documentVisible: true,
networkOnline: true,
});
assert.equal(visible.reopen, true);
state = visible.state;
for (const event of [
{ type: "page-restore", persisted: true },
{ type: "network-online" },
{ type: "heartbeat" },
]) {
const duplicate = reduceCameraPlaybackRecovery(state, event, {
activeAuthorityIdentity: authority,
now: 8_100,
documentVisible: true,
networkOnline: true,
});
assert.equal(duplicate.reopen, false);
state = duplicate.state;
}
({ state } = reduceCameraPlaybackRecovery(state, { type: "document-hidden" }, {
activeAuthorityIdentity: authority,
now: 12_000,
documentVisible: false,
networkOnline: true,
}));
const secondWake = reduceCameraPlaybackRecovery(state, { type: "document-visible" }, {
activeAuthorityIdentity: authority,
now: 18_000,
documentVisible: true,
networkOnline: true,
});
assert.equal(secondWake.reopen, true);
assert.equal(secondWake.state.lastReopenAt, 18_000);
});
test("healthy camera survives a long main-thread heartbeat gap without decoder churn", () => {
const authority = "camera-authority-acquisition-healthy";
let state = initialCameraPlaybackRecoveryState(authority, 1_000);
for (const now of [2_000, 3_000, 4_000, 12_000, 25_000]) {
const heartbeat = reduceCameraPlaybackRecovery(state, { type: "heartbeat" }, {
activeAuthorityIdentity: authority,
now,
documentVisible: true,
networkOnline: true,
transportHealthy: true,
});
assert.equal(heartbeat.reopen, false);
state = heartbeat.state;
}
assert.equal(state.lastReopenAt, null);
});
test("callbacks from a replaced camera WebSocket and SourceBuffer are fenced", () => {
assert.equal(cameraTransportCallbackIsCurrent(4, 4, false), true);
assert.equal(cameraTransportCallbackIsCurrent(5, 4, false), false);
assert.equal(cameraTransportCallbackIsCurrent(4, 4, true), false);
});
test("an already-stale document opens zero camera transports on mount", () => {
let transportOpenCount = 0;
const uiBuildStaleRef = { current: false };
const subscribeToAlreadyStaleCoordinator = (listener) => {
listener();
return () => undefined;
};
subscribeToAlreadyStaleCoordinator(() => {
uiBuildStaleRef.current = true;
});
if (cameraTransportCanOpen(uiBuildStaleRef.current)) transportOpenCount += 1;
assert.equal(uiBuildStaleRef.current, true);
assert.equal(transportOpenCount, 0);
});
test("only the current acquisition authority may schedule a replacement decoder", () => {
const authority = "camera-authority-acquisition-current";
assert.equal(
cameraTransportRecoveryIsCurrent(authority, authority, 7, 7, false),
true,
);
assert.equal(
cameraTransportRecoveryIsCurrent(`${authority}:replaced`, authority, 7, 7, false),
false,
);
assert.equal(
cameraTransportRecoveryIsCurrent(authority, authority, 8, 7, false),
false,
);
assert.equal(
cameraTransportRecoveryIsCurrent(authority, authority, 7, 7, true),
false,
);
assert.equal(
cameraTransportRecoveryIsCurrent(null, authority, 7, 7, false),
false,
);
});
test("same camera delivery gets a new browser transport owner for a new acquisition", () => {
const camera = xgridsK1ObservationSources(
cameraStreamingState("sensor.camera.right"),
model(),
).find(({ sourceId }) => sourceId === "sensor.camera.right");
assert.ok(camera?.delivery && camera.delivery.kind === "mse-fmp4-websocket");
const previousAuthority = liveCameraPlaybackAuthorityIdentity(camera);
const nextAuthority = liveCameraPlaybackAuthorityIdentity({
...camera,
binding: {
...camera.binding,
deviceSessionId: "device-session-002",
acquisitionId: "acquisition-002",
},
});
assert.ok(previousAuthority && nextAuthority);
assert.notEqual(nextAuthority, previousAuthority);
assert.notEqual(
cameraBrowserTransportIdentity(camera.delivery, nextAuthority),
cameraBrowserTransportIdentity(camera.delivery, previousAuthority),
);
});
test("malformed camera exclusivity or media descriptors cannot own wake recovery", () => {
const camera = xgridsK1ObservationSources(
cameraStreamingState("sensor.camera.left"),
model(),
).find(({ sourceId }) => sourceId === "sensor.camera.left");
assert.ok(camera?.activation && camera.delivery?.kind === "mse-fmp4-websocket");
for (const malformed of [
{ ...camera, activation: { ...camera.activation, groupId: " " } },
{ ...camera, activation: { ...camera.activation, groupId: null } },
{ ...camera, activation: { ...camera.activation, maxActive: 2 } },
{ ...camera, delivery: { ...camera.delivery, mediaType: " " } },
{ ...camera, delivery: { ...camera.delivery, mediaType: null } },
{ ...camera, delivery: { ...camera.delivery, mediaType: "video/webm" } },
]) {
assert.equal(liveCameraPlaybackAuthorityIdentity(malformed), null);
}
});
test("point-cloud recovery cannot revive a dead or stale camera delivery", () => {
const sources = xgridsK1ObservationSources(
cameraStreamingState("sensor.camera.left"),
model(),
);
const pointCloud = sources.find(({ modality }) => modality === "point-cloud");
const camera = sources.find(({ sourceId }) => sourceId === "sensor.camera.left");
assert.ok(pointCloud && camera);
assert.equal(pointCloud.availability, "streaming");
assert.ok(liveCameraPlaybackAuthorityIdentity(camera));
assert.equal(liveCameraPlaybackAuthorityIdentity({
...camera,
availability: "error",
}), null);
assert.equal(liveCameraPlaybackAuthorityIdentity({
...camera,
binding: { ...camera.binding, acquisitionId: "acquisition-replaced" },
activation: { ...camera.activation, selected: false },
}), null);
});
test("exact active recovery retains the same camera delivery without controls", () => {
const healthy = xgridsK1ObservationSources(
cameraStreamingState("sensor.camera.left"),
model(),
);
const recovering = xgridsK1ObservationSources(
cameraRecoveringState("sensor.camera.left"),
model(),
);
const healthyPointCloud = healthy.find(({ modality }) => modality === "point-cloud");
const recoveryPointCloud = recovering.find(({ modality }) => modality === "point-cloud");
const healthyCamera = healthy.find(({ sourceId }) => sourceId === "sensor.camera.left");
const recoveryCamera = recovering.find(({ sourceId }) => sourceId === "sensor.camera.left");
assert.ok(healthyPointCloud && recoveryPointCloud && healthyCamera && recoveryCamera);
assert.equal(recoveryPointCloud.id, healthyPointCloud.id);
assert.equal(recoveryPointCloud.previewUrl, healthyPointCloud.previewUrl);
assert.equal(recoveryPointCloud.availability, "connecting");
assert.equal(recoveryCamera.id, healthyCamera.id);
assert.deepEqual(recoveryCamera.delivery, healthyCamera.delivery);
assert.equal(recoveryCamera.activation.selected, true);
assert.equal(recoveryCamera.activation.controllable, false);
assert.equal(recoveryCamera.availability, "connecting");
assert.deepEqual(recoveryCamera.presentationLease, {
kind: "active-stream-recovery",
runtimeId: "runtime-camera-recovery-001",
acquisitionId: "acquisition-001",
acquisitionStateRevision: 3,
producerGeneration: 17,
recoveryGeneration: 6,
});
const healthyAuthority = liveCameraPlaybackAuthorityIdentity(healthyCamera);
const recoveryAuthority = liveCameraPlaybackAuthorityIdentity(recoveryCamera);
assert.ok(healthyAuthority && recoveryAuthority);
assert.notEqual(recoveryAuthority, healthyAuthority);
assert.equal(
liveCameraPlaybackAuthorityIdentity(
xgridsK1ObservationSources(
cameraRecoveringState("sensor.camera.left"),
model(),
).find(({ sourceId }) => sourceId === "sensor.camera.left"),
),
recoveryAuthority,
"repeated snapshots of the same recovery generation keep one browser owner",
);
});
test("camera recovery stays connecting until the current epoch has durable first media", () => {
const pending = xgridsK1ObservationSources(
cameraRecoveringState("sensor.camera.left"),
model(),
).find(({ sourceId }) => sourceId === "sensor.camera.left");
const ready = xgridsK1ObservationSources(
cameraRecoveringState("sensor.camera.left", {
camera_media_state: "ready",
camera_media_ready: true,
camera_epoch: {
generation: 7,
init_committed: true,
init_committed_age_ms: 500,
first_media_committed: true,
first_media_committed_age_ms: 100,
committed_media_segment_count: 1,
last_media_segment_age_ms: 100,
},
}),
model(),
).find(({ sourceId }) => sourceId === "sensor.camera.left");
assert.ok(pending?.delivery && ready?.delivery);
assert.equal(pending.activation.selected, true);
assert.equal(pending.availability, "connecting");
assert.ok(pending.presentationLease);
assert.equal(ready.activation.selected, true);
assert.equal(ready.availability, "streaming");
assert.ok(ready.presentationLease);
});
test("first recovered PCL keeps the exact camera authority until browser media is playable", () => {
const reconnectingState = cameraRecoveringState("sensor.camera.left");
const recoveredState = cameraRecoveringState(
"sensor.camera.left",
{
state: "recovered",
force_finish_allowed: false,
elapsed_ms: null,
reason_code: null,
},
{
phase: "live",
connection_supervisor: connectionSupervisor({ dataAuthoritative: true }),
},
);
recoveredState.camera_preview = {
...recoveredState.camera_preview,
phase: "connecting",
};
recoveredState.sensor_catalog = {
...recoveredState.sensor_catalog,
streams: recoveredState.sensor_catalog.streams.map((stream) =>
stream.source_id === "sensor.camera.left"
? { ...stream, availability: "connecting" }
: stream),
};
const reconnecting = xgridsK1ObservationSources(reconnectingState, model());
const recovered = xgridsK1ObservationSources(recoveredState, model());
const reconnectingCamera = reconnecting.find(
({ sourceId }) => sourceId === "sensor.camera.left",
);
const recoveredCamera = recovered.find(
({ sourceId }) => sourceId === "sensor.camera.left",
);
const reconnectingPointCloud = reconnecting.find(
({ modality }) => modality === "point-cloud",
);
const recoveredPointCloud = recovered.find(
({ modality }) => modality === "point-cloud",
);
assert.ok(
reconnectingCamera
&& recoveredCamera
&& reconnectingPointCloud
&& recoveredPointCloud,
);
assert.equal(recoveredCamera.availability, "connecting");
assert.equal(recoveredCamera.activation.selected, true);
assert.equal(recoveredCamera.activation.controllable, true);
assert.deepEqual(recoveredCamera.delivery, reconnectingCamera.delivery);
assert.deepEqual(recoveredCamera.presentationLease, reconnectingCamera.presentationLease);
assert.deepEqual(
recoveredPointCloud.presentationLease,
reconnectingPointCloud.presentationLease,
);
assert.equal(
liveCameraPlaybackAuthorityIdentity(recoveredCamera),
liveCameraPlaybackAuthorityIdentity(reconnectingCamera),
"reconnecting→recovered must not retire the decoder before first playable frame",
);
const spatialSource = {
id: "acquisition-001",
url: reconnectingPointCloud.previewUrl,
label: "Live",
kind: "rerun-grpc",
};
assert.equal(
liveRerunRecoveryAuthorityIdentity(recoveredPointCloud, spatialSource),
liveRerunRecoveryAuthorityIdentity(reconnectingPointCloud, spatialSource),
"the first recovered PCL keeps one exact Rerun browser authority",
);
});
test("camera recovery fails closed while the exact spatial lease may continue", () => {
const cameraStreamError = cameraRecoveringState("sensor.camera.left");
cameraStreamError.sensor_catalog.streams = cameraStreamError.sensor_catalog.streams.map(
(stream) => stream.source_id === "sensor.camera.left"
? { ...stream, availability: "error" }
: stream,
);
const mismatchedDelivery = cameraRecoveringState("sensor.camera.left");
mismatchedDelivery.camera_preview.delivery = {
...mismatchedDelivery.camera_preview.delivery,
id: "preview-generation-replaced:sensor.camera.left",
};
const missingDelivery = cameraRecoveringState("sensor.camera.left");
missingDelivery.camera_preview.delivery = null;
missingDelivery.sensor_catalog.streams = missingDelivery.sensor_catalog.streams.map(
(stream) => stream.source_id === "sensor.camera.left"
? { ...stream, delivery: null }
: stream,
);
const cases = [
cameraRecoveringState("sensor.camera.left", { camera_recovery: "blocked" }),
cameraRecoveringState("sensor.camera.left", {}, {
device_session: {
...cameraStreamingState("sensor.camera.left").device_session,
device_session_id: "device-session-replaced",
},
}),
cameraRecoveringState("sensor.camera.left", {}, {
camera_preview: {
...cameraStreamingState("sensor.camera.left").camera_preview,
phase: "error",
},
}),
cameraStreamError,
mismatchedDelivery,
missingDelivery,
];
for (const state of cases) {
const sources = xgridsK1ObservationSources(state, model());
const pointCloud = sources.find(({ modality }) => modality === "point-cloud");
const camera = sources.find(({ sourceId }) => sourceId === "sensor.camera.left");
assert.ok(pointCloud && camera);
assert.equal(pointCloud.previewUrl, "rerun+http://127.0.0.1:9877/proxy");
assert.equal(pointCloud.availability, "connecting");
assert.equal(camera.activation.selected, false);
assert.equal(camera.activation.controllable, false);
assert.equal(camera.delivery, null);
assert.equal(camera.presentationLease, null);
assert.equal(liveCameraPlaybackAuthorityIdentity(camera), null);
}
});
test("stale and terminal recovery states withdraw both retained transports", () => {
const staleProducer = cameraRecoveringState("sensor.camera.left");
staleProducer.producer_generation += 1;
const differentAcquisition = cameraRecoveringState("sensor.camera.left", {
acquisition_id: "acquisition-stale",
});
for (const state of [
staleProducer,
differentAcquisition,
cameraRecoveringState("sensor.camera.left", { state: "blocked" }),
cameraRecoveringState("sensor.camera.left", { state: "standby" }),
cameraRecoveringState("sensor.camera.left", { state: "fault" }),
]) {
const sources = xgridsK1ObservationSources(state, model());
const pointCloud = sources.find(({ modality }) => modality === "point-cloud");
const camera = sources.find(({ sourceId }) => sourceId === "sensor.camera.left");
assert.ok(pointCloud && camera);
assert.equal(pointCloud.previewUrl, null);
assert.equal(pointCloud.presentationLease, null);
assert.equal(camera.activation.selected, false);
assert.equal(camera.delivery, null);
assert.equal(camera.presentationLease, null);
}
});
test("malformed recovery leases cannot own a camera WebSocket or decoder", () => {
const camera = xgridsK1ObservationSources(
cameraRecoveringState("sensor.camera.left"),
model(),
).find(({ sourceId }) => sourceId === "sensor.camera.left");
assert.ok(camera?.presentationLease);
for (const presentationLease of [
null,
{ ...camera.presentationLease, runtimeId: " " },
{ ...camera.presentationLease, acquisitionId: "acquisition-stale" },
{ ...camera.presentationLease, acquisitionStateRevision: 0 },
{ ...camera.presentationLease, producerGeneration: 0 },
{ ...camera.presentationLease, recoveryGeneration: 0 },
]) {
assert.equal(
liveCameraPlaybackAuthorityIdentity({ ...camera, presentationLease }),
null,
);
}
});
test("layout policy evicts only exclusive camera peers", () => {
const sources = xgridsK1ObservationSources(
cameraStreamingState("sensor.camera.left"),
model(),
);
const left = sources.find(({ sourceId }) => sourceId === "sensor.camera.left");
const right = sources.find(({ sourceId }) => sourceId === "sensor.camera.right");
const pointCloud = sources.find(({ modality }) => modality === "point-cloud");
assert.ok(left && right && pointCloud);
const change = openObservationSource(
[pointCloud.id, left.id],
right.id,
sources,
);
assert.deepEqual(change.visibleIds, [pointCloud.id, right.id]);
assert.deepEqual(change.removedIds, [left.id]);
});
test("selected camera without delivery is explicitly restartable", () => {
const state = cameraStreamingState("sensor.camera.left");
state.camera_preview = {
...state.camera_preview,
phase: "error",
delivery: null,
};
state.sensor_catalog.streams = state.sensor_catalog.streams.map((stream) =>
stream.source_id === "sensor.camera.left"
? { ...stream, availability: "error", delivery: null }
: stream,
);
const sources = xgridsK1ObservationSources(state, model());
const left = sources.find(({ sourceId }) => sourceId === "sensor.camera.left");
const right = sources.find(({ sourceId }) => sourceId === "sensor.camera.right");
assert.ok(left && right);
assert.equal(left.activation.selected, true);
assert.equal(left.delivery, null);
assert.equal(left.availability, "error");
assert.equal(shouldRestartObservationSource(left), true);
assert.equal(shouldRestartObservationSource(right), false);
});