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/<lbug.VERSION>/..., 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 <noreply@anthropic.com>
This commit is contained in:
Gergo Magyar 2026-07-21 06:06:42 +00:00
parent 6476cc11ae
commit b25caa3191
3 changed files with 55 additions and 36 deletions

View file

@ -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 `<platform>/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)';

View file

@ -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();

View file

@ -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/<version>/<platform>/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;
}
};