'use strict'; /** * THE TRUTH-LAW PROOF. * * The claim this engine makes is that a post cannot contain a number that was * not pulled. These tests are that claim, made falsifiable. If any of them can * be made to pass while a fabricated string escapes, the moat is decorative. */ const engine = require('../../src/services/content/contentEngine'); const { toSvg } = require('../../src/services/content/cardRenderer'); const base = { id: 'test_tpl', sport: 'mlb', requires: ['n'], pull: async () => ({ n: 3, name: 'Real Player' }), copy: () => 'we graded {n} props', card: () => ({ title: 'T', lines: [{ text: '{n} props', style: 'stat' }] }), }; const reg = (over = {}) => engine.registerTemplate({ ...base, ...over, id: over.id || `t_${Math.random()}` }); describe('a template CANNOT render an unbacked claim', () => { it('refuses a token with no pulled fact', async () => { // The failure this prevents: a template author writes {edge} and the engine // helpfully renders "undefined" or, worse, an empty string that reads fine. const t = reg({ copy: () => 'edge is {edge_that_was_never_pulled}%' }); const out = await engine.generate(t.id, {}); expect(out.ok).toBe(false); expect(out.skipped).toBe(true); expect(out.reason).toMatch(/TRUTH LAW: unbacked token/); }); it('refuses an unbacked token on the CARD too, not just the copy', async () => { const t = reg({ card: () => ({ title: 'T', lines: [{ text: '{ghost_stat}', style: 'stat' }] }) }); const out = await engine.generate(t.id, {}); expect(out.ok).toBe(false); expect(out.reason).toMatch(/unbacked token/); }); it('render() throws directly — the gate is not bypassable by a caller', () => { expect(() => engine.render('{nope}', { yes: 1 })).toThrow(/TRUTH LAW/); }); it('a null fact is ABSENT, not rendered as "null"', async () => { const t = reg({ pull: async () => ({ n: null }), copy: () => '{n} props' }); const out = await engine.generate(t.id, {}); expect(out.ok).toBe(false); expect(String(out.copy || '')).not.toMatch(/null/); }); it('an empty string is absent — a hole in a sentence is a lie by omission', () => { expect(engine.isPresent('')).toBe(false); expect(engine.isPresent(' ')).toBe(false); }); it('ZERO is PRESENT — "0 cleared B+" is a real and important claim', () => { // Treating 0 as missing is the Number(null) === 0 breach wearing its // opposite coat, and it would silently delete our most honest post. expect(engine.isPresent(0)).toBe(true); expect(engine.render('{n} cleared', { n: 0 })).toBe('0 cleared'); }); it('NaN and Infinity are absent — they are arithmetic failures, not facts', () => { expect(engine.isPresent(NaN)).toBe(false); expect(engine.isPresent(Infinity)).toBe(false); }); }); describe('the fact contract is checked BEFORE any string is built', () => { it('a contract gap skips with the missing field named', async () => { const t = reg({ requires: ['n', 'missing_field'] }); const out = await engine.generate(t.id, {}); expect(out.ok).toBe(false); expect(out.reason).toMatch(/fact-contract gap: missing_field/); }); it('a gap can emit an HONEST ABSENCE instead of nothing', async () => { const t = reg({ requires: ['n', 'absent_thing'], absent: () => ({ copy: 'nothing tonight, and we say so', card: { title: 'NONE' } }), }); const out = await engine.generate(t.id, {}); expect(out.ok).toBe(true); expect(out.honest_absence).toBe(true); expect(out.copy).toMatch(/nothing tonight/); }); it('a failing pull skips rather than rendering a half-post', async () => { const t = reg({ pull: async () => { throw new Error('source down'); } }); const out = await engine.generate(t.id, {}); expect(out.ok).toBe(false); expect(out.reason).toMatch(/pull failed: source down/); }); }); describe('copy and card cannot disagree', () => { it('both render from ONE fact object', async () => { const t = reg({ pull: async () => ({ n: 7 }), copy: () => 'we graded {n}', card: () => ({ title: 'T', lines: [{ text: '{n} graded', style: 'stat' }] }), }); const out = await engine.generate(t.id, {}); expect(out.copy).toMatch(/7/); expect(out.card.lines[0].text).toMatch(/7/); }); it('the card SVG contains the same pulled number', async () => { const t = reg({ pull: async () => ({ n: 42 }), copy: () => '{n}', card: () => ({ title: 'T', lines: [{ text: '{n} graded', style: 'stat' }] }) }); const out = await engine.generate(t.id, {}); expect(toSvg(out.card)).toMatch(/42 graded/); }); }); describe('the real templates obey the contract', () => { const hot = require('../../src/services/content/templates/hotHitters'); const flex = require('../../src/services/content/templates/honestyFlex'); const streak = require('../../src/services/content/templates/streakList'); it.each([[hot], [flex], [streak]])('every token in %s is declarable', (t) => { const tokens = new Set([ ...engine.tokensIn(t.copy({})), ...engine.tokensIn(JSON.stringify(t.card({}))), ]); expect(tokens.size).toBeGreaterThan(0); // Each template must ship an absent-variant, or a thin night silently // produces nothing and the flywheel stops without anyone noticing. expect(typeof t.absent).toBe('function'); }); it('hot hitters refuses a hitter with too little history', async () => { engine.registerTemplate(hot); const out = await engine.generate('hot_hitters', { date: '2026-08-07', hitterForm: async () => ([{ name: 'Rookie', season_games: 4, recent_rate: 0.9, season_rate: 0.2 }]), }); // 4 games is not a season, so nobody QUALIFIES -- which is a source problem, // not a quiet night. It must SKIP rather than publish honest-absence copy. expect(out.ok).toBe(false); expect(out.reason).toMatch(/source problem, not a quiet night/); }); it('hot hitters DOES emit honest absence when a real pool has nobody hot', async () => { // The distinction that matters: candidates existed, were judged, none hot. engine.registerTemplate(hot); const out = await engine.generate('hot_hitters', { date: '2026-08-07', hitterForm: async () => Array.from({ length: 30 }, (_, i) => ({ name: `P${i}`, season_games: 90, recent_rate: 0.30, season_rate: 0.40, })), }); expect(out.honest_absence).toBe(true); expect(out.copy).toMatch(/No hitter is meaningfully hot/); }); it('streaks refuse a run not verified from settled results', async () => { engine.registerTemplate(streak); const out = await engine.generate('streak_list', { date: '2026-08-07', settledStreaks: async () => ([{ name: 'X', streak: 9, verified_from_settled: false }]), }); expect(out.honest_absence).toBe(true); }); });