123 lines
4.7 KiB
TypeScript
123 lines
4.7 KiB
TypeScript
|
|
/**
|
||
|
|
* @module tests/unit/brainy/maintenance-debt
|
||
|
|
* @description Coverage for `brain.maintenanceDebt()` (8.10.1) — the
|
||
|
|
* observability seam so an operator sees a provider's outstanding background
|
||
|
|
* maintenance work (compaction, deferred writes, a build-new→verify→swap in
|
||
|
|
* flight, ...) BEFORE it grinds a transaction into a budget-busting op, the
|
||
|
|
* same failure class documented on `TransactionTimeoutError`
|
||
|
|
* (src/transaction/errors.ts). Sibling to tests/unit/brainy/warm.test.ts,
|
||
|
|
* which establishes this file's technique: shape the probe points brain.ts
|
||
|
|
* reads (`typeof provider.maintenanceDebt === 'function'`) directly on the
|
||
|
|
* REAL, live provider instances rather than hand-rolling full fakes for the
|
||
|
|
* larger `MetadataIndexProvider` / `GraphIndexProvider` interfaces.
|
||
|
|
*
|
||
|
|
* `brain.maintenanceDebt()` is a PURE PASSTHROUGH: no thresholds, no
|
||
|
|
* polling, no JS-side estimation — these tests pin exactly that by asserting
|
||
|
|
* the returned payload is the provider's object, verbatim.
|
||
|
|
*/
|
||
|
|
import { describe, it, expect } from 'vitest'
|
||
|
|
import { Brainy } from '../../../src/brainy.js'
|
||
|
|
import { NounType } from '../../../src/types/graphTypes.js'
|
||
|
|
import type { ProviderMaintenanceDebt } from '../../../src/plugin.js'
|
||
|
|
|
||
|
|
// Brainy's ValidationConfig fixes vectors at exactly 384 dimensions
|
||
|
|
// (src/utils/paramValidation.ts) — match it so add() doesn't reject test data.
|
||
|
|
const DIM = 384
|
||
|
|
const V = (seed = 1): number[] => Array.from({ length: DIM }, (_, i) => Math.sin(seed + i))
|
||
|
|
|
||
|
|
async function freshBrain(): Promise<Brainy<any>> {
|
||
|
|
const brain = new Brainy({
|
||
|
|
requireSubtype: false,
|
||
|
|
storage: { type: 'memory' },
|
||
|
|
silent: true
|
||
|
|
})
|
||
|
|
await brain.init()
|
||
|
|
await brain.add({ data: 'a', type: NounType.Thing, vector: V(1) })
|
||
|
|
return brain
|
||
|
|
}
|
||
|
|
|
||
|
|
describe('brain.maintenanceDebt()', () => {
|
||
|
|
it('reports "unavailable" for every surface when no active provider implements maintenanceDebt() (the built-in JS stack today)', async () => {
|
||
|
|
const brain = await freshBrain()
|
||
|
|
|
||
|
|
const report = await brain.maintenanceDebt()
|
||
|
|
|
||
|
|
expect(report.vector.outcome).toBe('unavailable')
|
||
|
|
expect(report.vector.debt).toBeUndefined()
|
||
|
|
expect(report.metadata.outcome).toBe('unavailable')
|
||
|
|
expect(report.metadata.debt).toBeUndefined()
|
||
|
|
expect(report.graph.outcome).toBe('unavailable')
|
||
|
|
expect(report.graph.debt).toBeUndefined()
|
||
|
|
|
||
|
|
await brain.close()
|
||
|
|
})
|
||
|
|
|
||
|
|
it('reports "reported" + the exact payload when the active provider implements maintenanceDebt() (verbatim passthrough, no thresholding)', async () => {
|
||
|
|
const brain = await freshBrain()
|
||
|
|
|
||
|
|
const vectorDebt: ProviderMaintenanceDebt = {
|
||
|
|
pendingBytes: 4_096,
|
||
|
|
pendingItems: 12,
|
||
|
|
lastPassCompletedAt: 1_700_000_000_000,
|
||
|
|
lastPassOutcome: 'completed',
|
||
|
|
converging: true
|
||
|
|
}
|
||
|
|
;(brain as any).index.maintenanceDebt = async () => vectorDebt
|
||
|
|
|
||
|
|
const report = await brain.maintenanceDebt()
|
||
|
|
|
||
|
|
expect(report.vector.outcome).toBe('reported')
|
||
|
|
// Verbatim passthrough — the exact object, not a re-derived copy.
|
||
|
|
expect(report.vector.debt).toBe(vectorDebt)
|
||
|
|
// Untouched surfaces stay honestly 'unavailable'.
|
||
|
|
expect(report.metadata.outcome).toBe('unavailable')
|
||
|
|
expect(report.graph.outcome).toBe('unavailable')
|
||
|
|
|
||
|
|
await brain.close()
|
||
|
|
})
|
||
|
|
|
||
|
|
it('mixed surfaces: each surface\'s outcome depends ONLY on its OWN active provider — one surface reporting never leaks into another', async () => {
|
||
|
|
const brain = await freshBrain()
|
||
|
|
|
||
|
|
const metadataDebt: ProviderMaintenanceDebt = {
|
||
|
|
pendingItems: 3,
|
||
|
|
lastPassOutcome: 'partial',
|
||
|
|
converging: false
|
||
|
|
}
|
||
|
|
const graphDebt: ProviderMaintenanceDebt = {
|
||
|
|
pendingBytes: 0,
|
||
|
|
converging: true
|
||
|
|
}
|
||
|
|
;(brain as any).metadataIndex.maintenanceDebt = async () => metadataDebt
|
||
|
|
;(brain as any).graphIndex.maintenanceDebt = async () => graphDebt
|
||
|
|
// Vector is deliberately left unpatched.
|
||
|
|
|
||
|
|
const report = await brain.maintenanceDebt()
|
||
|
|
|
||
|
|
expect(report.vector.outcome).toBe('unavailable')
|
||
|
|
expect(report.vector.debt).toBeUndefined()
|
||
|
|
|
||
|
|
expect(report.metadata.outcome).toBe('reported')
|
||
|
|
expect(report.metadata.debt).toBe(metadataDebt)
|
||
|
|
|
||
|
|
expect(report.graph.outcome).toBe('reported')
|
||
|
|
expect(report.graph.debt).toBe(graphDebt)
|
||
|
|
|
||
|
|
await brain.close()
|
||
|
|
})
|
||
|
|
|
||
|
|
it('an empty ProviderMaintenanceDebt object (every field omitted) is still honestly "reported" — presence of the hook, not the payload\'s richness, drives the outcome', async () => {
|
||
|
|
const brain = await freshBrain()
|
||
|
|
|
||
|
|
const emptyDebt: ProviderMaintenanceDebt = {}
|
||
|
|
;(brain as any).graphIndex.maintenanceDebt = async () => emptyDebt
|
||
|
|
|
||
|
|
const report = await brain.maintenanceDebt()
|
||
|
|
|
||
|
|
expect(report.graph.outcome).toBe('reported')
|
||
|
|
expect(report.graph.debt).toEqual({})
|
||
|
|
|
||
|
|
await brain.close()
|
||
|
|
})
|
||
|
|
})
|