test(01-02): add RED ring-buffer tests
Four RED tests pin D-10 (header pinning) and D-11 (30s trim) contracts:
- 'first chunk is header' — isFirst marker on first addChunk
- 'second chunk is NOT header' — only the first is pinned
- 'trim 30s — keeps header, evicts aged tail' — header survives indefinitely
- 'trim with empty buffer does not throw' — defensive edge case
Plan 03 must export {addChunk, trimAged, getBuffer, resetBuffer} from
src/offscreen/recorder.ts to flip these to GREEN.
Also stages tests/fixtures/.gitkeep so the fixture dir survives clean
checkouts (Plan 07 drops a known-good last_30sec.webm into it after the
manual smoke test).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
0
tests/fixtures/.gitkeep
vendored
Normal file
0
tests/fixtures/.gitkeep
vendored
Normal file
40
tests/offscreen/ring-buffer.test.ts
Normal file
40
tests/offscreen/ring-buffer.test.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import { addChunk, trimAged, getBuffer, resetBuffer } from '../../src/offscreen/recorder';
|
||||
|
||||
describe('ring buffer', () => {
|
||||
beforeEach(() => resetBuffer());
|
||||
|
||||
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([]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user