157 lines
6.8 KiB
TypeScript
157 lines
6.8 KiB
TypeScript
/**
|
|
* @module tests/unit/utils/indexReadiness
|
|
* @description Pins for the read-gate authority, {@link assessProviderHealth}, and
|
|
* its older sibling {@link assessIndexReadiness}. The health-gate law: a provider's
|
|
* NAMED, synchronous, O(1) health report — when exposed — REPLACES the `isReady()`/
|
|
* size-heuristic fallback as the read gate's source of truth. A throw from
|
|
* `healthReport()` is a CONTRACT VIOLATION (never read as healthy, never swallowed
|
|
* into "unknown"); an `unledgered` family is UNKNOWN (never healthy, never broken —
|
|
* `serving` is always the provider's own verdict, verbatim).
|
|
*/
|
|
import { describe, it, expect } from 'vitest'
|
|
import { assessIndexReadiness, assessProviderHealth } from '../../../src/utils/indexReadiness.js'
|
|
import type { HealthReport, LedgerInvariantResult } from '../../../src/plugin.js'
|
|
|
|
function invariant(overrides: Partial<LedgerInvariantResult> = {}): LedgerInvariantResult {
|
|
return {
|
|
name: 'manifest-residency',
|
|
holds: true,
|
|
detail: 'ok',
|
|
heal: 'none',
|
|
source: 'ledger',
|
|
...overrides
|
|
}
|
|
}
|
|
|
|
function report(overrides: Partial<HealthReport> = {}): HealthReport {
|
|
return {
|
|
provider: 'vector',
|
|
healthy: true,
|
|
serving: true,
|
|
invariants: [],
|
|
// A FIXED stamp, never Date.now(): the pin at :98 compares two
|
|
// independently-built reports, and a live clock made them differ by 1ms
|
|
// whenever the millisecond ticked between the two calls — a plant-lane
|
|
// red that had nothing to do with the code under test.
|
|
checkedAt: 1_700_000_000_000,
|
|
durationMs: 1,
|
|
generation: 1,
|
|
unledgered: [],
|
|
...overrides
|
|
}
|
|
}
|
|
|
|
describe('assessIndexReadiness (legacy isReady() classifier)', () => {
|
|
it('unknown when the provider is null/undefined', () => {
|
|
expect(assessIndexReadiness(null)).toBe('unknown')
|
|
expect(assessIndexReadiness(undefined)).toBe('unknown')
|
|
})
|
|
|
|
it('unknown when isReady() is absent', () => {
|
|
expect(assessIndexReadiness({})).toBe('unknown')
|
|
})
|
|
|
|
it('ready / not-ready mirror isReady()', () => {
|
|
expect(assessIndexReadiness({ isReady: () => true })).toBe('ready')
|
|
expect(assessIndexReadiness({ isReady: () => false })).toBe('not-ready')
|
|
})
|
|
})
|
|
|
|
describe('assessProviderHealth — the read-gate authority', () => {
|
|
it('via "none": no provider at all', () => {
|
|
const a = assessProviderHealth(null)
|
|
expect(a.via).toBe('none')
|
|
expect(a.readiness).toBe('unknown')
|
|
expect(a.report).toBeNull()
|
|
expect(a.reasons.length).toBeGreaterThan(0)
|
|
})
|
|
|
|
it('via "size-heuristic": provider exposes neither healthReport() nor isReady()', () => {
|
|
const a = assessProviderHealth({})
|
|
expect(a.via).toBe('size-heuristic')
|
|
expect(a.readiness).toBe('unknown')
|
|
expect(a.report).toBeNull()
|
|
})
|
|
|
|
it('via "is-ready": provider exposes isReady() but no healthReport() — ready', () => {
|
|
const a = assessProviderHealth({ isReady: () => true })
|
|
expect(a.via).toBe('is-ready')
|
|
expect(a.readiness).toBe('ready')
|
|
expect(a.reasons).toEqual([])
|
|
})
|
|
|
|
it('via "is-ready": isReady() === false — not-ready with a reason', () => {
|
|
const a = assessProviderHealth({ isReady: () => false })
|
|
expect(a.via).toBe('is-ready')
|
|
expect(a.readiness).toBe('not-ready')
|
|
expect(a.reasons.length).toBeGreaterThan(0)
|
|
})
|
|
|
|
it('healthReport() present REPLACES isReady() — serving:true wins even if isReady() lies false', () => {
|
|
const p = { isReady: () => false, healthReport: () => report({ serving: true }) }
|
|
const a = assessProviderHealth(p)
|
|
expect(a.via).toBe('health-report')
|
|
expect(a.readiness).toBe('ready')
|
|
})
|
|
|
|
it('serving:true, healthy:true, no invariants failing → ready, no reasons', () => {
|
|
const p = { healthReport: () => report({ serving: true, healthy: true }) }
|
|
const a = assessProviderHealth(p)
|
|
expect(a.readiness).toBe('ready')
|
|
expect(a.reasons).toEqual([])
|
|
expect(a.report).toEqual(report({ serving: true, healthy: true }))
|
|
})
|
|
|
|
it('serving:false with a named heal:"rebuild" failing invariant → not-ready, reason names it', () => {
|
|
const failing = invariant({ name: 'posted-count-floor', holds: false, heal: 'rebuild', detail: 'posted 10 < canonical 20' })
|
|
const p = { healthReport: () => report({ serving: false, healthy: false, invariants: [failing] }) }
|
|
const a = assessProviderHealth(p)
|
|
expect(a.readiness).toBe('not-ready')
|
|
expect(a.reasons.some((r) => r.includes('posted-count-floor') && r.includes('heal:rebuild') && r.includes('posted 10 < canonical 20'))).toBe(true)
|
|
})
|
|
|
|
it('unledgered-only report (serving:true, no failing invariant) → ready, reason names the unledgered family', () => {
|
|
const p = { healthReport: () => report({ serving: true, healthy: true, unledgered: ['canonical-verb-coverage'] }) }
|
|
const a = assessProviderHealth(p)
|
|
expect(a.readiness).toBe('ready')
|
|
expect(a.reasons.some((r) => r.includes('unledgered') && r.includes('canonical-verb-coverage'))).toBe(true)
|
|
})
|
|
|
|
it('UNLEDGERED IS UNKNOWN: an unledgered family never flips a NOT-serving provider to ready', () => {
|
|
const failing = invariant({ holds: false, heal: 'rebuild', name: 'x' })
|
|
const p = { healthReport: () => report({ serving: false, healthy: false, invariants: [failing], unledgered: ['some-family'] }) }
|
|
const a = assessProviderHealth(p)
|
|
expect(a.readiness).toBe('not-ready')
|
|
})
|
|
|
|
it('serving:true, healthy:false with a heal:"repair" failure → still ready (degraded-but-serving)', () => {
|
|
const failing = invariant({ name: 'stale-counter', holds: false, heal: 'repair', detail: 'counter drift' })
|
|
const p = { healthReport: () => report({ serving: true, healthy: false, invariants: [failing] }) }
|
|
const a = assessProviderHealth(p)
|
|
expect(a.readiness).toBe('ready')
|
|
expect(a.reasons.some((r) => r.includes('stale-counter') && r.includes('heal:repair'))).toBe(true)
|
|
})
|
|
|
|
it('healthReport() that THROWS is a CONTRACT VIOLATION: not-ready, via health-report, reason names the throw — never "unknown"', () => {
|
|
const p = { healthReport: () => { throw new Error('mmap window busy') } }
|
|
const a = assessProviderHealth(p)
|
|
expect(a.via).toBe('health-report')
|
|
expect(a.readiness).toBe('not-ready')
|
|
expect(a.report).toBeNull()
|
|
expect(a.reasons.some((r) => r.includes('mmap window busy'))).toBe(true)
|
|
expect(a.readiness).not.toBe('unknown')
|
|
})
|
|
|
|
it('healthReport() that throws a non-Error value still produces a named reason (String(err))', () => {
|
|
const p = { healthReport: () => { throw 'boom' } }
|
|
const a = assessProviderHealth(p)
|
|
expect(a.readiness).toBe('not-ready')
|
|
expect(a.reasons.some((r) => r.includes('boom'))).toBe(true)
|
|
})
|
|
|
|
it('the returned report carries the generation for narration dedup', () => {
|
|
const p = { healthReport: () => report({ generation: 42 }) }
|
|
const a = assessProviderHealth(p)
|
|
expect(a.report?.generation).toBe(42)
|
|
})
|
|
})
|