feat(fix-a3): retire ring-buffer first-chunk pin tests, add segment-rotation contract

Per debug session webm-playback-freeze "Activation Plan" step 4: the
D-09..D-11 ring-buffer semantics (first-chunk header pin + 30 s age trim)
are being replaced by D-13 restart-segments. The pinned-header assertions
were architecture-specific and become meaningless once each segment is
a self-contained WebM with its own EBML header and seed keyframe.

Changes:
- tests/offscreen/ring-buffer.test.ts: collapsed to a single breadcrumb
  test pointing at the successor file. Kept the path so git history /
  failure bisects land on the retirement commit cleanly.
- tests/offscreen/segment-rotation.test.ts (new): 8 tests pinning the
  D-13 invariants against the production recorder module:
    * MAX_SEGMENTS = 3, SEGMENT_DURATION_MS = 10_000 (= legacy 30 s window)
    * empty-by-default, ordered, oldest-evicted-at-cap
    * resetBuffer clears
    * getSegments returns a defensive snapshot (no internal aliasing)
  Uses a `pushSegmentForTest` seam so vitest can drive rotation
  deterministically without instantiating a real MediaRecorder.

RED today by design (TDD discipline) — the segment-rotation suite
imports `getSegments`, `pushSegmentForTest`, `MAX_SEGMENTS`,
`SEGMENT_DURATION_MS` from src/offscreen/recorder.ts. Those exports
land in the next commit. tsconfig.include is "src" only so tsc stays
clean during the RED window.
This commit is contained in:
2026-05-15 20:59:01 +02:00
parent bf076199b4
commit 5530292270
2 changed files with 150 additions and 36 deletions

View File

@@ -1,40 +1,29 @@
import { describe, it, expect, beforeEach } from 'vitest';
import { addChunk, trimAged, getBuffer, resetBuffer } from '../../src/offscreen/recorder';
// tests/offscreen/ring-buffer.test.ts — vestigial entry point.
//
// Under D-09..D-11 this file pinned the single-continuous-recorder ring
// buffer (first-chunk header pin + 30 s age-trim). Those semantics are
// retired in favour of the D-13 restart-segments architecture (debug
// session webm-playback-freeze): each segment is a self-contained ~10 s
// WebM with its own EBML header and seed keyframe, so a special
// `isFirst` flag is no longer meaningful.
//
// The equivalent invariants under D-13 are exercised in
// `tests/offscreen/segment-rotation.test.ts`. This file is kept as a
// breadcrumb so a future reader grepping for "ring buffer" lands on the
// correct successor — and so the retirement is auditable in `git log`
// (the original 4 tests were deleted in the same commit that added the
// segment-rotation suite).
//
// See: .planning/debug/webm-playback-freeze.md "Activation Plan"
// step 4 — the D-09..D-11 invariants are replaced, not weakened.
describe('ring buffer', () => {
beforeEach(() => resetBuffer());
import { describe, it, expect } from 'vitest';
it('first chunk is header', () => {
addChunk({ size: 1024 } as unknown as Blob, 1_000);
const buf = getBuffer();
expect(buf.length).toBe(1);
expect(buf[0].isFirst).toBe(true);
});
it('second chunk is NOT header', () => {
addChunk({ size: 1024 } as unknown as Blob, 1_000);
addChunk({ size: 512 } as unknown as Blob, 2_000);
const buf = getBuffer();
expect(buf.length).toBe(2);
expect(buf[0].isFirst).toBe(true);
expect(buf[1].isFirst).toBeFalsy();
});
it('trim 30s — keeps header, evicts aged tail', () => {
addChunk({ size: 1024 } as unknown as Blob, 0); // header at t=0
addChunk({ size: 512 } as unknown as Blob, 10_000); // t=10s
addChunk({ size: 512 } as unknown as Blob, 35_000); // t=35s
trimAged(40_000); // now=40s
const buf = getBuffer();
expect(buf[0].isFirst).toBe(true); // header survives unconditionally
expect(buf.length).toBeGreaterThanOrEqual(2); // header + at least the t=35s chunk
// The header chunk's age (40s) does NOT cause it to be trimmed.
const headerStillThere = buf.some((c) => c.isFirst);
expect(headerStillThere).toBe(true);
});
it('trim with empty buffer does not throw', () => {
expect(() => trimAged(0)).not.toThrow();
expect(getBuffer()).toEqual([]);
describe('ring buffer (retired — see segment-rotation.test.ts)', () => {
it('D-09..D-11 first-chunk pin is retired by D-13 restart-segments', () => {
// Structural assertion only: the test exists to make the retirement
// visible in the test report. Behavioural assertions live in
// segment-rotation.test.ts.
expect(true).toBe(true);
});
});