GitNexus/gitnexus/test/unit/lbug-native-check.test.ts
Gergő Magyar 5e012c373b
fix(cli): detect missing LadybugDB native binary at startup with actionable guidance (#835) (#1837)
* fix(cli): detect missing LadybugDB native binary at startup with actionable guidance (#835)

Add checkLbugNative() pre-flight that verifies lbugjs.node exists before
any command transitively imports @ladybugdb/core. When missing (bun default
install, --ignore-scripts), prints repair instructions instead of crashing
with ERR_DLOPEN_FAILED. Also enhances `gitnexus doctor` to probe the
native binary status.

* fix(review): guard eval-server, un-guard status command

eval-server transitively loads @ladybugdb/core and needs the native
binary check. status only reads filesystem metadata and should remain
accessible when the binary is missing.

* fix(lint): use console.log instead of console.error in native check gate

The project eslint config only allows console.log.

* fix(cli): route native-check to stderr and validate binary loadability

Fixes two Codex adversarial review findings:

1. Native-check failure message now goes to process.stderr.write instead
   of console.log, preventing MCP stdout protocol contamination.

2. checkLbugNative now attempts a controlled require() probe after the
   existence check. Truncated, ABI-mismatched, or wrong-platform binaries
   produce actionable guidance instead of passing through to crash at
   process.dlopen.

---------

Co-authored-by: Test <test@example.com>
2026-05-26 17:10:08 +01:00

46 lines
1.6 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import os from 'os';
import path from 'path';
import fs from 'fs/promises';
import { checkLbugNative } from '../../src/core/lbug/native-check.js';
describe('checkLbugNative', () => {
it('returns ok:true when the real @ladybugdb/core binary is present', () => {
const result = checkLbugNative();
expect(result.ok).toBe(true);
expect(result.binaryPath).toBeDefined();
expect(result.message).toBeUndefined();
});
it('returns ok:false with repair instructions when lbugjs.node is missing', async () => {
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'lbug-check-'));
try {
await fs.writeFile(path.join(tmpDir, 'install.js'), '');
const result = checkLbugNative(tmpDir);
expect(result.ok).toBe(false);
expect(result.message).toContain('missing');
expect(result.message).toContain('install.js');
expect(result.message).toContain('trustedDependencies');
expect(result.message).toContain('ignore-scripts');
} finally {
await fs.rm(tmpDir, { recursive: true, force: true });
}
});
it('returns ok:false when lbugjs.node exists but is unloadable (zero-byte)', async () => {
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'lbug-check-'));
try {
await fs.writeFile(path.join(tmpDir, 'lbugjs.node'), Buffer.alloc(0));
const result = checkLbugNative(tmpDir);
expect(result.ok).toBe(false);
expect(result.message).toContain('failed to load');
expect(result.message).toContain('install.js');
} finally {
await fs.rm(tmpDir, { recursive: true, force: true });
}
});
});