GitNexus/gitnexus/test/integration/lbug-vector-extension.test.ts
Mr. WorldwideBrown 6d9ec1009e
fix: load VECTOR extension during DB init for semantic search (#782)
* fix: load VECTOR extension during DB init for semantic search

The VECTOR extension was only loaded inside the embedding generation

pipeline (createVectorIndex). On a fresh gitnexus serve session,

semantic and hybrid search failed because QUERY_VECTOR_INDEX was

unknown.

Now loads the VECTOR extension alongside FTS during database

initialization in both the single-connection and pool-based paths.

Fixes #766

* fix: reset vectorExtensionLoaded on DB close and retry paths

The vectorExtensionLoaded flag was not being reset in closeLbug() or
the busy-retry cleanup path in withLbugDb(). This caused the VECTOR
extension to not be re-loaded after a close+re-init cycle, breaking
semantic search on reconnection.

Also resets shared.ftsLoaded and shared.vectorLoaded in the pool
adapter closeOne() for external DB entries, preventing stale extension
state when the pool is re-opened.

Adds integration tests covering vector extension loading, idempotency,
and state reset on both close and busy-retry paths.

* fix: set ftsLoaded flag in initLbugWithDb to avoid redundant extension reloads

* fix: set shared.vectorLoaded flag in initLbugWithDb to avoid redundant reloads
2026-04-11 12:17:40 +01:00

75 lines
2.8 KiB
TypeScript

/**
* Integration Tests: Vector extension loading and state reset
*
* Tests: loadVectorExtension idempotency, vectorExtensionLoaded reset
* on closeLbug and busy-retry cleanup paths.
*
* Follows existing lbug integration test patterns (lbug-core-adapter,
* lbug-lock-retry).
*/
import { describe, it, expect } from 'vitest';
import { withTestLbugDB } from '../helpers/test-indexed-db.js';
withTestLbugDB('vector-extension', (handle) => {
describe('loadVectorExtension', () => {
it('loads the VECTOR extension without error', async () => {
const { loadVectorExtension } = await import('../../src/core/lbug/lbug-adapter.js');
// Should resolve without throwing -- idempotent if already loaded by doInitLbug
await expect(loadVectorExtension()).resolves.toBeUndefined();
});
it('is idempotent -- calling twice does not throw', async () => {
const { loadVectorExtension } = await import('../../src/core/lbug/lbug-adapter.js');
await loadVectorExtension();
await expect(loadVectorExtension()).resolves.toBeUndefined();
});
});
describe('vectorExtensionLoaded reset on closeLbug', () => {
it('re-initializes vector extension after close + re-init cycle', async () => {
const adapter = await import('../../src/core/lbug/lbug-adapter.js');
// Ensure vector extension is loaded
await adapter.loadVectorExtension();
// Close the adapter -- should reset vectorExtensionLoaded
await adapter.closeLbug();
expect(adapter.isLbugReady()).toBe(false);
// Re-initialize -- doInitLbug calls loadVectorExtension internally
await adapter.initLbug(handle.dbPath);
expect(adapter.isLbugReady()).toBe(true);
// loadVectorExtension should succeed (not skip due to stale flag)
await expect(adapter.loadVectorExtension()).resolves.toBeUndefined();
});
});
describe('vectorExtensionLoaded reset on busy-retry cleanup', () => {
it('withLbugDb resets vectorExtensionLoaded on BUSY retry', async () => {
const adapter = await import('../../src/core/lbug/lbug-adapter.js');
// Ensure vector extension is loaded
await adapter.loadVectorExtension();
// Simulate a BUSY error on first attempt, success on second.
// The retry path should reset vectorExtensionLoaded so the
// re-initialized DB gets a fresh extension load.
let callCount = 0;
const result = await adapter.withLbugDb(handle.dbPath, async () => {
callCount++;
if (callCount === 1) throw new Error('database is BUSY');
return 'recovered';
});
expect(result).toBe('recovered');
expect(callCount).toBe(2);
// After recovery, vector extension should still be loadable
// (the flag was reset and re-loaded during re-init)
await expect(adapter.loadVectorExtension()).resolves.toBeUndefined();
});
});
});