import { describe, expect, it } from 'vitest' import { overall, validateReport } from '@extant2000/evidence-record' import { assessStream, formatStream, keyVerdict, transit, type LinkPolicy, type Message, } from '../src/index.js' const msg = (p: Partial = {}): Message => ({ id: 'm1', sender: 'probe-a', sequence: 1, originAt: '2026-08-01T10:00:00Z', receivedAt: '2026-08-01T10:20:00Z', keyId: 'k1', where: 'downlink.log', ...p, }) const policy: LinkPolicy = { keys: [{ id: 'k1' }] } describe('expiry has two ends and one check cannot express both', () => { const expiring: LinkPolicy = { keys: [{ id: 'k1', notAfter: '2026-08-01T10:10:00Z' }] } it('calls a key that expired IN FLIGHT its own thing', () => { // Sent 10:00 (valid), arrived 10:20 (expired at 10:10). expect(keyVerdict(msg(), expiring)).toBe('expired-in-flight') }) it('ACCEPTS it, because rejecting would drop legitimate traffic', () => { const { apply, report } = assessStream([msg()], expiring) expect(apply).toHaveLength(1) const f = report.findings.find(x => x.id.endsWith('/key-flight'))! expect(f.determination).toBe('not-applicable') expect(f.detail).toContain('rejecting it would drop legitimate traffic') }) it('still records it, because the key is no longer trusted for anything new', () => { expect(formatStream([msg()], expiring)).toContain('expired while in flight') }) it('rejects a key that was already invalid at origin', () => { const late = msg({ originAt: '2026-08-01T10:15:00Z', receivedAt: '2026-08-01T10:30:00Z' }) expect(keyVerdict(late, expiring)).toBe('invalid-at-origin') expect(assessStream([late], expiring).reject).toHaveLength(1) }) it('revocation ignores origin time entirely', () => { // The key was usually compromised before anyone noticed. const revoked: LinkPolicy = { keys: [{ id: 'k1', revokedAt: '2026-08-01T23:00:00Z' }] } expect(keyVerdict(msg(), revoked)).toBe('revoked') const r = assessStream([msg()], revoked) expect(r.reject).toHaveLength(1) expect(r.report.findings[0]!.detail).toContain('Revocation is not a schedule') }) it('rejects an unrecognised or absent key as unassessed', () => { expect(keyVerdict(msg({ keyId: undefined }), policy)).toBe('unknown') expect(keyVerdict(msg({ keyId: 'nope' }), policy)).toBe('unknown') const r = assessStream([msg({ keyId: undefined })], policy) expect(r.report.findings[0]!.determination).toBe('not-assessed') expect(r.reject).toHaveLength(1) }) }) describe('ordering is by sender sequence, never by arrival', () => { it('rejects an older message that arrived after a newer one', () => { // Applying in arrival order would overwrite newer state with older, // and would look like it worked. const out = [ msg({ id: 'm2', sequence: 2, originAt: '2026-08-01T10:05:00Z', receivedAt: '2026-08-01T10:10:00Z' }), msg({ id: 'm1', sequence: 1 }), ] const r = assessStream(out, policy) // Sequence 1 sorts first, so 2 is applied and there is no regression. expect(r.apply.map(m => m.sequence)).toEqual([1, 2]) }) it('returns apply order by sequence even when the input is shuffled', () => { const shuffled = [ msg({ id: 'c', sequence: 3 }), msg({ id: 'a', sequence: 1 }), msg({ id: 'b', sequence: 2 }), ] expect(assessStream(shuffled, policy).apply.map(m => m.sequence)).toEqual([1, 2, 3]) }) it('tracks sequences per sender, not globally', () => { const two = [ msg({ id: 'a', sender: 'probe-a', sequence: 9 }), msg({ id: 'b', sender: 'probe-b', sequence: 1 }), ] expect(assessStream(two, policy).reject).toHaveLength(0) }) }) describe('a duplicate is the delivery guarantee working', () => { const dup = [msg({ id: 'a', sequence: 1 }), msg({ id: 'b', sequence: 1 })] it('fails a duplicate when the receiver is not idempotent', () => { const r = assessStream(dup, policy) const f = r.report.findings.find(x => x.id.endsWith('/duplicate'))! expect(f.determination).toBe('fail') expect(f.detail).toContain('the assumption of exactly-once is') }) it('accepts it when the receiver declares idempotency', () => { const r = assessStream(dup, { ...policy, receiverIsIdempotent: true }) const f = r.report.findings.find(x => x.id.endsWith('/duplicate'))! expect(f.determination).toBe('not-applicable') expect(r.reject).toHaveLength(0) }) it('accepts it when the message itself is idempotent', () => { const r = assessStream([msg({ id: 'a', idempotent: true }), msg({ id: 'b', idempotent: true })], policy) expect(r.reject).toHaveLength(0) }) }) describe('transit', () => { it('rejects a message too old to act on, and says to record it anyway', () => { const r = assessStream([msg()], { ...policy, maxTransitSeconds: 300 }) const f = r.report.findings.find(x => x.id.endsWith('/stale'))! expect(f.determination).toBe('fail') expect(f.detail).toContain('Record it; do not act on it') }) it('flags impossible clocks as unassessed, not as a fast message', () => { const back = msg({ originAt: '2026-08-01T10:20:00Z', receivedAt: '2026-08-01T10:00:00Z' }) expect(transit(back).impossible).toBe(true) const r = assessStream([back], policy) expect(r.report.findings[0]!.determination).toBe('not-assessed') expect(r.report.findings[0]!.detail).toContain('neither transit time nor any expiry decision') }) }) describe('conformance with the evidence-record standard', () => { it('an empty stream is not-assessed, and says what silence means', () => { const r = assessStream([]) expect(overall(r.report)).toBe('not-assessed') expect(formatStream([])).toContain('silence is not evidence that nothing was sent') }) it('every conclusion carries a citation', () => { expect(validateReport(assessStream([msg(), msg({ id: 'b', sequence: 2 })], policy).report)).toEqual([]) }) it('passes a clean stream', () => { expect(overall(assessStream([msg()], policy).report)).toBe('pass') }) })