import { describe, expect, it } from 'vitest'; import { aggregateSummary, createAssetResult, strongestEvidence, verdictFromEvidence } from '../src/shared/status'; import type { Evidence } from '../src/shared/types'; const evidence = (overrides: Partial): Evidence => ({ id: crypto.randomUUID(), kind: 'c2pa', strength: 'none', verdict: 'unknown', title: 'Evidence', detail: 'Test evidence', ...overrides }); describe('evidence precedence', () => { it('treats invalid credentials as stronger than positive evidence', () => { const items = [ evidence({ strength: 'verified', verdict: 'ai' }), evidence({ strength: 'invalid', verdict: 'invalid' }) ]; expect(strongestEvidence(items)).toBe('invalid'); expect(verdictFromEvidence(items)).toBe('invalid'); }); it('never turns empty evidence into a human-origin assertion', () => { const result = createAssetResult('one', 'image', 'Unsigned image', []); expect(result.verdict).toBe('unknown'); expect(result.strength).toBe('none'); }); }); describe('page aggregation', () => { const unknownPage = createAssetResult('page', 'page', 'Page', []); it('reports verified AI when a trusted AI credential is present', () => { const image = createAssetResult('image', 'image', 'Image', [ evidence({ strength: 'verified', verdict: 'ai' }) ]); expect(aggregateSummary(unknownPage, [image], [], 1, 0).status).toBe('verified-ai'); }); it('reports mixed provenance when AI and non-AI assertions coexist', () => { const ai = createAssetResult('ai', 'image', 'AI', [evidence({ strength: 'verified', verdict: 'ai' })]); const camera = createAssetResult('camera', 'image', 'Camera', [ evidence({ strength: 'verified', verdict: 'not-ai-asserted' }) ]); expect(aggregateSummary(unknownPage, [ai, camera], [], 2, 0).status).toBe('mixed-provenance'); }); it('prioritises invalid credentials', () => { const invalid = createAssetResult('bad', 'image', 'Invalid', [ evidence({ strength: 'invalid', verdict: 'invalid' }) ]); expect(aggregateSummary(unknownPage, [invalid], [], 1, 0).status).toBe('invalid-credential'); }); it('reports suspicion without asserting AI authorship when heuristic clues cross the threshold', () => { const suspectedPage = createAssetResult('page', 'page', 'Page', [ evidence({ kind: 'heuristic', strength: 'clue', category: 'visual', score: 3 }), evidence({ kind: 'heuristic', strength: 'clue', category: 'layout', score: 2 }) ]); const summary = aggregateSummary(suspectedPage, [], [], 0, 0, 6); expect(summary.status).toBe('heuristic-suspected'); expect(suspectedPage.verdict).toBe('unknown'); expect(summary.heuristicScore).toBe(6); }); it('does not elevate a single weak style clue', () => { const page = createAssetResult('page', 'page', 'Page', [ evidence({ kind: 'heuristic', strength: 'clue', category: 'visual', score: 2 }) ]); expect(aggregateSummary(page, [], [], 0, 0, 6).status).toBe('no-supported-evidence'); }); });