First public release
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { overall, validateReport } from '@extant2000/evidence-record'
|
||||
import { ambiguousAttribution, formatPane, unify, type Signal } from '../src/index.js'
|
||||
|
||||
const NOW = '2026-08-01T12:00:00Z'
|
||||
const fresh = '2026-08-01T11:58:00Z'
|
||||
const old = '2026-08-01T09:00:00Z'
|
||||
|
||||
const sig = (p: Partial<Signal> = {}): Signal => ({
|
||||
source: 'gatus',
|
||||
subject: 'crm',
|
||||
metric: 'availability',
|
||||
value: 100,
|
||||
health: 'up',
|
||||
observedAt: fresh,
|
||||
where: 'https://status.example.com',
|
||||
...p,
|
||||
})
|
||||
|
||||
describe('silence is not health', () => {
|
||||
it('a stale panel is NOT ASSESSED, not green', () => {
|
||||
// An error board can show zero events because ingestion stopped, not
|
||||
// because nothing went wrong.
|
||||
const r = unify([sig({ observedAt: old })], { now: NOW })
|
||||
expect(r.findings[0]!.determination).toBe('not-assessed')
|
||||
expect(overall(r)).toBe('not-assessed')
|
||||
const text = formatPane([sig({ observedAt: old })], { now: NOW })
|
||||
expect(text).toContain('Silence is not health')
|
||||
expect(text).toContain('showing its last reading')
|
||||
expect(text).not.toContain('✓')
|
||||
})
|
||||
|
||||
it('passes a reading inside the freshness window', () => {
|
||||
expect(overall(unify([sig()], { now: NOW }))).toBe('pass')
|
||||
})
|
||||
|
||||
it('honours a custom freshness window', () => {
|
||||
// 2 minutes old, with a 60-second window.
|
||||
expect(overall(unify([sig()], { now: NOW, maxAgeSeconds: 60 }))).toBe('not-assessed')
|
||||
})
|
||||
|
||||
it('treats an unstamped reading as indistinguishable from a stale one', () => {
|
||||
const r = unify([sig({ observedAt: undefined })], { now: NOW })
|
||||
expect(r.findings[0]!.determination).toBe('not-assessed')
|
||||
expect(r.findings[0]!.detail).toContain('may be current; nothing here establishes that')
|
||||
})
|
||||
|
||||
it('reports an expected subject with no signal at all', () => {
|
||||
const r = unify([sig({ subject: 'crm' })], { now: NOW, expectedSubjects: ['crm', 'hr', 'desk'] })
|
||||
const missing = r.findings.filter(f => f.id.endsWith('/missing'))
|
||||
expect(missing).toHaveLength(2)
|
||||
expect(missing[0]!.detail).toContain('green because nothing was ever wired to it')
|
||||
})
|
||||
})
|
||||
|
||||
describe('shared identity is not per-subject evidence', () => {
|
||||
// Several products reporting through ONE error-tracking project. A
|
||||
// per-product search returning nothing means "not separable", not "no errors".
|
||||
const shared = [
|
||||
sig({ source: 'sentry', subject: 'crm', metric: 'errors', value: 0, attributedTo: 'shared-project' }),
|
||||
sig({ source: 'sentry', subject: 'hr', metric: 'errors', value: 0, attributedTo: 'shared-project' }),
|
||||
sig({ source: 'sentry', subject: 'desk', metric: 'errors', value: 0, attributedTo: 'shared-project' }),
|
||||
]
|
||||
|
||||
it('names the entangled subjects rather than saying "ambiguous"', () => {
|
||||
const groups = ambiguousAttribution(shared)
|
||||
expect(groups).toHaveLength(1)
|
||||
expect(groups[0]!.subjects).toEqual(['crm', 'desk', 'hr'])
|
||||
})
|
||||
|
||||
it('refuses to read a shared-identity zero as a per-subject pass', () => {
|
||||
const r = unify(shared, { now: NOW })
|
||||
expect(overall(r)).toBe('not-assessed')
|
||||
for (const f of r.findings.filter(f => f.id.startsWith('crm/'))) {
|
||||
expect(f.determination).toBe('not-assessed')
|
||||
}
|
||||
})
|
||||
|
||||
it('says what a search returning nothing actually means', () => {
|
||||
const text = formatPane(shared, { now: NOW })
|
||||
expect(text).toContain('absence of attributed events is not absence of events')
|
||||
expect(text).toContain('crm, desk, hr')
|
||||
})
|
||||
|
||||
it('leaves a uniquely-attributed signal alone', () => {
|
||||
const distinct = [
|
||||
sig({ source: 'sentry', subject: 'crm', attributedTo: 'crm-prod' }),
|
||||
sig({ source: 'sentry', subject: 'hr', attributedTo: 'hr-prod' }),
|
||||
]
|
||||
expect(ambiguousAttribution(distinct)).toEqual([])
|
||||
expect(overall(unify(distinct, { now: NOW }))).toBe('pass')
|
||||
})
|
||||
|
||||
it('does not entangle the same identity across different sources', () => {
|
||||
// gatus/"prod" and sentry/"prod" are unrelated namespaces.
|
||||
const r = ambiguousAttribution([
|
||||
sig({ source: 'gatus', subject: 'crm', attributedTo: 'prod' }),
|
||||
sig({ source: 'sentry', subject: 'hr', attributedTo: 'prod' }),
|
||||
])
|
||||
expect(r).toEqual([])
|
||||
})
|
||||
})
|
||||
|
||||
describe('health states', () => {
|
||||
it('reports down as critical and degraded as high', () => {
|
||||
expect(unify([sig({ health: 'down' })], { now: NOW }).findings[0]!.severity).toBe('critical')
|
||||
expect(unify([sig({ health: 'degraded' })], { now: NOW }).findings[0]!.severity).toBe('high')
|
||||
expect(overall(unify([sig({ health: 'down' })], { now: NOW }))).toBe('fail')
|
||||
})
|
||||
|
||||
it('reports an uncomputable value as not-assessed, not zero', () => {
|
||||
const r = unify([sig({ value: null, health: undefined })], { now: NOW })
|
||||
expect(r.findings[0]!.determination).toBe('not-assessed')
|
||||
})
|
||||
|
||||
it('carries the sample count into the citation for a null value', () => {
|
||||
const text = formatPane([sig({ value: null, health: undefined })], { now: NOW, evidence: 'inline' })
|
||||
expect(text).toContain('not computable')
|
||||
expect(text).toContain('0 samples')
|
||||
})
|
||||
})
|
||||
|
||||
describe('conformance with the evidence-record standard', () => {
|
||||
it('an empty board is not-assessed, never healthy', () => {
|
||||
const r = unify([], { now: NOW })
|
||||
expect(overall(r)).toBe('not-assessed')
|
||||
expect(formatPane([], { now: NOW })).toContain('An empty board is not a healthy one')
|
||||
})
|
||||
|
||||
it('every conclusion carries a citation', () => {
|
||||
expect(validateReport(unify([sig(), sig({ subject: 'hr', health: 'down' })], { now: NOW }))).toEqual([])
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user