From fe6850b0670b6ab6144d67eea5f6b8ec1444ad0c Mon Sep 17 00:00:00 2001 From: Willisbest <132954469+Willisbest@users.noreply.github.com> Date: Thu, 23 Jul 2026 20:01:51 +0200 Subject: [PATCH] fix(test): isolate GBRAIN_HOME in hybrid-reranker integration test (#1527) (#2640) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The four `hybridSearch — reranker enabled (reorder)` cases stub the gateway at 1536 dims (DIMS). Since v0.36.3.0 hybridSearch resolves the embedding column via loadConfig(), whose precedence is cfg.embedding_dimensions > gateway dims > default. On any machine whose ~/.gbrain/config.json sets embedding_dimensions to something other than 1536 (e.g. text-embedding-3-small at 1280), the real config outranks the stub: the 1536-d stub vector fails the gateway dim check, the error is swallowed, search falls back to keyword-only, and the reranker never runs (rerankerFn gets 0 docs, rerank_score undefined). Green in CI only because a fresh runner has no config file — deterministic red on a contributor's machine. Fix (test-only): isolate GBRAIN_HOME to an empty tmpdir in beforeAll so loadConfig() returns null and the stub's dims win, then restore it and clean up in afterAll. Same idiom as emptyHome() in test/ai/gateway-probe-chat-model.test.ts. Verified with a planted ~/.gbrain/config.json at 1280 dims: 2 pass / 4 fail before, 6 pass / 0 fail after; still green with no config file. typecheck clean. Fixes #1527 Co-authored-by: Claude Opus 4.8 --- ...hybrid-reranker-integration.serial.test.ts | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/test/search/hybrid-reranker-integration.serial.test.ts b/test/search/hybrid-reranker-integration.serial.test.ts index 350f7ec8d..c900e4c21 100644 --- a/test/search/hybrid-reranker-integration.serial.test.ts +++ b/test/search/hybrid-reranker-integration.serial.test.ts @@ -27,9 +27,24 @@ import { } from '../../src/core/ai/gateway.ts'; import type { PageInput, SearchOpts } from '../../src/core/types.ts'; import type { RerankInput, RerankResult } from '../../src/core/ai/gateway.ts'; +import { mkdtempSync, rmSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; let engine: PGLiteEngine; +// These tests stub the gateway at 1536 dims (DIMS). Since v0.36.3.0 hybridSearch +// resolves the embedding column via loadConfig(), whose precedence is +// cfg.embedding_dimensions > gateway dims > default — so a contributor's real +// ~/.gbrain/config.json (e.g. text-embedding-3-small at 1280) outranks the stub, +// the 1536-d stub vector then fails the gateway dim check, search silently falls +// back to keyword-only, and the reranker never runs (0 docs → 4 tests fail). CI +// is green only because a fresh runner has no config file (#1527). Isolate +// GBRAIN_HOME to an empty tmpdir so loadConfig() returns null and the stub's dims +// win — same idiom as emptyHome() in test/ai/gateway-probe-chat-model.test.ts. +let prevGbrainHome: string | undefined; +let isolatedHome: string; + const DIMS = 1536; // gateway default embedding dim const FAKE_EMB = Array.from({ length: DIMS }, (_, j) => (j === 0 ? 1 : 0.01)); @@ -40,6 +55,12 @@ function stubEmbeddings(): void { } beforeAll(async () => { + // Hermetic config home: ignore the machine's real ~/.gbrain so its + // embedding_dimensions can't outrank the 1536-d stub (see note above, #1527). + prevGbrainHome = process.env.GBRAIN_HOME; + isolatedHome = mkdtempSync(join(tmpdir(), 'gbrain-rerank-home-')); + process.env.GBRAIN_HOME = isolatedHome; + engine = new PGLiteEngine(); await engine.connect({}); await engine.initSchema(); @@ -85,6 +106,9 @@ afterAll(async () => { __setEmbedTransportForTests(null); resetGateway(); await engine.disconnect(); + if (prevGbrainHome === undefined) delete process.env.GBRAIN_HOME; + else process.env.GBRAIN_HOME = prevGbrainHome; + rmSync(isolatedHome, { recursive: true, force: true }); }); describe('hybridSearch — reranker disabled (pass-through)', () => {