Files
slow-lane/test/slow-lane.test.ts
2026-09-28 14:49:03 -04:00

155 lines
6.0 KiB
TypeScript

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> = {}): 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')
})
})