import { test } from "node:test"; import assert from "node:assert/strict"; import { AVAILABILITY } from "../../src/lib/preview/contracts.js"; import { buildFixtureManifest } from "../../src/lib/preview/manifest.js"; import { createInitialState, reduce, PHASE } from "../../src/lib/preview/session.js"; import { createFixtureTransport } from "../../src/lib/preview/transport.js"; import { makeNativeLease } from "../../src/lib/preview/lease.js"; const SID = "sess-t"; const T0 = 1_700_000_000_000; const SRC = { provider: "deezer", providerTrackId: "1", matchConfidence: "native" }; function openSession() { const manifest = buildFixtureManifest({ rows: [ { providerTrackId: "1", title: "One", initialAvailability: AVAILABILITY.AVAILABLE }, { providerTrackId: "2", title: "Two", initialAvailability: AVAILABILITY.AVAILABLE }, ], createdAtMs: T0, }); return reduce(createInitialState(SID), { type: "MANIFEST_READY", sessionId: SID, manifest }); } test("end-to-end against the fake transport: load → play → ticks → auto-advance", () => { let state = openSession(); const transport = createFixtureTransport((action) => { state = reduce(state, action); }); const lease = makeNativeLease({ source: SRC, opaqueLeaseId: "op-1", safeForMs: 300_000, clipDurationMs: 30_000 }); state = reduce(state, { type: "LEASE_READY", sessionId: SID, generation: state.requestGeneration, sessionKey: state.activeItemKey, clipDurationMs: 30_000, }); transport.load(lease, { sessionId: SID, generation: state.requestGeneration, sessionKey: state.activeItemKey }); transport.play(); assert.equal(state.phase, PHASE.PLAYING); transport.tick(12_000); assert.equal(state.positionMs, 12_000); transport.tick(18_000); // reaches clip end → MEDIA_ENDED → auto-advance assert.equal(state.activeManifestIndex, 1); assert.equal(state.phase, PHASE.RESOLVING_MEDIA); assert.equal(state.desiredPlaying, true); }); test("events after a step carry the old generation and are inert", () => { let state = openSession(); const transport = createFixtureTransport((action) => { state = reduce(state, action); }); const lease = makeNativeLease({ source: SRC, opaqueLeaseId: "op-1", safeForMs: 300_000 }); const genAtLoad = state.requestGeneration; transport.load(lease, { sessionId: SID, generation: genAtLoad, sessionKey: state.activeItemKey }); transport.play(); assert.equal(state.phase, PHASE.PLAYING); // user steps: the reducer bumps the generation… state = reduce(state, { type: "STEP", sessionId: SID, dir: 1 }); const after = state; // …but the un-stopped transport keeps ticking with the OLD generation transport.tick(5_000); assert.equal(state.positionMs, after.positionMs, "stale position must not land"); assert.equal(state.phase, PHASE.RESOLVING_MEDIA); }); test("invalid lease surfaces ITEM_FAILED(invalid_media) instead of playing", () => { let state = openSession(); const transport = createFixtureTransport((action) => { state = reduce(state, action); }); const bogus = { transport: "native", safeForMs: 100 }; // no opaqueLeaseId transport.load(bogus, { sessionId: SID, generation: state.requestGeneration, sessionKey: state.activeItemKey }); assert.equal(state.phase, PHASE.ITEM_FAILED); assert.equal(state.errorClass, "invalid_media"); }); test("dispose is terminal and idempotent", () => { const transport = createFixtureTransport(() => {}); transport.dispose(); transport.dispose(); assert.throws(() => transport.load({}, { sessionId: SID, generation: 1, sessionKey: "k" })); });