/** * @module tests/integration/generation-store-factory * @description Pins the `createGenerationStore` protected factory hook on * `Brainy` ({@link Brainy.createGenerationStore}). The hook exists so an * engine built on top of this reference implementation can substitute a * `GenerationStore` that keeps the same behavioural contract; this suite * proves two things: * * 1. A subclass overriding the hook is the ONLY path that constructs the * generation store — it is called exactly once, with the same storage * instance `performInit` holds — and the store the brain actually uses * is the one the override returned. * 2. The default (non-overridden) path is unaffected — proven here by * confirming the base class still produces a plain `GenerationStore` * wired to `brain.storage`, and separately by running the existing * `db-mvcc` and `brainy-core.integration` suites unmodified against this * change (they exercise generation-store behaviour end to end). */ import { describe, it, expect, afterEach } from 'vitest' import { Brainy } from '../../src/brainy.js' import { GenerationStore } from '../../src/db/generationStore.js' import type { BaseStorage } from '../../src/storage/baseStorage.js' /** Typed access to the brain's private storage + generation-store fields (test injection point). */ function internalsOf(brain: Brainy): { storage: BaseStorage; generationStore: GenerationStore } { return brain as unknown as { storage: BaseStorage; generationStore: GenerationStore } } /** * A `GenerationStore` subclass that counts its own construction and * remembers the storage instance it was built with, so the test can prove * the hook is the sole construction path without mocking the module. */ class SpyGenerationStore extends GenerationStore { static constructCount = 0 static lastStorage: BaseStorage | undefined constructor(storage: BaseStorage) { super(storage) SpyGenerationStore.constructCount++ SpyGenerationStore.lastStorage = storage } } /** A Brainy subclass overriding the factory hook — stands in for an engine built on the reference. */ class BrainyWithSpyStore extends Brainy { hookCallCount = 0 hookStorageArg: BaseStorage | undefined protected override createGenerationStore(storage: BaseStorage): GenerationStore { this.hookCallCount++ this.hookStorageArg = storage return new SpyGenerationStore(storage) } } describe('Brainy.createGenerationStore — protected factory hook', () => { const brains: Brainy[] = [] afterEach(async () => { SpyGenerationStore.constructCount = 0 SpyGenerationStore.lastStorage = undefined for (const brain of brains.splice(0)) { try { await brain.close() } catch { // already closed by the test } } }) it('a subclass override is the sole construction path: called once, same storage instance, its store is the one the brain uses', async () => { const brain = new BrainyWithSpyStore({ requireSubtype: false, storage: { type: 'memory' } }) await brain.init() brains.push(brain) // Called exactly once, through the hook. expect(brain.hookCallCount).toBe(1) expect(SpyGenerationStore.constructCount).toBe(1) // Same storage instance the base class holds — not a copy, not a different adapter. const { storage, generationStore } = internalsOf(brain) expect(brain.hookStorageArg).toBe(storage) expect(SpyGenerationStore.lastStorage).toBe(storage) // The store the brain actually uses is the one the override returned. expect(generationStore).toBeInstanceOf(SpyGenerationStore) }) it('the default (non-overridden) path still produces a plain GenerationStore wired to the same storage', async () => { const brain = new Brainy({ requireSubtype: false, storage: { type: 'memory' } }) await brain.init() brains.push(brain) const { storage, generationStore } = internalsOf(brain) expect(generationStore).toBeInstanceOf(GenerationStore) // The default implementation constructs from the same storage the brain holds. expect((generationStore as unknown as { storage: BaseStorage }).storage).toBe(storage) }) })