Files
blast-radius/test/blast-radius.test.ts
T
2026-09-28 14:56:05 -04:00

192 lines
6.5 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import { overall, validateReport } from '@extant2000/evidence-record'
import {
codeOnly,
toReport,
compareImplementations,
extractFunction,
fingerprint,
formatDrift,
} from '../src/index.js'
const A = `
// header comment
function evaluateSendRules(c) {
if (!c.postalAddress) return { ok: false }
return { ok: true }
}
module.exports = { evaluateSendRules }
`
// Same behaviour, different comments and formatting — must NOT be drift.
const B = `
/* different doc */
function evaluateSendRules(c) {
// a comment the other copy does not have
if (!c.postalAddress) return { ok: false }
return { ok: true } // trailing note
}
`
// A real behavioural change — the postal-address check is gone.
const C = `
function evaluateSendRules(c) {
return { ok: true }
}
`
describe('extractFunction', () => {
it('extracts a function by brace matching', () => {
expect(extractFunction(A, 'evaluateSendRules')).toContain('postalAddress')
})
it('handles nested braces without stopping early', () => {
const src = 'function f() { if (x) { g({ a: 1 }) } return 2 }'
expect(extractFunction(src, 'f')).toBe(src)
})
it('finds async and exported declarations', () => {
expect(extractFunction('async function f() { return 1 }', 'f')).toBeTruthy()
expect(extractFunction('export function f() { return 1 }', 'f')).toBeTruthy()
})
it('returns null when the function is absent', () => {
expect(extractFunction(A, 'nope')).toBeNull()
})
})
describe('codeOnly', () => {
it('ignores comments and whitespace so docs may differ', () => {
expect(codeOnly(extractFunction(A, 'evaluateSendRules')!))
.toBe(codeOnly(extractFunction(B, 'evaluateSendRules')!))
})
it('does NOT ignore a behavioural change', () => {
expect(codeOnly(extractFunction(A, 'evaluateSendRules')!))
.not.toBe(codeOnly(extractFunction(C, 'evaluateSendRules')!))
})
})
describe('compareImplementations', () => {
it('reports identical copies as in sync', () => {
const r = compareImplementations(
[{ label: 'a', source: A }, { label: 'b', source: B }],
'evaluateSendRules',
)
expect(r.inSync).toBe(true)
expect(r.groups).toHaveLength(1)
expect(r.vacuous).toBe(false)
})
it('catches the one-sided fix — the failure this exists for', () => {
// A CAN-SPAM guard removed in one repo only.
const r = compareImplementations(
[{ label: 'hosting', source: A }, { label: 'crm', source: C }],
'evaluateSendRules',
)
expect(r.inSync).toBe(false)
expect(r.groups).toHaveLength(2)
expect(formatDrift(r)).toContain('has drifted across')
})
it('groups three copies as 2-vs-1 and names which side is odd', () => {
const r = compareImplementations(
[{ label: 'a', source: A }, { label: 'b', source: B }, { label: 'c', source: C }],
'evaluateSendRules',
)
// Majority group first.
expect(r.groups[0]!.labels).toEqual(['a', 'b'])
expect(r.groups[1]!.labels).toEqual(['c'])
})
it('treats a MISSING function as not-found, never as passing', () => {
// Renamed or deleted is not "no drift" — a guard that cannot find its
// subject is not passing.
const r = compareImplementations(
[{ label: 'a', source: A }, { label: 'gone', source: 'function other() {}' }],
'evaluateSendRules',
)
expect(r.missing).toEqual(['gone'])
expect(r.inSync).toBe(false)
expect(formatDrift(r)).toContain('could not be found')
})
it('flags a single copy as NOT ASSESSED, not in sync', () => {
// One copy cannot disagree with itself; calling that a pass would let a
// deleted sibling read as clean.
const r = compareImplementations([{ label: 'a', source: A }], 'evaluateSendRules')
expect(r.vacuous).toBe(true)
const text = formatDrift(r)
expect(text).toContain('NOT ASSESSED')
expect(text).toContain('One copy cannot disagree with itself')
// The word it replaced needed a glossary.
expect(text).not.toContain('VACUOUS')
})
it('an empty input set is vacuous', () => {
expect(compareImplementations([], 'f').vacuous).toBe(true)
})
})
describe('fingerprint', () => {
it('is stable and differs on changed code', () => {
expect(fingerprint('abc')).toBe(fingerprint('abc'))
expect(fingerprint('abc')).not.toBe(fingerprint('abd'))
})
})
describe('conformance with the evidence-record standard', () => {
const A = 'function evaluateSendRules(c) {\n if (c.unsub) return false\n return true\n}'
const B = 'function evaluateSendRules(c) {\n return true\n}'
it('cites the file of every copy that disagrees', () => {
const r = compareImplementations([
{ label: 'crm', path: 'service-a/lib/send.js', source: A },
{ label: 'hosting', path: 'service-b/lib/send.js', source: B },
], 'evaluateSendRules')
const text = formatDrift(r, { evidence: 'full' })
expect(text).toContain('service-a/lib/send.js')
expect(text).toContain('service-b/lib/send.js')
expect(overall(toReport(r))).toBe('fail')
})
it('cites the copies behind an in-sync pass', () => {
// A pass that cannot name what it compared is an asserted result.
const r = compareImplementations([
{ label: 'crm', path: 'a/send.js', source: A },
{ label: 'hosting', path: 'b/send.js', source: A },
], 'evaluateSendRules')
expect(overall(toReport(r))).toBe('pass')
expect(formatDrift(r, { evidence: 'full' })).toContain('a/send.js')
})
it('every conclusion carries a citation', () => {
const r = compareImplementations([
{ label: 'crm', path: 'a/send.js', source: A },
{ label: 'hosting', path: 'b/send.js', source: B },
], 'evaluateSendRules')
expect(validateReport(toReport(r))).toEqual([])
})
})
describe('a missing subject is a finding, not an absence of one', () => {
it('stays assessed when one copy is present and one is missing', () => {
// `vacuous` counts LOCATED copies, so this pair is vacuous by that
// definition. Reporting it as NOT ASSESSED would hide a deleted or
// renamed sibling behind "nothing was compared" — the exact failure this
// library exists to catch.
const r = compareImplementations(
[
{ label: 'a', path: 'a/send.js', source: 'function evaluateSendRules(c) { return true }' },
{ label: 'gone', path: 'b/send.js', source: 'function other() {}' },
],
'evaluateSendRules',
)
expect(r.vacuous).toBe(true)
expect(overall(toReport(r))).toBe('fail')
expect(formatDrift(r)).toContain('could not be found')
expect(formatDrift(r)).not.toContain('NOT ASSESSED')
})
})