Files
KeelrLeek 8ede6a8adf Phase 1: preview contracts and pure session core, 61 tests
src/lib/preview/ — framework-free ESM modules, per design §12–14:
- contracts.js: versioned manifest/item/source-ref factories; signed
  URLs structurally excluded from manifests; Apple identity first-class.
- lease.js: discriminated MediaLease union; computeSafeForMs derives
  lifetimes provider-clock-first with fallback/contradiction handling
  (margins are ADRs backed by Phase 0 soak evidence).
- redact.js: URL redaction (host+hash+exp only) and the HTTPS + exact
  host allowlist gate applied to initial URLs and every redirect.
- manifest.js: artist (rank order, exact-id dedupe), album (disc/track
  sort, context inheritance) and fixture builders — pure, no fetching.
- session.js: reducer with session/generation guards on every async
  action; keyed active item with derived playable indices/cursor;
  auto-advance under fresh generations; close is terminal.
- resolver.js: PreviewResolver contract + scripted fixture resolver
  with classified failures (stale_lease/not_found/network/…).
- transport.js: transport seam + deterministic fake (manual ticks).
- focus.js: revision gateway (all main-player entry points; focus-
  internal tag) and the coordinator — acquisition only via a
  generation-bound silence ack, restore only with revisions unchanged.

tests/preview/*.test.mjs run via `npm test` (node --test): 61 passing,
covering the full Phase 1 list in IMPLEMENTATION-PLAN §6 including the
exit gate: stale lease/play events are inert, no restoration after any
simulated main command, and no component renders anywhere.
2026-07-22 18:29:03 -05:00

125 lines
5.0 KiB
JavaScript

import { test } from "node:test";
import assert from "node:assert/strict";
import {
computeSafeForMs,
makeNativeLease,
makeWebviewLease,
isValidLease,
LEASE_FALLBACK_SAFE_MS,
LEASE_SAFETY_MARGIN_MS,
LEASE_MAX_SAFE_MS,
} from "../../src/lib/preview/lease.js";
import { redactUrl, parseSignedExpiryMs, isAllowedMediaUrl } from "../../src/lib/preview/redact.js";
const SRC = Object.freeze({ provider: "deezer", providerTrackId: "1", matchConfidence: "native" });
const NOW = 1_784_760_000_000;
const MIN = 60_000;
// ---------------------------------------------------------- safeForMs -----
test("valid exp + Date: provider-clock lifetime minus margin", () => {
const safe = computeSafeForMs({ expEpochMs: NOW + 14 * MIN, dateHeaderMs: NOW, localNowMs: NOW });
assert.equal(safe, 14 * MIN - LEASE_SAFETY_MARGIN_MS);
});
test("local clock AHEAD of provider: the shorter local view wins", () => {
const safe = computeSafeForMs({
expEpochMs: NOW + 14 * MIN,
dateHeaderMs: NOW,
localNowMs: NOW + 10 * MIN, // local thinks only 4 min remain
});
assert.equal(safe, 4 * MIN - LEASE_SAFETY_MARGIN_MS);
});
test("local clock BEHIND provider: local view cannot extend the lease", () => {
const safe = computeSafeForMs({
expEpochMs: NOW + 14 * MIN,
dateHeaderMs: NOW,
localNowMs: NOW - 60 * MIN, // local view claims 74 min
});
assert.equal(safe, 14 * MIN - LEASE_SAFETY_MARGIN_MS);
});
test("missing exp → short capped fallback", () => {
assert.equal(computeSafeForMs({ expEpochMs: null, dateHeaderMs: NOW, localNowMs: NOW }), LEASE_FALLBACK_SAFE_MS);
});
test("malformed exp → fallback", () => {
assert.equal(computeSafeForMs({ expEpochMs: NaN, dateHeaderMs: NOW, localNowMs: NOW }), LEASE_FALLBACK_SAFE_MS);
});
test("contradictory: exp before Date → fallback", () => {
assert.equal(
computeSafeForMs({ expEpochMs: NOW - MIN, dateHeaderMs: NOW, localNowMs: NOW }),
LEASE_FALLBACK_SAFE_MS
);
});
test("contradictory: absurd multi-hour lifetime → fallback", () => {
assert.equal(
computeSafeForMs({ expEpochMs: NOW + 6 * 60 * MIN, dateHeaderMs: NOW, localNowMs: NOW }),
LEASE_FALLBACK_SAFE_MS
);
});
test("missing Date but sane local view → local view minus margin, capped", () => {
const safe = computeSafeForMs({ expEpochMs: NOW + 14 * MIN, dateHeaderMs: null, localNowMs: NOW });
assert.equal(safe, 14 * MIN - LEASE_SAFETY_MARGIN_MS);
const capped = computeSafeForMs({ expEpochMs: NOW + 90 * MIN, dateHeaderMs: null, localNowMs: NOW });
assert.equal(capped, LEASE_MAX_SAFE_MS - LEASE_SAFETY_MARGIN_MS);
});
test("already expired with no Date → fallback (refresh immediately-ish, never negative)", () => {
assert.equal(
computeSafeForMs({ expEpochMs: NOW - MIN, dateHeaderMs: null, localNowMs: NOW }),
LEASE_FALLBACK_SAFE_MS
);
});
// ------------------------------------------------------ union validity ----
test("discriminated union: native carries exactly an opaque id", () => {
const lease = makeNativeLease({ source: SRC, opaqueLeaseId: "op-1", safeForMs: 1000 });
assert.ok(isValidLease(lease));
assert.equal(lease.url, undefined);
assert.ok(!isValidLease({ ...lease, url: "https://x" }));
});
test("discriminated union: webview carries exactly a url", () => {
const lease = makeWebviewLease({ source: SRC, url: "https://cdnt-preview.dzcdn.net/a.mp3", safeForMs: 1000 });
assert.ok(isValidLease(lease));
assert.equal(lease.opaqueLeaseId, undefined);
assert.ok(!isValidLease({ ...lease, opaqueLeaseId: "x" }));
});
test("constructors refuse the wrong locator", () => {
assert.throws(() => makeNativeLease({ source: SRC, safeForMs: 1 }));
assert.throws(() => makeWebviewLease({ source: SRC, safeForMs: 1 }));
});
// ------------------------------------------------------------ redaction ---
test("redactUrl yields host + hash + exp, never the url", () => {
const url = "https://cdnt-preview.dzcdn.net/api/x.mp3?hdnea=exp=1784761537~acl=/x*~hmac=deadbeef";
const r = redactUrl(url);
assert.equal(r.host, "cdnt-preview.dzcdn.net");
assert.equal(r.expMs, 1784761537000);
assert.match(r.hash, /^[0-9a-f]{8}$/);
assert.ok(!JSON.stringify(r).includes("hmac"));
});
test("parseSignedExpiryMs handles bare exp params and garbage", () => {
assert.equal(parseSignedExpiryMs("https://h/x?exp=1784761537"), 1784761537000);
assert.equal(parseSignedExpiryMs("https://h/x"), null);
assert.equal(parseSignedExpiryMs(null), null);
});
test("host policy: https + exact allowlist, redirects re-checked by the same gate", () => {
assert.ok(isAllowedMediaUrl("https://cdnt-preview.dzcdn.net/a.mp3"));
assert.ok(!isAllowedMediaUrl("http://cdnt-preview.dzcdn.net/a.mp3")); // scheme
assert.ok(!isAllowedMediaUrl("https://evil.dzcdn.net/a.mp3")); // host
assert.ok(!isAllowedMediaUrl("https://cdnt-preview.dzcdn.net.evil.com/a.mp3")); // suffix trick
assert.ok(!isAllowedMediaUrl("not a url"));
});