fix(lab): make recorded replay seek-safe
This commit is contained in:
@@ -14,6 +14,7 @@ let selectM4ThreatTimelineSequence;
|
||||
let advanceRecordedEvidencePlayback;
|
||||
let synchronizeRecordedEvidencePlayback;
|
||||
let m4ThreatChunkWindowStarts;
|
||||
let cancelM4ThreatChunkRequestsOutsideWindow;
|
||||
let buildM4LocalSurface;
|
||||
|
||||
const resultId = `m4-threat-replay-${"a".repeat(64)}`;
|
||||
@@ -38,7 +39,10 @@ before(async () => {
|
||||
} = await server.ssrLoadModule(
|
||||
"/src/components/laboratory/useRecordedEvidencePlayback.ts",
|
||||
));
|
||||
({ m4ThreatChunkWindowStarts } = await server.ssrLoadModule(
|
||||
({
|
||||
m4ThreatChunkWindowStarts,
|
||||
cancelM4ThreatChunkRequestsOutsideWindow,
|
||||
} = await server.ssrLoadModule(
|
||||
"/src/workspaces/laboratory/useM4ThreatTimeline.ts",
|
||||
));
|
||||
({ buildM4LocalSurface } = await server.ssrLoadModule(
|
||||
@@ -375,6 +379,23 @@ test("M4.6 spatial buffering keeps previous, active and two future chunks", () =
|
||||
assert.deepEqual(m4ThreatChunkWindowStarts(0, 24, 4489), [0, 24, 48]);
|
||||
});
|
||||
|
||||
test("M4.6 spatial buffering drops stale in-flight windows across rapid jumps", () => {
|
||||
const aborted = [];
|
||||
const controller = (start) => ({ abort: () => aborted.push(start) });
|
||||
const inFlight = new Map(
|
||||
m4ThreatChunkWindowStarts(0, 24, 4489).map((start) => [start, controller(start)]),
|
||||
);
|
||||
for (const activeStart of [1488, 4488]) {
|
||||
const desired = m4ThreatChunkWindowStarts(activeStart, 24, 4489);
|
||||
cancelM4ThreatChunkRequestsOutsideWindow(inFlight, desired);
|
||||
for (const start of desired) {
|
||||
if (!inFlight.has(start)) inFlight.set(start, controller(start));
|
||||
}
|
||||
}
|
||||
assert.deepEqual([...inFlight.keys()], [4464, 4488]);
|
||||
assert.deepEqual(aborted, [0, 24, 48, 1464, 1488, 1512, 1536]);
|
||||
});
|
||||
|
||||
test("recorded evidence clock advances by selected rate and stops at the sealed end", () => {
|
||||
const range = { startSeconds: 10, endSeconds: 20 };
|
||||
assert.deepEqual(
|
||||
@@ -448,6 +469,7 @@ test("M4.6 viewer keeps media and spatial panes on one playback clock", async ()
|
||||
assert.match(visual, /const spatialFrame = frame\?\.spatialAvailable/);
|
||||
assert.match(visual, /<ObservationTimeline/);
|
||||
assert.match(visual, /useM4ThreatTimelineFrame/);
|
||||
assert.match(visual, /resolveObservationSessionReplay\(timeline\.recordedSourceSessionId/);
|
||||
assert.match(visual, /label: "VIDEO"/);
|
||||
assert.match(visual, /label: "CAMERA"/);
|
||||
assert.match(visual, /label: "3D"/);
|
||||
@@ -463,11 +485,11 @@ test("M4.6 viewer keeps media and spatial panes on one playback clock", async ()
|
||||
assert.match(visual, /secondaryMode=\{\{/);
|
||||
assert.match(visual, /playback=\{playbackController\.playback\}/);
|
||||
assert.match(visual, /clock: mediaMode === "video" \? "external" : "animation"/);
|
||||
assert.match(visual, /segmentSequence=\{frame \? frame\.sequence \+ 1 : undefined\}/);
|
||||
assert.match(visual, /timelineFrame\.activeSequence \+ 1/);
|
||||
assert.match(visual, /segmentCount=\{timeline\.frameCount\}/);
|
||||
assert.match(visual, /onPlaybackChange=\{playbackController\.synchronize\}/);
|
||||
assert.match(visual, /onPlayingRejected=\{\(\) => playbackController\.setPlaying\(false\)\}/);
|
||||
assert.match(visual, /currentSeconds: playbackController\.playback\.currentSeconds/);
|
||||
assert.doesNotMatch(visual, /setPlaying\(false\)/);
|
||||
assert.match(visualCss, /m4-replay-threat-visual__deck > \.nodedc-split-pane/);
|
||||
assert.match(visualCss, /m4-replay-threat-visual__pane-toolbar\[data-pane-toolbar="media"\]/);
|
||||
assert.match(visualCss, /m4-replay-threat-visual__pane-toolbar\[data-pane-toolbar="spatial"\]/);
|
||||
|
||||
Reference in New Issue
Block a user