Files
vyndr/tests/unit/mediaEngine.test.js
builtbykev 0e7871ac0e S4 (a1): the media engine — VOICE templates, /desk, Ghost drafts
4a VOICE v1.1 committed (board start); lint is EXECUTABLE — banned list +
   no-exclamation law enforced in the engine (throws in test, drops in
   prod) and locked by tests. Curly-apostrophe variants covered.
4b mediaEngine: deterministic templates (MORNING WIRE, SIGNAL, STREAK
   WATCH, THE SETTLE, RECEIPTS, ARCHETYPE WATCH, LINE DISPATCH) filled
   ONLY from snapshot/ledger/streaks JSON. Record percentages never render
   under n>=20 (counts + 'Record building' below). Stark layer = curated
   committed library (content/stark-lines.json), day-rotated selection —
   selected, never generated.
4c /desk (founder-only: requireAuth + DESK_OWNERS email allowlist,
   deny-by-default): all formats as text + <=280-char pre-segmented tweets
   with per-tweet copy buttons + char counts, wire/numbers-only variants,
   DATA BRIEF block (structured day numbers) with copy-for-claude.ai.
   ntfy ping after the day's first snapshot: 'Desk pack ready'.
4d ghostPublisher: DRAFTS ONLY (status:'draft' test-locked), env-gated
   no-op, HS256 JWT via node crypto (zero new deps). POST
   /api/internal/ghost/drafts saves slate preview + settle drafts.
   Nothing anywhere auto-posts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 14:31:35 -04:00

180 lines
8.2 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Session 63 (A1-S4) — the VOICE v1.1 template engine. Numbers only from
// pipeline JSON; lint enforces the banned list + no-exclamation law; record
// claims obey n≥20; Stark lines are SELECTED from the committed library.
const media = require('../../src/services/mediaEngine');
const { lintVoice, segmentThread, starkLine, morningWire, signal, theSettle, receipt, streakWatch, archetypeWatch, lineDispatch } = media;
describe('lintVoice — the banned list is law', () => {
test.each([
['This one is a lock!', 2], // "lock" + exclamation
['🔥 free money, whos tailing', 3],
['Were so back 🚀', 2],
])('%s → %i violations', (text, n) => {
expect(lintVoice(text).length).toBeGreaterThanOrEqual(n);
});
test('clean wire copy passes', () => {
expect(lintVoice('THE SETTLE — Jul 11\nA-tier: 6-2\nvyndr.app/ledger')).toEqual([]);
});
});
describe('every template output passes its own lint', () => {
const NOW = '2026-07-12T14:05:00.000Z';
const GRADE = {
player: 'Aaron Judge', stat_type: 'total_bases', line: 1.5, direction: 'over',
grade: 'A', confidence: 78, archetype: 'BOMBER',
gradedAt: { line: 1.5, odds: -120, timestamp: NOW },
};
test('morning wire: real counts, stark from the library, no bans', () => {
const text = morningWire({ dateIso: NOW, counts: { mlb: 15, wnba: 3, nba: 0 }, loudest: GRADE });
expect(text).toContain('15 MLB.');
expect(text).toContain('3 WNBA.');
expect(text).not.toContain('0 NBA'); // zero sports never listed
expect(text).toContain('Aaron Judge');
expect(text).toContain('vyndr.app');
expect(lintVoice(text)).toEqual([]);
});
test('signal: player + line + grade + ET timestamp with real odds', () => {
const text = signal(GRADE);
expect(text).toContain('Aaron Judge — TB o1.5 · A');
expect(text).toContain('BOMBER read.');
expect(text).toContain('at -120');
expect(lintVoice(text)).toEqual([]);
});
test('the settle: counts always, percentages NEVER under n≥20', () => {
const agg = { settled: 8, hits: 5, misses: 3, pushes: 0, hit_pct: null, pending: 16, by_tier: { A: { settled: 4, hits: 3, misses: 1, hit_pct: null } } };
const misses = [{ player_name: 'Aja Wilson', stat: 'points', line: 23.5, side: 'over' }];
const text = theSettle({ aggregate: agg, misses, dateIso: NOW });
expect(text).toContain('All grades: 5-3');
expect(text).toContain('A-tier: 3-1');
expect(text).not.toContain('%'); // no percentage below the gate
expect(text).toContain('Record building — 8 settled');
expect(text).toContain('Wilson PTS o23.5 ❌'); // misses BY NAME
expect(lintVoice(text)).toEqual([]);
});
test('receipt: lock beside close beside result', () => {
const row = {
player_name: 'Aaron Judge', stat: 'total_bases', line: 1.5, side: 'over', grade: 'A',
graded_at: NOW, locked_odds: '-120', closing_line: 1.5, closing_odds: '-145',
clv: 0, clv_result: 'flat', outcome: 'hit', actual_value: 2, settled_at: NOW,
};
const text = receipt(row);
expect(text).toContain('Posted');
expect(text).toContain('Judge TB o1.5 · A · -120');
expect(text).toContain('Close: o1.5 at -145');
expect(text).toContain('Result: 2 ✅');
expect(lintVoice(text)).toEqual([]);
});
test('streak watch: lens reads only, absent lens rows dropped', () => {
const rows = [
{ player: 'Brice Turang', lens: { read: '12-game on-base streak; tonight @ Pittsburgh Pirates' } },
{ player: 'No Lens Guy', lens: { read: null } },
];
const text = streakWatch(rows, NOW);
expect(text).toContain('Turang');
expect(text).not.toContain('No Lens Guy');
expect(lintVoice(text)).toEqual([]);
});
test('archetype watch needs ≥2 of a kind, else null', () => {
expect(archetypeWatch([GRADE], NOW)).toBeNull();
const two = archetypeWatch([GRADE, { ...GRADE, player: 'Shohei Ohtani' }], NOW);
expect(two).toContain('BOMBER watch: 2');
expect(lintVoice(two)).toEqual([]);
});
test('line dispatch from a real movement row', () => {
const text = lineDispatch({ ...GRADE, movement: { kind: 'steam', delta: 1, currentLine: 2.5 } }, NOW);
expect(text).toContain('o1.5 → o2.5 ▲');
expect(text).toContain('Steam.');
expect(lintVoice(text)).toEqual([]);
});
});
describe('stark layer — selected, never generated; deterministic', () => {
test('same date → same line; comes from the committed library', () => {
const a = starkLine('morning', '2026-07-12T10:00:00Z');
const b = starkLine('morning', '2026-07-12T22:00:00Z');
expect(a).toBe(b);
expect(media.__internals.STARK.morning).toContain(a);
});
test('the whole library passes lint', () => {
for (const lines of Object.values(media.__internals.STARK)) {
if (!Array.isArray(lines)) continue;
for (const l of lines) expect(lintVoice(l)).toEqual([]);
}
});
});
describe('segmentThread — ≤280 per tweet on line boundaries', () => {
test('splits long threads and preserves every line', () => {
const long = Array.from({ length: 30 }, (_, i) => `Line ${i} of the settle with some padding text`).join('\n');
const tweets = segmentThread(long);
expect(tweets.length).toBeGreaterThan(1);
for (const t of tweets) expect(t.length).toBeLessThanOrEqual(280);
expect(tweets.join('\n')).toBe(long);
});
});
describe('deskService — pack assembly from pipeline data only', () => {
const { assembleDeskPack } = require('../../src/services/deskService');
test('assembles formats + data brief from injected caches', async () => {
const NOW = '2026-07-12T14:05:00.000Z';
const store = {
'snapshot:mlb:latest': { grades: [
{ player: 'Aaron Judge', stat_type: 'total_bases', line: 1.5, direction: 'over', grade: 'A', confidence: 80, archetype: 'BOMBER', gradedAt: { line: 1.5, odds: -120, timestamp: NOW } },
{ player: 'Shohei Ohtani', stat_type: 'home_runs', line: 0.5, direction: 'over', grade: 'A', confidence: 76, archetype: 'BOMBER', gradedAt: { line: 0.5, odds: 130, timestamp: NOW } },
] },
};
const pack = await assembleDeskPack({
cacheGet: async (k) => store[k] ?? null,
loadRosterLogs: async () => [],
ledger: { getModelAggregate: async () => ({ settled: 0, hits: 0, misses: 0, pushes: 0, hit_pct: null, pending: 24 }), __internals: { isConfigured: () => false } },
fetchSettled: async () => [],
now: () => NOW,
});
expect(pack.formats.morning_wire[0].text).toContain('2 MLB.');
expect(pack.formats.signals.length).toBe(2);
expect(pack.formats.archetype_watch[0].text).toContain('BOMBER watch: 2');
// variants: wire + numbers-only when a stark line was present
expect(pack.formats.morning_wire.length).toBeGreaterThanOrEqual(1);
expect(pack.data_brief.top_signals[0].player).toBe('Aaron Judge');
expect(pack.data_brief.record.pending).toBe(24);
// every emitted text passes lint
const all = JSON.stringify(pack.formats);
expect(all).not.toContain('!');
});
});
describe('ghostPublisher — drafts only, env-gated, no deps', () => {
const ghost = require('../../src/services/ghostPublisher');
test('JWT is a valid HS256 three-parter with the key id', () => {
const t = ghost.ghostJwt('abc123:6465616462656566', 1_752_000_000);
const [h, p] = t.split('.');
const header = JSON.parse(Buffer.from(h, 'base64url').toString());
const payload = JSON.parse(Buffer.from(p, 'base64url').toString());
expect(header.kid).toBe('abc123');
expect(payload.aud).toBe('/admin/');
expect(payload.exp - payload.iat).toBe(300);
});
test('unconfigured → no-op, never a throw', async () => {
const r = await ghost.saveDraft({ title: 't', html: '<p>x</p>' });
expect(r.ok).toBe(false);
expect(r.reason).toBe('ghost not configured');
});
test('posts DRAFT status only', async () => {
let body = null;
const r = await ghost.saveDraft({ title: 't', html: '<p>x</p>' }, {
force: true, url: 'https://blog.test', adminKey: 'id:6162',
fetchImpl: async (url, opts) => { body = JSON.parse(opts.body); return { ok: true, json: async () => ({ posts: [{ id: 'p1' }] }) }; },
});
expect(r.ok).toBe(true);
expect(body.posts[0].status).toBe('draft'); // NOTHING auto-publishes
});
});