123 lines
5.1 KiB
TypeScript
123 lines
5.1 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import { overall, validateReport } from '@extant2000/evidence-record'
|
|
import { assessCoverage, formatSuite, generateCases, type Operation } from '../src/index.js'
|
|
|
|
const listContacts: Operation = {
|
|
name: 'listContacts',
|
|
where: 'server/api/contacts/index.get.ts',
|
|
params: [
|
|
{ name: 'ids', kind: 'collection', max: 400 },
|
|
{ name: 'orgId', kind: 'identifier', required: true, tenantScoped: true },
|
|
{ name: 'limit', kind: 'number', max: 1000 },
|
|
],
|
|
}
|
|
|
|
const createSubscription: Operation = {
|
|
name: 'createSubscription',
|
|
where: 'server/api/billing/subscribe.post.ts',
|
|
idempotent: true,
|
|
mutates: true,
|
|
params: [{ name: 'priceId', kind: 'identifier', required: true }],
|
|
}
|
|
|
|
const ids = (op: Operation) => generateCases([op]).map(c => c.id)
|
|
|
|
describe('the cases come from real defect classes', () => {
|
|
it('derives the empty-collection case — the most productive input in the set', () => {
|
|
// [].every(...) is true, so an empty collection satisfies every
|
|
// universal assertion made about it.
|
|
const c = generateCases([listContacts]).find(c => c.id === 'listContacts/ids/empty')!
|
|
expect(c.expectation).toContain('never a pass')
|
|
expect(c.rationale).toContain('[].every(...) is true')
|
|
expect(c.severity).toBe('high')
|
|
})
|
|
|
|
it('derives the boundary+1 case from a documented maximum', () => {
|
|
const c = generateCases([listContacts]).find(c => c.id === 'listContacts/ids/over-max')!
|
|
expect(c.input).toContain('401 items')
|
|
expect(c.expectation).toContain('never silently truncates')
|
|
})
|
|
|
|
it('derives the wrong-tenant case only for tenant-scoped identifiers', () => {
|
|
expect(ids(listContacts)).toContain('listContacts/orgId/wrong-tenant')
|
|
expect(ids(createSubscription)).not.toContain('createSubscription/priceId/wrong-tenant')
|
|
})
|
|
|
|
it('rates the wrong-tenant case as abuse, the highest severity', () => {
|
|
const c = generateCases([listContacts]).find(c => c.id === 'listContacts/orgId/wrong-tenant')!
|
|
expect(c.category).toBe('abuse')
|
|
expect(c.severity).toBe('critical')
|
|
expect(c.expectation).toContain('indistinguishable from the not-found case')
|
|
})
|
|
|
|
it('derives the null-vs-zero case for numbers', () => {
|
|
const c = generateCases([listContacts]).find(c => c.id === 'listContacts/limit/zero')!
|
|
expect(c.expectation).toContain('Zero samples is not 100%')
|
|
})
|
|
|
|
it('derives the absent-optional case — the silently dropped expansion', () => {
|
|
const c = generateCases([listContacts]).find(c => c.id === 'listContacts/ids/absent')!
|
|
expect(c.rationale).toContain('silently dropped a requested expansion')
|
|
})
|
|
|
|
it('derives replay cases only for idempotent operations', () => {
|
|
expect(ids(createSubscription)).toContain('createSubscription/replay/identical')
|
|
expect(ids(createSubscription)).toContain('createSubscription/replay/changed-params')
|
|
expect(ids(listContacts)).not.toContain('listContacts/replay/identical')
|
|
})
|
|
|
|
it('derives concurrency and partial-failure cases only for mutating operations', () => {
|
|
expect(ids(createSubscription)).toContain('createSubscription/concurrent/double-submit')
|
|
expect(ids(createSubscription)).toContain('createSubscription/partial-failure')
|
|
expect(ids(listContacts)).not.toContain('listContacts/concurrent/double-submit')
|
|
})
|
|
|
|
it('writes expectations as requirements, not hopes', () => {
|
|
for (const c of generateCases([listContacts, createSubscription])) {
|
|
expect(c.expectation.length).toBeGreaterThan(10)
|
|
expect(c.rationale.length).toBeGreaterThan(10)
|
|
}
|
|
})
|
|
})
|
|
|
|
describe('an uncovered case is an absence of a check, not a failure', () => {
|
|
it('reports uncovered cases as not-assessed, never fail', () => {
|
|
const r = assessCoverage([listContacts])
|
|
expect(r.findings.every(f => f.determination === 'not-assessed')).toBe(true)
|
|
expect(overall(r)).toBe('not-assessed')
|
|
expect(formatSuite([listContacts])).toContain('cannot distinguish from working')
|
|
})
|
|
|
|
it('passes a case that has a test', () => {
|
|
const all = generateCases([listContacts]).map(c => c.id)
|
|
const r = assessCoverage([listContacts], { covered: all })
|
|
expect(overall(r)).toBe('pass')
|
|
})
|
|
|
|
it('counts the gap', () => {
|
|
const all = generateCases([listContacts]).map(c => c.id)
|
|
const text = formatSuite([listContacts], { covered: all.slice(0, 2) })
|
|
expect(text).toContain(`2 of ${all.length} implied cases covered`)
|
|
})
|
|
|
|
it('cites the operation definition on every case', () => {
|
|
expect(formatSuite([listContacts], { covered: [] }, { evidence: 'full' }))
|
|
.toContain('server/api/contacts/index.get.ts')
|
|
})
|
|
})
|
|
|
|
describe('conformance with the evidence-record standard', () => {
|
|
it('no operations described is not-assessed, never clean', () => {
|
|
expect(overall(assessCoverage([]))).toBe('not-assessed')
|
|
})
|
|
|
|
it('every conclusion carries a citation', () => {
|
|
expect(validateReport(assessCoverage([listContacts, createSubscription]))).toEqual([])
|
|
})
|
|
|
|
it('produces unique case ids', () => {
|
|
const all = generateCases([listContacts, createSubscription]).map(c => c.id)
|
|
expect(new Set(all).size).toBe(all.length)
|
|
})
|
|
})
|