/** * @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() } }) })