// Unit: content formatter (Session 29). const fmt = require('../../src/services/contentFormatter'); describe('contentFormatter โ€” slate thread', () => { test('formats each post role into readable text', () => { const thread = { posts: [ { role: 'hook', text: '๐Ÿ”ฅ VYNDR SLATE' }, { role: 'pick', player: 'Wemby', stat: 'points', side: 'over', line: 28.5, grade: 'A', confidence: 78, edge: 6.2, analysis: 'Edge on the line.' }, { role: 'game_highlight', game: 'ARI @ CIN', time: null, bestHomeML: { odds: '-120', book: 'betmgm' }, total: 9.5, bookCount: 2 }, { role: 'movers', movers: [{ player: 'X', stat: 'hits', opening: 1.5, current: 2.5, delta: 1, sharpSignal: false }] }, { role: 'schedule', games: [{ away: 'ATL', home: 'NYM', time: null }] }, { role: 'cta', text: 'Full slate at vyndr.app' }, ], }; const out = fmt.formatSlateThread(thread); expect(out[0]).toBe('๐Ÿ”ฅ VYNDR SLATE'); expect(out[1]).toContain('Wemby ยท points over 28.5'); expect(out[1]).toContain('Grade: A ยท 78% confidence'); expect(out[1]).toContain('Edge: +6.2%'); expect(out[2]).toContain('ARI @ CIN'); expect(out[2]).toContain('betmgm'); expect(out[3]).toContain('Biggest line moves'); expect(out[4]).toContain('ATL @ NYM'); expect(out[5]).toBe('Full slate at vyndr.app'); }); test('never emits "undefined"', () => { const out = fmt.formatSlateThread({ posts: [{ role: 'pick', player: 'X', stat: 'pts', grade: 'B' }] }); expect(out[0]).not.toMatch(/undefined/); }); test('non-thread input โ†’ empty array', () => { expect(fmt.formatSlateThread(null)).toEqual([]); }); }); describe('contentFormatter โ€” POTD + recap', () => { test('POTD full', () => { const t = fmt.formatPOTD({ dataLevel: 'full', player: 'Wemby', stat: 'points', side: 'over', line: 28.5, grade: 'A', confidence: 78 }); expect(t).toContain('PROP OF THE DAY'); expect(t).toContain('Wemby'); }); test('POTD game of the day', () => { const t = fmt.formatPOTD({ dataLevel: 'lines', subtype: 'game_of_the_day', game: 'ARI @ CIN', total: 9.5 }); expect(t).toContain('GAME OF THE DAY'); expect(t).toContain('O/U 9.5'); }); test('POTD unavailable', () => { expect(fmt.formatPOTD({ available: false })).toMatch(/vyndr\.app/); }); test('recap formats record + win rate', () => { const t = fmt.formatRecap({ available: true, date: 'Friday', record: { wins: 3, losses: 2, pushes: 1 }, winRate: 0.6, topHits: [{ player: 'A', stat: 'points', side: 'over', line: 20 }], metrics: { brierScore: 0.18 } }); expect(t).toContain('3-2-1 (60%)'); expect(t).toContain('โœ… A points over 20'); expect(t).toContain('Brier: 0.18'); }); });