From b25caa3191a46ffae17722784442633bbd0109d3 Mon Sep 17 00:00:00 2001 From: Gergo Magyar Date: Tue, 21 Jul 2026 06:06:42 +0000 Subject: [PATCH] fix(test): discover the installed FTS extension version dir instead of assuming it equals lbug.VERSION resolveInstalledFtsExtension (extension-binary-real.test.ts) and resolveSeedExtension (fts-extension-e2e.test.ts) both hardcoded the on-disk FTS extension path as .lbdb/extension//..., but LadybugDB's native INSTALL/LOAD resolves its own extension-ABI version directory, which does not always track the npm package version. Bumping @ladybugdb/core from 0.18.1 to 0.18.2 in this PR still installs into a 0.18.1 directory, so both hardcoded lookups came up empty and failed hard under GITNEXUS_REQUIRE_FTS=1 in CI (all platforms, shard 3). Add findInstalledFtsExtension() to discover the real installed file by scanning every version subdirectory, and use it from both test files. Co-Authored-By: Claude Sonnet 5 --- gitnexus/test/helpers/fts-availability.ts | 36 +++++++++++++++++++ .../integration/extension-binary-real.test.ts | 20 +++-------- .../integration/fts-extension-e2e.test.ts | 35 ++++++++---------- 3 files changed, 55 insertions(+), 36 deletions(-) diff --git a/gitnexus/test/helpers/fts-availability.ts b/gitnexus/test/helpers/fts-availability.ts index 28d8eb7dd..bc3620662 100644 --- a/gitnexus/test/helpers/fts-availability.ts +++ b/gitnexus/test/helpers/fts-availability.ts @@ -1,3 +1,39 @@ +import { existsSync, readdirSync, statSync } from 'node:fs'; +import { join } from 'node:path'; + +/** A valid `libfts.lbug_extension` is ~2.2MB; anything smaller is truncated/corrupt. */ +const MIN_VALID_FTS_EXTENSION_BYTES = 1024 * 1024; + +/** + * Find the installed FTS extension file under a `.lbdb/extension` root, + * discovering the version directory instead of assuming it equals the npm + * `@ladybugdb/core` package version. LadybugDB's native INSTALL/LOAD resolves + * its own extension-ABI version directory, which does not always track the + * npm package version — e.g. #2587: bumping the package from 0.18.1 to 0.18.2 + * still installs into a `0.18.1` directory, because the underlying + * extension-ABI build did not change with that patch release. + * + * Scans every version subdirectory for a `/fts/libfts.lbug_extension` + * file and returns the most recently modified one (the one an install/load + * actually just resolved), or null when nothing is installed. + */ +export const findInstalledFtsExtension = (extensionRoot: string): string | null => { + if (!existsSync(extensionRoot)) return null; + let best: { path: string; mtimeMs: number } | null = null; + for (const versionEntry of readdirSync(extensionRoot)) { + const versionDir = join(extensionRoot, versionEntry); + if (!statSync(versionDir).isDirectory()) continue; + for (const platformEntry of readdirSync(versionDir)) { + const candidate = join(versionDir, platformEntry, 'fts', 'libfts.lbug_extension'); + if (!existsSync(candidate)) continue; + const stat = statSync(candidate); + if (stat.size < MIN_VALID_FTS_EXTENSION_BYTES) continue; + if (!best || stat.mtimeMs > best.mtimeMs) best = { path: candidate, mtimeMs: stat.mtimeMs }; + } + } + return best?.path ?? null; +}; + export const FTS_UNAVAILABLE_NOTE = 'FTS extension unavailable (load-only policy; LOAD failed on this machine)'; diff --git a/gitnexus/test/integration/extension-binary-real.test.ts b/gitnexus/test/integration/extension-binary-real.test.ts index 27293c1a5..a2734d40f 100644 --- a/gitnexus/test/integration/extension-binary-real.test.ts +++ b/gitnexus/test/integration/extension-binary-real.test.ts @@ -2,21 +2,21 @@ import { copyFileSync, existsSync, mkdtempSync, - readdirSync, readFileSync, rmSync, - statSync, writeFileSync, } from 'node:fs'; import { homedir, tmpdir } from 'node:os'; import { join } from 'node:path'; import { afterAll, describe, expect, it } from 'vitest'; -import lbug from '@ladybugdb/core'; import { diagnoseExtensionLoad, inspectExtensionBinary, } from '../../src/core/lbug/extension-load-error.js'; -import { requireFtsResourceOrSkip } from '../helpers/fts-availability.js'; +import { + findInstalledFtsExtension, + requireFtsResourceOrSkip, +} from '../helpers/fts-availability.js'; /** * #2374: exercise the language-independent structural classifier against REAL @@ -49,17 +49,7 @@ function resolveLbugNative(): string | null { /** The actual installed FTS extension binary for the running lbug version. */ function resolveInstalledFtsExtension(): string | null { const home = process.env.USERPROFILE ?? process.env.HOME ?? homedir(); - const base = join(home, '.lbdb', 'extension', lbug.VERSION); - try { - const platformDir = readdirSync(base).find((entry) => - statSync(join(base, entry)).isDirectory(), - ); - if (!platformDir) return null; - const ext = join(base, platformDir, 'fts', 'libfts.lbug_extension'); - return existsSync(ext) ? ext : null; - } catch { - return null; - } + return findInstalledFtsExtension(join(home, '.lbdb', 'extension')); } const lbugNative = resolveLbugNative(); diff --git a/gitnexus/test/integration/fts-extension-e2e.test.ts b/gitnexus/test/integration/fts-extension-e2e.test.ts index 2c072a2fe..94d56cd1b 100644 --- a/gitnexus/test/integration/fts-extension-e2e.test.ts +++ b/gitnexus/test/integration/fts-extension-e2e.test.ts @@ -25,9 +25,9 @@ import path from 'path'; import fs from 'fs'; import os from 'os'; -import lbug from '@ladybugdb/core'; import { getExtensionInstallChildProcessArgs } from '../../src/core/lbug/extension-loader.js'; import { cleanupTempDirSync } from '../helpers/test-db.js'; +import { findInstalledFtsExtension } from '../helpers/fts-availability.js'; /** `.lbdb/extension///fts/libfts.lbug_extension`, discovered not hardcoded. */ let extensionRelPath: string; @@ -52,16 +52,12 @@ const makeTmpDir = (label: string): string => { * home — the production installer script, not a reimplementation. */ const resolveSeedExtension = (): void => { - const relBase = path.join('.lbdb', 'extension', lbug.VERSION); - const realVersionDir = path.join(os.homedir(), relBase); - const platformDirs = fs.existsSync(realVersionDir) ? fs.readdirSync(realVersionDir) : []; - for (const platform of platformDirs) { - const candidate = path.join(realVersionDir, platform, 'fts', 'libfts.lbug_extension'); - if (fs.existsSync(candidate) && fs.statSync(candidate).size > 1024 * 1024) { - extensionRelPath = path.join(relBase, platform, 'fts', 'libfts.lbug_extension'); - seedExtensionFile = candidate; - return; - } + const realExtensionRoot = path.join(os.homedir(), '.lbdb', 'extension'); + const installed = findInstalledFtsExtension(realExtensionRoot); + if (installed) { + extensionRelPath = path.relative(os.homedir(), installed); + seedExtensionFile = installed; + return; } // No local copy — run the real installer against a hermetic probe home. const probeHome = makeTmpDir('seed-home'); @@ -70,16 +66,13 @@ const resolveSeedExtension = (): void => { timeout: 120_000, env: { ...process.env, HOME: probeHome, USERPROFILE: probeHome }, }); - const probeVersionDir = path.join(probeHome, relBase); - const probePlatforms = fs.existsSync(probeVersionDir) ? fs.readdirSync(probeVersionDir) : []; - for (const platform of probePlatforms) { - const candidate = path.join(probeVersionDir, platform, 'fts', 'libfts.lbug_extension'); - if (install.status === 0 && fs.existsSync(candidate)) { - extensionRelPath = path.join(relBase, platform, 'fts', 'libfts.lbug_extension'); - seedExtensionFile = candidate; - networkAvailable = true; - return; - } + const probeExtensionRoot = path.join(probeHome, '.lbdb', 'extension'); + const probeInstalled = findInstalledFtsExtension(probeExtensionRoot); + if (install.status === 0 && probeInstalled) { + extensionRelPath = path.relative(probeHome, probeInstalled); + seedExtensionFile = probeInstalled; + networkAvailable = true; + return; } };