/** * @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 { return { name: 'manifest-residency', holds: true, detail: 'ok', heal: 'none', source: 'ledger', ...overrides } } function report(overrides: Partial = {}): 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) }) })