import { describe, expect, it } from 'bun:test'; import type { Task } from '@/lib/types'; import { buildTaskNotification } from '@/lib/server/task-notifications'; function baseTask(overrides: Partial> = {}): Omit { return { id: 'task-1', user_id: 'user-1', task_type: 'sync_filings', status: 'running', stage: 'sync.extract_taxonomy', stage_detail: 'Extracting XBRL taxonomy for 0000320193-26-000001', stage_context: { progress: { current: 2, total: 5, unit: 'filings' }, counters: { hydrated: 1, failed: 0 }, subject: { ticker: 'AAPL', accessionNumber: '0000320193-26-000001' } }, resource_key: 'sync_filings:AAPL', notification_read_at: null, notification_silenced_at: null, priority: 50, payload: { ticker: 'AAPL', limit: 20 }, result: null, error: null, attempts: 1, max_attempts: 3, workflow_run_id: 'run-1', created_at: '2026-03-09T10:00:00.000Z', updated_at: '2026-03-09T10:05:00.000Z', finished_at: null, ...overrides }; } describe('task notification builder', () => { it('builds progress-driven notifications for running sync jobs', () => { const notification = buildTaskNotification(baseTask()); expect(notification.title).toBe('Filing sync'); expect(notification.statusLine).toContain('Running'); expect(notification.progress?.percent).toBe(40); expect(notification.stats.some((stat) => stat.label === 'Hydrated' && stat.value === '1')).toBe(true); expect(notification.actions[0]).toMatchObject({ id: 'open_filings', primary: true, href: '/filings?ticker=AAPL' }); }); it('builds report actions for completed analyze jobs', () => { const notification = buildTaskNotification(baseTask({ task_type: 'analyze_filing', status: 'completed', stage: 'completed', stage_detail: 'Analysis report generated for AAPL 10-Q 0000320193-26-000001.', stage_context: { subject: { ticker: 'AAPL', accessionNumber: '0000320193-26-000001', label: '10-Q' } }, payload: { accessionNumber: '0000320193-26-000001' }, result: { ticker: 'AAPL', accessionNumber: '0000320193-26-000001', filingType: '10-Q', model: 'test-model' }, finished_at: '2026-03-09T10:06:00.000Z' })); expect(notification.tone).toBe('success'); expect(notification.actions[0]).toMatchObject({ id: 'open_analysis_report', label: 'Open summary', primary: true }); expect(notification.actions[0]?.href).toContain('/analysis/reports/AAPL/0000320193-26-000001'); expect(notification.stats.some((stat) => stat.label === 'Form' && stat.value === '10-Q')).toBe(true); }); it('keeps filings navigation available for failed analyze jobs', () => { const notification = buildTaskNotification(baseTask({ task_type: 'analyze_filing', status: 'failed', stage: 'analyze.fetch_document', stage_detail: 'Could not load the primary filing document.', error: 'Could not load the primary filing document for AAPL ยท 0000320193-26-000001. Retry the job after confirming the SEC source is reachable.', stage_context: { subject: { ticker: 'AAPL', accessionNumber: '0000320193-26-000001' } }, payload: { accessionNumber: '0000320193-26-000001' }, result: null, finished_at: '2026-03-09T10:06:00.000Z' })); expect(notification.tone).toBe('error'); expect(notification.statusLine).toBe('Failed during fetch primary document'); expect(notification.detailLine).toBe('Could not load the primary filing document.'); expect(notification.actions.some((action) => action.id === 'open_filings')).toBe(true); }); });