Add calibrated K1 projection, recorded and near-live perception qualification, unified Rerun operator layers, bounded replay admission, audited viewer controls, worker experiments, and lab evidence.
187 lines
6.5 KiB
JavaScript
187 lines
6.5 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 fetchRecordedMediaArchive;
|
|
let recordedMediaPresentationState;
|
|
let recordedMediaSeekableCoverage;
|
|
|
|
before(async () => {
|
|
server = await createServer({
|
|
appType: "custom",
|
|
logLevel: "silent",
|
|
server: { middlewareMode: true },
|
|
});
|
|
({
|
|
fetchRecordedMediaArchive,
|
|
recordedMediaPresentationState,
|
|
recordedMediaSeekableCoverage,
|
|
} = await server.ssrLoadModule("/src/components/RecordedFmp4Player.tsx"));
|
|
});
|
|
|
|
after(async () => {
|
|
await server?.close();
|
|
});
|
|
|
|
function fixture({ byteLength = 36_000_000_000 } = {}) {
|
|
const manifestUrl = "/api/v1/observation-sessions/session-1/media/camera-1/manifest";
|
|
const generation = "a".repeat(64);
|
|
const streamUrl = manifestUrl.replace(
|
|
"/manifest",
|
|
`/epochs/1/recording.mp4?generation=${generation}`,
|
|
);
|
|
const manifest = {
|
|
schema_version: "missioncore.observation-recorded-media/v3",
|
|
source_id: "recorded.camera.camera-1",
|
|
generation_sha256: generation,
|
|
byte_length: byteLength,
|
|
timeline_start_seconds: 0,
|
|
timeline_end_seconds: 36_000,
|
|
synchronization: "host-arrival-best-effort",
|
|
epochs: [{
|
|
ordinal: 1,
|
|
timeline_start_seconds: 0,
|
|
timeline_end_seconds: 36_000,
|
|
media_type: 'video/mp4; codecs="avc1.640028"',
|
|
byte_length: byteLength,
|
|
stream_url: streamUrl,
|
|
}],
|
|
};
|
|
const source = {
|
|
id: manifest.source_id,
|
|
label: "Записанная камера",
|
|
modality: "video",
|
|
manifestUrl,
|
|
manifestGenerationSha256: generation,
|
|
byteLength,
|
|
mediaType: "video/mp4",
|
|
timelineStartSeconds: 0,
|
|
timelineEndSeconds: 36_000,
|
|
seekable: true,
|
|
synchronization: "host-arrival-best-effort",
|
|
};
|
|
return { source, manifest, generation, streamUrl };
|
|
}
|
|
|
|
function jsonResponse(payload, generation) {
|
|
return new Response(JSON.stringify(payload), {
|
|
status: 200,
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
ETag: `"sha256:${generation}"`,
|
|
},
|
|
});
|
|
}
|
|
|
|
test("multi-hour camera admission fetches only its compact generation-bound manifest", async () => {
|
|
const { source, manifest, generation, streamUrl } = fixture();
|
|
const requested = [];
|
|
const archive = await fetchRecordedMediaArchive(source, {
|
|
fetcher: async (input, request = {}) => {
|
|
requested.push(String(input));
|
|
assert.equal(new Headers(request.headers).get("If-Match"), `"sha256:${generation}"`);
|
|
return jsonResponse(manifest, generation);
|
|
},
|
|
});
|
|
|
|
assert.deepEqual(requested, [source.manifestUrl]);
|
|
assert.equal(archive.byteLength, 36_000_000_000);
|
|
assert.equal(archive.manifest.epochs[0].streamUrl, streamUrl);
|
|
});
|
|
|
|
test("first camera manifest request rejects a replaced generation", async () => {
|
|
const { source, manifest, generation } = fixture();
|
|
const replacementGeneration = "d".repeat(64);
|
|
await assert.rejects(
|
|
fetchRecordedMediaArchive(source, {
|
|
fetcher: async (_input, request = {}) => {
|
|
assert.equal(new Headers(request.headers).get("If-Match"), `"sha256:${generation}"`);
|
|
return jsonResponse(
|
|
{ ...manifest, generation_sha256: replacementGeneration },
|
|
replacementGeneration,
|
|
);
|
|
},
|
|
}),
|
|
/несовместим|заменён/,
|
|
);
|
|
});
|
|
|
|
test("camera manifest fails closed when epoch bytes do not match launch bytes", async () => {
|
|
const { source, manifest, generation } = fixture();
|
|
const mismatched = {
|
|
...manifest,
|
|
epochs: [{ ...manifest.epochs[0], byte_length: manifest.byte_length - 1 }],
|
|
};
|
|
await assert.rejects(
|
|
fetchRecordedMediaArchive(source, {
|
|
fetcher: async () => jsonResponse(mismatched, generation),
|
|
}),
|
|
/не совпадает/,
|
|
);
|
|
});
|
|
|
|
test("camera presentation gate opens only for the seekable selected epoch", () => {
|
|
assert.equal(recordedMediaPresentationState("loading", null, "g1", false), "loading");
|
|
assert.equal(recordedMediaPresentationState("ready", null, "g1", false), "loading");
|
|
assert.equal(recordedMediaPresentationState("ready", "g2", "g1", false), "loading");
|
|
assert.equal(recordedMediaPresentationState("ready", "g1", "g1", false), "ready");
|
|
assert.equal(recordedMediaPresentationState("ready", "g1", null, true), "waiting");
|
|
assert.equal(recordedMediaPresentationState("error", "g1", "g1", false), "error");
|
|
assert.equal(recordedMediaPresentationState("ready", "g1", "g1", false, "loading"), "loading");
|
|
assert.equal(recordedMediaPresentationState("ready", "g1", null, true, "loading"), "loading");
|
|
assert.equal(recordedMediaPresentationState("ready", "g1", "g1", false, "error"), "error");
|
|
});
|
|
|
|
test("decoded duration and seekable range cover the complete declared epoch", () => {
|
|
assert.equal(recordedMediaSeekableCoverage(20, 20, 20), true);
|
|
assert.equal(recordedMediaSeekableCoverage(19, 19, 20), true);
|
|
assert.equal(recordedMediaSeekableCoverage(18.99, 20, 20), false);
|
|
assert.equal(recordedMediaSeekableCoverage(20, 18.99, 20), false);
|
|
assert.equal(recordedMediaSeekableCoverage(20, 20, 20, 1, 1), true);
|
|
assert.equal(recordedMediaSeekableCoverage(20, 20, 20, 1, 1.01), false);
|
|
});
|
|
|
|
test("recorded player range-streams and never builds a whole-video RAM Blob", async () => {
|
|
const source = await readFile(
|
|
new URL("../src/components/RecordedFmp4Player.tsx", import.meta.url),
|
|
"utf8",
|
|
);
|
|
assert.match(source, /video\.src\s*=\s*descriptor\.streamUrl/);
|
|
assert.doesNotMatch(source, /new Blob\(|response\.arrayBuffer\(|SourceBuffer/);
|
|
});
|
|
|
|
test("loading and error overlays fully conceal recorded camera pixels", async () => {
|
|
const css = await readFile(
|
|
new URL("../src/styles/observation.css", import.meta.url),
|
|
"utf8",
|
|
);
|
|
assert.match(
|
|
css,
|
|
/\.recorded-media-player:not\(\[data-state="ready"\]\) \.observation-media__asset\s*\{[^}]*visibility:\s*hidden/s,
|
|
);
|
|
assert.match(
|
|
css,
|
|
/\.recorded-media-player__notice\s*\{[^}]*inset:\s*0;[^}]*background:\s*#070809/s,
|
|
);
|
|
});
|
|
|
|
test("point-cloud fullscreen keeps the admitted recorded camera worker mounted", async () => {
|
|
const source = await readFile(
|
|
new URL("../src/workspaces/Workspaces.tsx", import.meta.url),
|
|
"utf8",
|
|
);
|
|
assert.match(source, /\{visibleMediaSources\.map\(\(source, index\) => \(/);
|
|
assert.match(source, /hidden=\{pointCloudFocused \|\| unifiedPerception\}/);
|
|
assert.doesNotMatch(
|
|
source,
|
|
/\{!pointCloudFocused \? visibleMediaSources\.map/,
|
|
);
|
|
assert.doesNotMatch(
|
|
source,
|
|
/\(pointCloudFocused \|\| !observationLayout\.visibleSourceIds\.has\(source\.id\)\)/,
|
|
);
|
|
});
|