import { describe, it, expect, vi, afterEach } from 'vitest'; import path from 'node:path'; /** * Tests for the CUDA-build-matching onnxruntime-node redirect. * * `@huggingface/transformers` exact-pins a CUDA-12 `onnxruntime-node`, while * gitnexus' own dep floats to a CUDA-13 build; on a CUDA-13 host this module * redirects transformers to the matching copy so embeddings use the GPU instead * of silently falling back to CPU. The detection primitives (`ldconfig` / `ldd` * / path scan) and `module.registerHooks` are mocked so the pure decision logic * is asserted without touching the real loader or the host's CUDA install. */ const RESOLVER = '../../src/core/embeddings/onnxruntime-node-resolver.js'; const REAL_PLATFORM = process.platform; const REAL_ENV = { ...process.env }; /** * Node's `path` module is bound to `path.win32` or `path.posix` based on the * REAL host OS at process start — stubbing `process.platform` later (as this * file's tests do, for the resolver's OWN platform branching) has no effect * on it. So on a genuine Windows CI runner, the resolver's `join(...)` calls * normalize our forward-slash fake dirs to backslash-separated strings, * which would silently fail to match the forward-slash fixtures/prefixes * below. Normalize before every comparison so these tests are host-OS-agnostic. */ const toPosix = (p: string): string => p.replace(/\\/g, '/'); /** Three fake, distinct onnxruntime-node locations for driving decide() into redirect:true. */ interface FakeDirs { /** gitnexus' own top-level onnxruntime-node dir (resolved via the module's own require). */ ourDir: string; /** transformers' pinned/nested onnxruntime-node dir (resolved via createRequire(transformersMain)). */ defaultDir: string; /** fake resolved path for require.resolve('@huggingface/transformers'). */ transformersMain: string; /** When false, createRequire(transformersMain).resolve('onnxruntime-node/package.json') throws * (simulating resolveDefaultOrtNodeDir() failing outright) instead of resolving to `defaultDir`. */ defaultResolvable?: boolean; /** When false, gitnexus' own top-level onnxruntime-node does NOT resolve (pruned install), * so resolveOurOrtNodeDir falls back to the on-demand prefix (#2372). */ ourResolvable?: boolean; /** The runtime prefix dir the test set via GITNEXUS_EMBEDDING_RUNTIME_DIR; its `/noop.js` * createRequire anchor is routed to a require that resolves onnxruntime-node to `prefixOrtNodeDir`. */ prefixDir?: string; /** onnxruntime-node dir the prefix-anchored require resolves to (the #2372 fallback target). */ prefixOrtNodeDir?: string; } interface LoadOpts { registerHooks?: unknown; platform?: NodeJS.Platform; execFileSync?: (cmd: string, args: string[]) => string; existsSync?: (p: string) => boolean; fakeDirs?: FakeDirs; /** Force the resolver's `join`/`dirname` calls to use `path.win32` semantics * (backslash-normalized output) regardless of the real host OS — proves the * `toPosix()` normalization above actually works, rather than merely being * argued for (#2341 follow-up). */ forceWin32Path?: boolean; } /** A require()-like function whose .resolve() is driven entirely by a specifier -> path map. */ function fakeRequire(resolveMap: Record) { return Object.assign( (specifier: string) => { throw new Error(`fakeRequire: unexpected require(${specifier})`); }, { resolve: (specifier: string) => { const hit = resolveMap[specifier]; if (!hit) { throw Object.assign(new Error(`Cannot find module '${specifier}'`), { code: 'MODULE_NOT_FOUND', }); } return hit; }, }, ); } /** * (Re)load the resolver with detection primitives + `registerHooks` mocked. * `vi.resetModules()` clears the module-level decision cache and one-shot guard, * so each test gets a pristine resolver. * * When `fakeDirs` is supplied, `createRequire` is also mocked so the module's * two CJS resolve-walks (`resolveOurOrtNodeDir`/`resolveDefaultOrtNodeDir`, and * the nodeUrl/commonUrl lookup inside `ensureOnnxRuntimeNodeMatchesSystem`) each * resolve against a distinct fake directory instead of whatever's actually * installed in this test's real node_modules — the only way to drive * `decide() -> redirect:true` deterministically without touching production code. */ async function loadResolver(opts: LoadOpts = {}) { vi.resetModules(); // Destructuring defaults (`= vi.fn()`) only apply when the property is // `undefined` — but callers pass `registerHooks: undefined` specifically to // simulate Node < 22.15 (no synchronous-hooks API), so a plain destructuring // default would silently substitute a real mock function and defeat that. // `'registerHooks' in opts` distinguishes "omitted → default to a spy" from // "explicitly undefined → simulate its absence". const registerHooks = 'registerHooks' in opts ? opts.registerHooks : vi.fn(); const { platform = 'linux', execFileSync = () => { throw Object.assign(new Error('enoent'), { code: 'ENOENT' }); }, existsSync = () => false, fakeDirs, forceWin32Path = false, } = opts; if (forceWin32Path) { vi.doMock('node:path', () => ({ ...path.win32, default: path.win32 })); } vi.doMock('node:module', async (io) => { const orig = await io(); if (!fakeDirs) return { ...orig, registerHooks }; const ourRequire = fakeRequire({ '@huggingface/transformers': fakeDirs.transformersMain, ...(fakeDirs.ourResolvable === false ? {} : { 'onnxruntime-node/package.json': `${fakeDirs.ourDir}/package.json` }), }); const defaultRequire = fakeRequire( fakeDirs.defaultResolvable === false ? {} : { 'onnxruntime-node/package.json': `${fakeDirs.defaultDir}/package.json` }, ); const effectiveRequire = fakeRequire({ 'onnxruntime-node': `${fakeDirs.ourDir}/index.js`, 'onnxruntime-common': `${fakeDirs.ourDir}/node_modules/onnxruntime-common/index.js`, }); // The on-demand prefix's require (anchored at `/noop.js`) resolves // gitnexus' effective top-level onnxruntime-node when the real one was pruned (#2372). const prefixRequire = fakeRequire( fakeDirs.prefixOrtNodeDir ? { 'onnxruntime-node/package.json': `${fakeDirs.prefixOrtNodeDir}/package.json` } : {}, ); return { ...orig, registerHooks, createRequire: (from: string) => { // `from` is produced by the resolver's own `join(effectiveDir, 'package.json')` // call — backslash-normalized on a real Windows host even though // `fakeDirs.ourDir` etc. are forward-slash fixtures; normalize before comparing. const normalizedFrom = toPosix(from); if (normalizedFrom === fakeDirs.transformersMain) return defaultRequire; if (normalizedFrom === `${fakeDirs.ourDir}/package.json`) return effectiveRequire; // The runtime-prefix anchor is the only createRequire `from` ending in // noop.js; match by suffix so a real-Windows `path.resolve` drive prefix // (C:\…) on the env-set prefix dir doesn't defeat an exact-path compare. if (fakeDirs.prefixDir && normalizedFrom.endsWith('/noop.js')) return prefixRequire; return ourRequire; }, }; }); vi.doMock('node:child_process', async (io) => ({ ...(await io()), // Normalize args (the `.so` path for `ldd`) so callers' forward-slash // prefix checks match regardless of which path module the resolver's // own `join(...)` calls were bound to on the host running this test. execFileSync: (cmd: string, args: string[]) => execFileSync(cmd, args.map(toPosix)), })); vi.doMock('node:fs', async (io) => ({ ...(await io()), existsSync: (p: unknown) => existsSync(toPosix(String(p))), })); Object.defineProperty(process, 'platform', { value: platform, configurable: true }); return import(RESOLVER); } afterEach(() => { vi.doUnmock('node:module'); vi.doUnmock('node:child_process'); vi.doUnmock('node:fs'); vi.doUnmock('node:path'); Object.defineProperty(process, 'platform', { value: REAL_PLATFORM, configurable: true }); process.env = { ...REAL_ENV }; }); describe('detectSystemCudaMajor', () => { it.each(['darwin', 'win32'] as const)( 'returns null on non-linux platforms (%s)', async (platform) => { const mod = await loadResolver({ platform }); expect(mod.detectSystemCudaMajor()).toBeNull(); }, ); it('prefers CUDA 13 over 12 when ldconfig lists both', async () => { const mod = await loadResolver({ execFileSync: () => 'libcublasLt.so.13 (libc6,x86-64) => /usr/local/cuda/lib64/libcublasLt.so.13\n' + 'libcublasLt.so.12 (libc6,x86-64) => /old/libcublasLt.so.12', }); expect(mod.detectSystemCudaMajor()).toBe(13); }); it('detects CUDA 12 when only .so.12 is present', async () => { const mod = await loadResolver({ execFileSync: () => 'libcublasLt.so.12 (libc6,x86-64) => /usr/lib/libcublasLt.so.12', }); expect(mod.detectSystemCudaMajor()).toBe(12); }); it('falls back to an LD_LIBRARY_PATH scan when ldconfig is unavailable', async () => { process.env.LD_LIBRARY_PATH = '/opt/cuda/lib64'; const mod = await loadResolver({ execFileSync: () => { throw new Error('ldconfig missing'); }, existsSync: (p) => p === '/opt/cuda/lib64/libcublasLt.so.13', }); expect(mod.detectSystemCudaMajor()).toBe(13); }); it('returns null when no cuBLASLt is found anywhere', async () => { const mod = await loadResolver({ execFileSync: () => 'libfoo.so => /x/libfoo.so' }); expect(mod.detectSystemCudaMajor()).toBeNull(); }); it('falls back to a CUDA_PATH scan when ldconfig is unavailable (#2341 follow-up)', async () => { // Mirrors the existing LD_LIBRARY_PATH-only test above — CUDA_PATH is // scanned first in the fallback loop and was previously untested on its own. process.env.CUDA_PATH = '/opt/cuda'; const mod = await loadResolver({ execFileSync: () => { throw new Error('ldconfig missing'); }, existsSync: (p) => p === '/opt/cuda/lib64/libcublasLt.so.13', }); expect(mod.detectSystemCudaMajor()).toBe(13); }); it('returns null (not a false match) when the ldconfig output is garbled/unrecognized', async () => { const mod = await loadResolver({ execFileSync: () => 'some-corrupted-binary-output-\x00\xff-not-a-cuda-lib-line', }); expect(mod.detectSystemCudaMajor()).toBeNull(); }); it('prefers a CUDA 13 found later in the search path over a CUDA 12 found earlier (#2341 follow-up)', async () => { // A stale CUDA_PATH entry (e.g. left over from a prior install) only has // .so.12; LD_LIBRARY_PATH, scanned after it, has the genuine .so.13. The // scan must not stop at the first match — it must keep looking for a // better (13) answer across the WHOLE search space. process.env.CUDA_PATH = '/opt/old-cuda-12'; process.env.LD_LIBRARY_PATH = '/opt/cuda-13/lib64'; const mod = await loadResolver({ execFileSync: () => { throw new Error('ldconfig missing'); }, existsSync: (p) => p === '/opt/old-cuda-12/libcublasLt.so.12' || p === '/opt/cuda-13/lib64/libcublasLt.so.13', }); expect(mod.detectSystemCudaMajor()).toBe(13); }); }); describe('ortCudaMajor', () => { it('returns null when the CUDA provider .so is absent', async () => { const mod = await loadResolver({ existsSync: () => false }); expect(mod.ortCudaMajor('/pkg/onnxruntime-node')).toBeNull(); }); it('reads CUDA 13 from the provider .so NEEDED entries', async () => { const mod = await loadResolver({ existsSync: () => true, execFileSync: () => 'libcublasLt.so.13 => /usr/local/cuda/lib64/libcublasLt.so.13', }); expect(mod.ortCudaMajor('/pkg/onnxruntime-node')).toBe(13); }); it('reads CUDA 12 even when the NEEDED lib is unresolved (ldd non-zero exit)', async () => { const mod = await loadResolver({ existsSync: () => true, execFileSync: () => { // ldd exits non-zero with the "=> not found" line on stdout throw Object.assign(new Error('ldd failed'), { stdout: 'libcublasLt.so.12 => not found', }); }, }); expect(mod.ortCudaMajor('/pkg/onnxruntime-node')).toBe(12); }); it('returns null (not a false match) when the ldd output is garbled/unrecognized (#2341 follow-up)', async () => { const mod = await loadResolver({ existsSync: () => true, execFileSync: () => 'libunrelated.so.1 => /x/libunrelated.so.1\nlibc.so.6 => /lib/libc.so.6', }); expect(mod.ortCudaMajor('/pkg/onnxruntime-node')).toBeNull(); }); it('warns (detection failed) when ldd produces no usable output at all, distinct from the silent no-provider case (#2341 follow-up)', async () => { // Capture AFTER loadResolver() so the capture targets the same (freshly // reset) logger.js instance the resolver module itself imports — the // module registry is cleared by loadResolver()'s vi.resetModules(). const mod = await loadResolver({ existsSync: () => true, // Simulates a missing `ldd` binary (ENOENT) or a permission-denied // `.so`: execFileSync throws with no `stdout` at all, unlike the // "=> not found" case above which still yields usable text. execFileSync: () => { throw Object.assign(new Error('spawn ldd ENOENT'), { code: 'ENOENT' }); }, }); const { _captureLogger } = await import('../../src/core/logger.js'); const cap = _captureLogger(); try { expect(mod.ortCudaMajor('/pkg/onnxruntime-node')).toBeNull(); const records = cap.records(); expect( records.some((r) => r.msg?.includes('Could not read CUDA provider dependencies')), ).toBe(true); } finally { cap.restore(); } }); it('does not warn when the CUDA provider .so is simply absent (no detection was even attempted)', async () => { const mod = await loadResolver({ existsSync: () => false }); const { _captureLogger } = await import('../../src/core/logger.js'); const cap = _captureLogger(); try { expect(mod.ortCudaMajor('/pkg/onnxruntime-node')).toBeNull(); const records = cap.records(); expect( records.some((r) => r.msg?.includes('Could not read CUDA provider dependencies')), ).toBe(false); } finally { cap.restore(); } }); }); describe('ensureOnnxRuntimeNodeMatchesSystem', () => { it('no-ops gracefully when registerHooks is unavailable (Node < 22.15), leaving the module otherwise functional', async () => { const mod = await loadResolver({ registerHooks: undefined }); expect(() => mod.ensureOnnxRuntimeNodeMatchesSystem()).not.toThrow(); // The one-shot guard tripping (or not) must not corrupt decide()'s cache — // subsequent calls to the other exports still work normally afterward. expect(() => mod.getEffectiveOnnxRuntimeNodeDir()).not.toThrow(); expect(mod.isEffectiveCudaAvailable()).toBe(false); // redirect can never be active without registerHooks }); it('installs no hook when there is no system CUDA (no redirect needed)', async () => { const spy = vi.fn(); // non-linux → detectSystemCudaMajor() === null → decide() → redirect: false const mod = await loadResolver({ registerHooks: spy, platform: 'darwin' }); mod.ensureOnnxRuntimeNodeMatchesSystem(); expect(spy).not.toHaveBeenCalled(); }); it('is idempotent in the no-redirect case: a second call is still a no-op (registerHooks never called)', async () => { const spy = vi.fn(); const mod = await loadResolver({ registerHooks: spy, platform: 'darwin' }); mod.ensureOnnxRuntimeNodeMatchesSystem(); mod.ensureOnnxRuntimeNodeMatchesSystem(); // (True install-once idempotency, where a redirect WOULD fire without the // guard, is covered by "installs registerHooks exactly once when the // redirect is active" below — this case only proves repeated calls stay // side-effect-free when there's nothing to install.) expect(spy).not.toHaveBeenCalled(); }); it('exposes an effective onnxruntime-node dir (string or null) for the CUDA probe, never throwing', async () => { const mod = await loadResolver({ platform: 'darwin' }); // Non-linux: no redirect, so the effective dir is transformers' default — // a string when resolvable in the test tree (it really is, in this repo), // or null if resolution ever genuinely fails. let result: string | null | undefined; expect(() => { result = mod.getEffectiveOnnxRuntimeNodeDir(); }).not.toThrow(); expect(result === null || typeof result === 'string').toBe(true); }); }); describe('decide() — registerHooks gating (#2341 follow-up)', () => { // ensureOnnxRuntimeNodeMatchesSystem() can never install a redirect on // Node < 22.15 (no registerHooks), so decide() must never report `ourDir` // as the effective target there. But transformers' DEFAULT copy still loads // without any hook, so its CUDA major must still be probed: a CUDA-12 host // on Node 22.0–22.14 whose default build already matches has to keep the // GPU it auto-selected before this redirect existed (pre-PR // isCudaAvailable() behavior), not silently fall back to CPU. const fakeDirs = { ourDir: '/fake/our/onnxruntime-node', defaultDir: '/fake/transformers-nested/onnxruntime-node', transformersMain: '/fake/transformers/dist/transformers.node.mjs', }; const soPrefix = (dir: string) => `${dir}/bin/napi-v6/linux`; // System CUDA major is the parameter; the two bundled copies are fixed at // ours=13 / default=12 (the PR's own documented layout). function loadOldNodeResolver(systemMajor: 12 | 13) { return loadResolver({ registerHooks: undefined, platform: 'linux', fakeDirs, existsSync: (p) => p.startsWith(soPrefix(fakeDirs.ourDir)) || p.startsWith(soPrefix(fakeDirs.defaultDir)), execFileSync: (cmd, args) => { if (cmd === 'ldconfig') return `libcublasLt.so.${systemMajor} (libc6,x86-64) => /usr/local/cuda/lib64/libcublasLt.so.${systemMajor}`; if (cmd === 'ldd') { const target = args[0] ?? ''; if (target.startsWith(soPrefix(fakeDirs.ourDir))) return 'libcublasLt.so.13 => /usr/local/cuda-13/lib64/libcublasLt.so.13'; if (target.startsWith(soPrefix(fakeDirs.defaultDir))) return 'libcublasLt.so.12 => /usr/local/cuda-12/lib64/libcublasLt.so.12'; } throw new Error(`unexpected execFileSync(${cmd}, ${JSON.stringify(args)})`); }, }); } it('never reports a redirect target that cannot be installed (CUDA-13 host, mismatched default)', async () => { const mod = await loadOldNodeResolver(13); expect(toPosix(String(mod.getEffectiveOnnxRuntimeNodeDir()))).toBe(fakeDirs.defaultDir); expect(mod.isEffectiveCudaAvailable()).toBe(false); expect(() => mod.ensureOnnxRuntimeNodeMatchesSystem()).not.toThrow(); }); it('still probes the default copy: a CUDA-12 host whose default build matches keeps the GPU', async () => { const mod = await loadOldNodeResolver(12); expect(toPosix(String(mod.getEffectiveOnnxRuntimeNodeDir()))).toBe(fakeDirs.defaultDir); expect(mod.isEffectiveCudaAvailable()).toBe(true); }); }); describe('ensureOnnxRuntimeNodeMatchesSystem — redirect:true (#2341 follow-up)', () => { // The prior test suite never drove decide() into redirect:true (it never // faked createRequire), so the actual installed resolve() closure — the PR's // real shipped behavior — had zero test coverage. Reproduce the PR's own // documented common case: system has CUDA 13, transformers' default build is // CUDA 12, gitnexus' own top-level build is CUDA 13. const fakeDirs = { ourDir: '/fake/our/onnxruntime-node', defaultDir: '/fake/transformers-nested/onnxruntime-node', transformersMain: '/fake/transformers/dist/transformers.node.mjs', }; const soPath = (dir: string) => `${dir}/bin/napi-v6/linux`; // arch-agnostic prefix match below function loadRedirectActiveResolver(registerHooksSpy: unknown) { return loadResolver({ registerHooks: registerHooksSpy, platform: 'linux', fakeDirs, existsSync: (p) => p.startsWith(soPath(fakeDirs.ourDir)) || p.startsWith(soPath(fakeDirs.defaultDir)), execFileSync: (cmd, args) => { if (cmd === 'ldconfig') return 'libcublasLt.so.13 (libc6,x86-64) => /usr/local/cuda/lib64/libcublasLt.so.13'; if (cmd === 'ldd') { const target = args[0] ?? ''; if (target.startsWith(soPath(fakeDirs.ourDir))) { return 'libcublasLt.so.13 => /usr/local/cuda-13/lib64/libcublasLt.so.13'; } if (target.startsWith(soPath(fakeDirs.defaultDir))) { return 'libcublasLt.so.12 => /usr/local/cuda-12/lib64/libcublasLt.so.12'; } } throw new Error(`unexpected execFileSync(${cmd}, ${JSON.stringify(args)})`); }, }); } it('reports the redirect-active effective dir as our own CUDA-13 build', async () => { const mod = await loadRedirectActiveResolver(vi.fn()); expect(mod.getEffectiveOnnxRuntimeNodeDir()).toBe(fakeDirs.ourDir); }); it('installs registerHooks exactly once when the redirect is active', async () => { const spy = vi.fn(); const mod = await loadRedirectActiveResolver(spy); mod.ensureOnnxRuntimeNodeMatchesSystem(); expect(spy).toHaveBeenCalledTimes(1); expect(typeof spy.mock.calls[0][0].resolve).toBe('function'); }); it('the installed resolve() closure redirects onnxruntime-node and onnxruntime-common, and passes through everything else', async () => { const spy = vi.fn(); const mod = await loadRedirectActiveResolver(spy); mod.ensureOnnxRuntimeNodeMatchesSystem(); const resolve = spy.mock.calls[0][0].resolve as ( s: string, c: never, n: (s: string, c: never) => unknown, ) => unknown; const ctx = {} as never; const next = vi.fn(() => ({ url: 'file:///should-not-be-used', shortCircuit: true })); const nodeResult = resolve('onnxruntime-node', ctx, next) as { url: string; shortCircuit: boolean; }; expect(nodeResult).toEqual({ url: expect.stringContaining('/fake/our/onnxruntime-node/index.js'), shortCircuit: true, }); expect(next).not.toHaveBeenCalled(); const commonResult = resolve('onnxruntime-common', ctx, next) as { url: string; shortCircuit: boolean; }; expect(commonResult).toEqual({ url: expect.stringContaining( '/fake/our/onnxruntime-node/node_modules/onnxruntime-common/index.js', ), shortCircuit: true, }); expect(next).not.toHaveBeenCalled(); resolve('some-other-package', ctx, next); expect(next).toHaveBeenCalledWith('some-other-package', ctx); }); it('isEffectiveCudaAvailable() reports true when the redirect-active effective build matches the system', async () => { const mod = await loadRedirectActiveResolver(vi.fn()); expect(mod.isEffectiveCudaAvailable()).toBe(true); }); it('logs the successful redirect at info level (#2341 follow-up)', async () => { const mod = await loadRedirectActiveResolver(vi.fn()); const { _captureLogger } = await import('../../src/core/logger.js'); const cap = _captureLogger(); try { mod.ensureOnnxRuntimeNodeMatchesSystem(); const record = cap .records() .find((r) => r.msg?.includes('Redirected onnxruntime-node to system-matched CUDA build')); expect(record).toBeDefined(); expect(record?.level).toBe(30); // pino 'info' } finally { cap.restore(); } }); it('does not log at info when no redirect is needed (common, expected path)', async () => { // Non-linux -> no system CUDA -> decide() never redirects. const mod = await loadResolver({ registerHooks: vi.fn(), platform: 'darwin' }); const { _captureLogger } = await import('../../src/core/logger.js'); const cap = _captureLogger('debug'); // capture below the default 'info' to prove nothing else fires either try { mod.ensureOnnxRuntimeNodeMatchesSystem(); const infoOrAboveRecords = cap.records().filter((r) => (r.level ?? 0) >= 30); expect(infoOrAboveRecords).toHaveLength(0); } finally { cap.restore(); } }); }); describe('resolveOurOrtNodeDir — on-demand prefix fallback (#2372)', () => { // When gitnexus' own top-level onnxruntime-node was pruned and fetched into the // runtime prefix, `embeddings install --cuda` puts the CUDA build there — so the // redirect must be able to target the prefix copy, not silently run on CPU. const prefixDir = '/fake/prefix-rt'; const prefixOrtNodeDir = '/fake/prefix-rt/node_modules/onnxruntime-node'; const defaultDir = '/fake/transformers-nested/onnxruntime-node'; const soPath = (dir: string): string => `${dir}/bin/napi-v6/linux`; const baseFakeDirs = { ourDir: '/fake/our/onnxruntime-node', // unused: ourResolvable=false defaultDir, transformersMain: '/fake/transformers/dist/transformers.node.mjs', ourResolvable: false, prefixDir, }; const cudaEnv = { existsSync: (p: string): boolean => p.startsWith(soPath(prefixOrtNodeDir)) || p.startsWith(soPath(defaultDir)), execFileSync: (cmd: string, args: string[]): string => { if (cmd === 'ldconfig') return 'libcublasLt.so.13 (libc6,x86-64) => /usr/local/cuda/lib64/libcublasLt.so.13'; if (cmd === 'ldd') { const target = args[0] ?? ''; if (target.startsWith(soPath(prefixOrtNodeDir))) return 'libcublasLt.so.13 => /usr/local/cuda-13/lib64/libcublasLt.so.13'; if (target.startsWith(soPath(defaultDir))) return 'libcublasLt.so.12 => /usr/local/cuda-12/lib64/libcublasLt.so.12'; } throw new Error(`unexpected execFileSync(${cmd}, ${JSON.stringify(args)})`); }, }; it('redirects to the prefix onnxruntime-node when our own top-level was pruned', async () => { process.env.GITNEXUS_EMBEDDING_RUNTIME_DIR = prefixDir; const mod = await loadResolver({ registerHooks: vi.fn(), platform: 'linux', fakeDirs: { ...baseFakeDirs, prefixOrtNodeDir }, ...cudaEnv, }); expect(toPosix(String(mod.getEffectiveOnnxRuntimeNodeDir()))).toBe(prefixOrtNodeDir); }); it('leaves the effective dir at the default when neither our copy nor the prefix resolves', async () => { process.env.GITNEXUS_EMBEDDING_RUNTIME_DIR = prefixDir; const mod = await loadResolver({ registerHooks: vi.fn(), platform: 'linux', fakeDirs: { ...baseFakeDirs }, // no prefixOrtNodeDir → prefix require misses too ...cudaEnv, }); expect(toPosix(String(mod.getEffectiveOnnxRuntimeNodeDir()))).toBe(defaultDir); }); }); describe('cudaRedirectDoctorStatus (#2341 follow-up)', () => { it('reports n/a when there is no system CUDA', async () => { const mod = await loadResolver({ registerHooks: vi.fn(), platform: 'darwin' }); expect(mod.cudaRedirectDoctorStatus()).toEqual({ status: 'n/a (no system CUDA detected)', detail: null, }); }); it('reports the redirect-active status with the effective dir as detail', async () => { const fakeDirs = { ourDir: '/fake/our/onnxruntime-node-doctor', defaultDir: '/fake/transformers-nested/onnxruntime-node-doctor', transformersMain: '/fake/transformers/doctor/index.js', }; const soPath = (dir: string) => `${dir}/bin/napi-v6/linux`; const mod = await loadResolver({ registerHooks: vi.fn(), platform: 'linux', fakeDirs, existsSync: (p) => p.startsWith(soPath(fakeDirs.ourDir)) || p.startsWith(soPath(fakeDirs.defaultDir)), execFileSync: (cmd, args) => { if (cmd === 'ldconfig') return 'libcublasLt.so.13 (libc6,x86-64) => /usr/local/cuda/lib64/libcublasLt.so.13'; if (cmd === 'ldd') { const target = args[0] ?? ''; if (target.startsWith(soPath(fakeDirs.ourDir))) return 'libcublasLt.so.13 => /a/libcublasLt.so.13'; if (target.startsWith(soPath(fakeDirs.defaultDir))) return 'libcublasLt.so.12 => /a/libcublasLt.so.12'; } throw new Error(`unexpected execFileSync(${cmd}, ${JSON.stringify(args)})`); }, }); expect(mod.cudaRedirectDoctorStatus()).toEqual({ status: expect.stringContaining('redirected onnxruntime-node to the CUDA 13 build'), detail: fakeDirs.ourDir, }); }); it('reports a mismatch status (with no fix available) when neither copy ships a matching CUDA provider', async () => { const mod = await loadResolver({ registerHooks: vi.fn(), platform: 'linux', existsSync: () => false, // no onnxruntime-node copy ships a CUDA provider .so at all execFileSync: (cmd) => { if (cmd === 'ldconfig') return 'libcublasLt.so.13 (libc6,x86-64) => /usr/local/cuda/lib64/libcublasLt.so.13'; throw new Error('ldd should not be reached when existsSync is false'); }, }); // `detail` (the resolved effectiveDir) isn't asserted here — resolveDefaultOrtNodeDir() // isn't mocked in this test, so it resolves against this sandbox's real // node_modules and its exact value isn't the point of this case; the // redirect-active test above already covers `detail` precisely. expect(mod.cudaRedirectDoctorStatus().status).toContain( 'no CUDA 13-matched onnxruntime-node build found', ); }); }); describe('isEffectiveCudaAvailable — no redundant subprocess spawns (#2341 follow-up)', () => { it('probes ldconfig/ldd only once total, regardless of how many times the effective dir and CUDA match are queried', async () => { const fakeDirs = { ourDir: '/fake/our/onnxruntime-node-u8', defaultDir: '/fake/transformers-nested/onnxruntime-node-u8', transformersMain: '/fake/transformers/u8/index.js', }; const soPath = (dir: string) => `${dir}/bin/napi-v6/linux`; const existsSyncSpy = vi.fn( (p: string) => p.startsWith(soPath(fakeDirs.ourDir)) || p.startsWith(soPath(fakeDirs.defaultDir)), ); const execFileSyncSpy = vi.fn((cmd: string, args: string[]) => { if (cmd === 'ldconfig') return 'libcublasLt.so.13 (libc6,x86-64) => /usr/local/cuda/lib64/libcublasLt.so.13'; if (cmd === 'ldd') { const target = args[0] ?? ''; if (target.startsWith(soPath(fakeDirs.ourDir))) { return 'libcublasLt.so.13 => /usr/local/cuda-13/lib64/libcublasLt.so.13'; } if (target.startsWith(soPath(fakeDirs.defaultDir))) { return 'libcublasLt.so.12 => /usr/local/cuda-12/lib64/libcublasLt.so.12'; } } throw new Error(`unexpected execFileSync(${cmd}, ${JSON.stringify(args)})`); }); const mod = await loadResolver({ registerHooks: vi.fn(), platform: 'linux', fakeDirs, existsSync: existsSyncSpy, execFileSync: execFileSyncSpy, }); // Query the decision through both public entry points, each more than once. mod.getEffectiveOnnxRuntimeNodeDir(); mod.isEffectiveCudaAvailable(); mod.getEffectiveOnnxRuntimeNodeDir(); expect(mod.isEffectiveCudaAvailable()).toBe(true); // decide() is memoized: exactly one ldconfig call (system major) and one // ldd call per onnxruntime-node dir actually probed (default + ours) — // never re-invoked across the 4 queries above. const ldconfigCalls = execFileSyncSpy.mock.calls.filter(([cmd]) => cmd === 'ldconfig'); const lddCalls = execFileSyncSpy.mock.calls.filter(([cmd]) => cmd === 'ldd'); expect(ldconfigCalls).toHaveLength(1); expect(lddCalls).toHaveLength(2); // defaultDir once, ourDir once }); }); describe('decide() — ourDir checked independently of defaultDir (#2341 follow-up)', () => { it("picks ourDir as the effective target when transformers' own onnxruntime-node resolution fails outright", async () => { const fakeDirs = { ourDir: '/fake/our/onnxruntime-node-u5', defaultDir: '/fake/unreachable/onnxruntime-node-u5', transformersMain: '/fake/transformers/u5/index.js', defaultResolvable: false, // createRequire(transformersMain).resolve(...) throws -> defaultDir stays null }; const soPrefix = (dir: string) => `${dir}/bin/napi-v6/linux`; const mod = await loadResolver({ registerHooks: vi.fn(), platform: 'linux', fakeDirs, existsSync: (p) => p.startsWith(soPrefix(fakeDirs.ourDir)), execFileSync: (cmd, args) => { if (cmd === 'ldconfig') { return 'libcublasLt.so.13 (libc6,x86-64) => /usr/local/cuda/lib64/libcublasLt.so.13'; } if (cmd === 'ldd' && (args[0] ?? '').startsWith(soPrefix(fakeDirs.ourDir))) { return 'libcublasLt.so.13 => /usr/local/cuda-13/lib64/libcublasLt.so.13'; } throw new Error(`unexpected execFileSync(${cmd}, ${JSON.stringify(args)})`); }, }); // Before this fix, the ourDir fallback lookup was nested inside // `if (systemMajor != null && defaultDir)`, so a null defaultDir skipped // checking ourDir entirely and this would incorrectly return null. expect(mod.getEffectiveOnnxRuntimeNodeDir()).toBe(fakeDirs.ourDir); }); }); describe('cross-platform path handling (#2341 follow-up)', () => { // This test file was added to cross-platform-tests.ts's PLATFORM_LOGIC list // (so it now runs on the Windows CI matrix, not just Ubuntu). Node's `path` // module is bound to path.win32 on a real Windows host regardless of any // process.platform stub — so the resolver's own join(effectiveDir, // 'package.json') calls backslash-normalize even when these tests fake // platform: 'linux'. forceWin32Path proves the toPosix() normalization // added above actually handles that, rather than merely being argued for. const fakeDirs = { ourDir: '/fake/our/onnxruntime-node', defaultDir: '/fake/transformers-nested/onnxruntime-node', transformersMain: '/fake/transformers/dist/transformers.node.mjs', }; const soPath = (dir: string) => `${dir}/bin/napi-v6/linux`; it('resolves the redirect-active dir and installs the resolve() closure correctly even when join()/dirname() backslash-normalize (simulated real Windows)', async () => { const spy = vi.fn(); const mod = await loadResolver({ registerHooks: spy, platform: 'linux', fakeDirs, forceWin32Path: true, existsSync: (p) => p.startsWith(soPath(fakeDirs.ourDir)) || p.startsWith(soPath(fakeDirs.defaultDir)), execFileSync: (cmd, args) => { if (cmd === 'ldconfig') return 'libcublasLt.so.13 (libc6,x86-64) => /usr/local/cuda/lib64/libcublasLt.so.13'; if (cmd === 'ldd') { const target = args[0] ?? ''; if (target.startsWith(soPath(fakeDirs.ourDir))) return 'libcublasLt.so.13 => /a/libcublasLt.so.13'; if (target.startsWith(soPath(fakeDirs.defaultDir))) return 'libcublasLt.so.12 => /a/libcublasLt.so.12'; } throw new Error(`unexpected execFileSync(${cmd}, ${JSON.stringify(args)})`); }, }); expect(mod.getEffectiveOnnxRuntimeNodeDir()).toBe(fakeDirs.ourDir); expect(mod.isEffectiveCudaAvailable()).toBe(true); mod.ensureOnnxRuntimeNodeMatchesSystem(); // The real proof: registerHooks must actually fire. Before the toPosix() // fix, the createRequire dispatcher's `from === ...` comparison would // mismatch against a backslash-joined `from` under forceWin32Path, // ensureOnnxRuntimeNodeMatchesSystem's outer try/catch would silently // swallow the resulting MODULE_NOT_FOUND, and this would never fire. expect(spy).toHaveBeenCalledTimes(1); const resolve = spy.mock.calls[0][0].resolve as ( s: string, c: never, n: (s: string, c: never) => unknown, ) => unknown; const ctx = {} as never; const next = vi.fn(); const nodeResult = resolve('onnxruntime-node', ctx, next) as { url: string; shortCircuit: boolean; }; expect(nodeResult.shortCircuit).toBe(true); expect(toPosix(nodeResult.url)).toContain('/fake/our/onnxruntime-node/index.js'); expect(next).not.toHaveBeenCalled(); }); });