brainy/tests/unit/storage/stats-count-accuracy.test.ts

59 lines
2.6 KiB
TypeScript
Raw Normal View History

/**
* @module tests/unit/storage/stats-count-accuracy
* @description Regression for per-type count inflation. `nounCountsByType` (read by
* `brain.stats().entitiesByType` and `counts.byTypeEnum`) used to be incremented in
* `saveNoun_internal()`, which runs on EVERY noun write including the HNSW index
* re-saving a node whenever its neighbor links change. So the per-type counts grew
* with graph connectivity rather than entity count (an internal report saw 8
* documents read as 44). The counter is now maintained in `saveNounMetadata_internal()`
* gated on `isNew` + visibility, exactly parallel to the authoritative total so the
* per-type counts must equal the entity count EXACTLY, no matter how dense the graph.
*
* These assert exact equality on purpose: the prior `toBeGreaterThanOrEqual` style
* passed for any inflated value and masked the bug.
*/
import { describe, it, expect } from 'vitest'
import { Brainy } from '../../../src/brainy.js'
import { NounType } from '../../../src/types/graphTypes.js'
describe('per-type count accuracy (HNSW re-save inflation)', () => {
it('stats()/counts report the EXACT entity count for a single dense type', async () => {
const brain = new Brainy({ requireSubtype: false, storage: { type: 'memory' } })
await brain.init()
try {
// Enough nodes that HNSW links each new node to existing neighbors and
// re-saves them — the exact path that used to double-count.
const N = 30
for (let i = 0; i < N; i++) {
await brain.add({ data: { name: `person ${i}` }, type: NounType.Person })
}
const stats = await brain.stats()
expect(stats.entityCount).toBe(N)
expect(stats.entitiesByType[NounType.Person]).toBe(N)
expect(brain.counts.byTypeEnum(NounType.Person)).toBe(N)
expect(await brain.getNounCount()).toBe(N)
} finally {
await brain.close()
}
})
it('keeps per-type counts exact across multiple types', async () => {
const brain = new Brainy({ requireSubtype: false, storage: { type: 'memory' } })
await brain.init()
try {
for (let i = 0; i < 12; i++) await brain.add({ data: { n: `p${i}` }, type: NounType.Person })
for (let i = 0; i < 7; i++) await brain.add({ data: { n: `d${i}` }, type: NounType.Document })
const stats = await brain.stats()
expect(stats.entitiesByType[NounType.Person]).toBe(12)
expect(stats.entitiesByType[NounType.Document]).toBe(7)
expect(stats.entityCount).toBe(19)
expect(brain.counts.byTypeEnum(NounType.Person)).toBe(12)
expect(brain.counts.byTypeEnum(NounType.Document)).toBe(7)
} finally {
await brain.close()
}
})
})