diff --git a/test/search/hybrid-reranker-integration.serial.test.ts b/test/search/hybrid-reranker-integration.serial.test.ts index 3ba02c371..34344731e 100644 --- a/test/search/hybrid-reranker-integration.serial.test.ts +++ b/test/search/hybrid-reranker-integration.serial.test.ts @@ -31,9 +31,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)); @@ -44,6 +59,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(); @@ -106,6 +127,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)', () => {