refactor(fts): drop targetConn! assertions, add targetConn-path test

Addresses the adversarial review on PR #726.

- Replaced `targetConn!` non-null assertions in loadFTSExtension with a
  typed local that TypeScript can narrow without `!`.
- Added an integration test exercising loadFTSExtension(conn) on a fresh
  Connection — the path used by the pool adapter but previously only
  covered transitively through search-pool tests.
This commit is contained in:
Pratyush Sharma 2026-04-24 01:47:48 +05:30
parent 7ee2b9b54a
commit 5d38d9b3d5
No known key found for this signature in database
2 changed files with 33 additions and 9 deletions

View file

@ -1156,12 +1156,11 @@ export const getEmbeddingTableName = (): string => EMBEDDING_TABLE_NAME;
*/
export const loadFTSExtension = async (targetConn?: lbug.Connection): Promise<boolean> => {
const useModuleState = targetConn === undefined;
if (useModuleState) {
if (ftsLoaded) return true;
if (!conn) {
throw new Error('LadybugDB not initialized. Call initLbug first.');
}
targetConn = conn;
if (useModuleState && ftsLoaded) return true;
const c: lbug.Connection | null = targetConn ?? conn;
if (!c) {
throw new Error('LadybugDB not initialized. Call initLbug first.');
}
const markLoaded = (): true => {
@ -1171,13 +1170,13 @@ export const loadFTSExtension = async (targetConn?: lbug.Connection): Promise<bo
try {
// Try loading locally first (no network required)
await targetConn!.query('LOAD EXTENSION fts');
await c.query('LOAD EXTENSION fts');
return markLoaded();
} catch {
// Fall back to install + load (requires network)
try {
await targetConn!.query('INSTALL fts');
await targetConn!.query('LOAD EXTENSION fts');
await c.query('INSTALL fts');
await c.query('LOAD EXTENSION fts');
return markLoaded();
} catch (err: any) {
const msg = err?.message || '';

View file

@ -45,6 +45,31 @@ withTestLbugDB(
).resolves.toBeUndefined();
});
it('loadFTSExtension(conn): loads on an explicit connection and returns true', async () => {
const lbug = (await import('@ladybugdb/core')).default;
const { loadFTSExtension, getDatabase } =
await import('../../src/core/lbug/lbug-adapter.js');
const db = getDatabase();
expect(db).not.toBeNull();
// Fresh Connection on the same Database — simulates the pool adapter's
// path where loadFTSExtension is called with an explicit connection
// rather than the module-level singleton.
const freshConn = new lbug.Connection(db!);
try {
const loaded = await loadFTSExtension(freshConn);
expect(loaded).toBe(true);
// Idempotent on the same connection — calling again still returns true
// (exercises the "already loaded" catch branch in the fallback path).
const loadedAgain = await loadFTSExtension(freshConn);
expect(loadedAgain).toBe(true);
} finally {
await freshConn.close().catch(() => {});
}
});
it('getLbugStats: returns correct node and edge counts for seeded data', async () => {
const { getLbugStats } = await import('../../src/core/lbug/lbug-adapter.js');