GitNexus/gitnexus/test/unit/doctor-format.test.ts
Gergő Magyar fca3494807
Some checks are pending
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
Gitleaks / gitleaks (push) Waiting to run
Publish / Classify release event (push) Waiting to run
Publish / RC guard (marker + release-PR skip) (push) Blocked by required conditions
Publish / ci (push) Blocked by required conditions
Publish / Publish to npm (push) Blocked by required conditions
Publish / Build & Push RC Docker images (push) Blocked by required conditions
Scorecard / Scorecard analysis (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-cli) (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-web) (push) Waiting to run
fix(embeddings): guard local ONNX runtime on macOS Intel before transformers.js import (#1987)
* fix(embeddings): guard local ONNX runtime on macOS Intel before transformers.js import

macOS Intel (darwin/x64) crashed on `gitnexus analyze --embeddings` with a raw
`Cannot find module .../bin/napi-v6/darwin/x64/onnxruntime_binding.node`: both
embedders imported @huggingface/transformers at module scope, which loads
onnxruntime-node and resolves the (unshipped) native binding before any backend
could be selected. ONNX_WEB_BACKEND=wasm could not help (#1516).

- Add a native-free runtime-support guard (getLocalEmbeddingRuntimeBlocker) that
  returns a clear, actionable message on darwin/x64 and null elsewhere.
- Convert both the core and MCP embedders to type-only transformers imports plus
  a guarded lazy `await import()`; throw the blocker in initEmbedder before any
  transformers.js / onnxruntime-node resolution. HTTP mode is unaffected.
- Surface the blocker cleanly in the analyze CLI instead of the misleading
  "installation may be corrupt" module-not-found hint.
- Add unit tests: guard DI, lazy-import timing, core+MCP darwin/x64 rejection,
  and HTTP mode not blocked.

Refs #1515, #1516

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(doctor): surface macOS Intel local-embedding limitation

`gitnexus doctor` now reports whether the local embedding runtime can load on
the current platform. macOS Intel (darwin/x64) users see up front that local
embeddings are unavailable — plus the recommended alternatives — instead of
only discovering it when `analyze --embeddings` fails (#1515).

The Embeddings section gains a "Support" line; on a blocked platform the full
guidance (reused from getLocalEmbeddingRuntimeBlocker, single source of truth)
is written to stderr. doctor stays import-safe — it never loads transformers.js
or onnxruntime-node, so it runs cleanly on macOS Intel.

Refs #1515

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(embeddings): close #1515 guard coverage gaps + PR #1987 review polish

Resolves the maintainer tri-review feedback on PR #1987:

- Add the analyze error-branch test (new analyze-local-embedding-error.test.ts):
  a darwin/x64 blocker routes to the clean local-embedding-unsupported message
  (exit 1), not the module-not-found "installation may be corrupt" branch, and
  wins over isHfDownloadFailure even when both match (guards the reorder below).
- Cover the MCP embedQuery darwin/x64 paths — HTTP bypass via httpEmbedQuery
  without importing transformers, and local-mode rejection before the import.
- Make the "defaults platform/arch" guard test falsifiable by stubbing the
  platform, instead of asserting null === null on the CI host.
- analyze.ts: evaluate the blocker-message branch before the network-heuristic
  isHfDownloadFailure branch so the explicit platform message takes priority.
- runtime-support.ts: the blocker message now also notes GITNEXUS_EMBEDDING_DEVICE
  =wasm/cpu cannot help, not only ONNX_WEB_BACKEND=wasm.
- doctor.ts: resolve platform/arch once instead of re-resolving after the guard.

Refs #1515, #1516

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 09:46:16 +01:00

57 lines
2 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import { displayWidth, localEmbeddingDoctorStatus, padDisplayEnd } from '../../src/cli/doctor.js';
describe('doctor output formatting', () => {
it('keeps ASCII padding equivalent to String.padEnd', () => {
expect(displayWidth('OS:')).toBe(3);
expect(padDisplayEnd('OS:', 10)).toBe('OS:'.padEnd(10));
});
it('pads CJK labels by terminal display width, not code-unit length', () => {
const padded = padDisplayEnd('系统:', 10);
expect(displayWidth('系统:')).toBe(6);
expect(displayWidth(padded)).toBe(10);
expect(padded).toBe('系统: ');
});
it('does not truncate labels that are already wider than the target width', () => {
expect(padDisplayEnd('图存储:', 4)).toBe('图存储:');
});
});
describe('doctor embedding-runtime support status', () => {
it('flags local embeddings as unavailable on macOS Intel (darwin/x64)', () => {
const { status, detail } = localEmbeddingDoctorStatus({
httpMode: false,
platform: 'darwin',
arch: 'x64',
});
expect(status).toBe('✗ local embeddings unavailable on darwin/x64');
expect(detail).not.toBeNull();
expect(detail).toMatch(/macOS Intel/);
expect(detail).toMatch(/native binding/i);
});
it('reports local embeddings as supported on darwin/arm64, linux/x64, and win32/x64', () => {
for (const [platform, arch] of [
['darwin', 'arm64'],
['linux', 'x64'],
['win32', 'x64'],
] as Array<[NodeJS.Platform, NodeJS.Architecture]>) {
const { status, detail } = localEmbeddingDoctorStatus({ httpMode: false, platform, arch });
expect(status).toBe('✓ local embeddings supported');
expect(detail).toBeNull();
}
});
it('reports HTTP backend as configured and never blocks on platform', () => {
const { status, detail } = localEmbeddingDoctorStatus({
httpMode: true,
platform: 'darwin',
arch: 'x64',
});
expect(status).toBe('✓ http endpoint configured');
expect(detail).toBeNull();
});
});