GitNexus/gitnexus/test/unit/lazy-action.test.ts
rome-xi 34b873696b
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(cli): recover pnpm and Bun invocation paths (#3721)
* fix(hooks): skip a pnpm shim that cannot run when picking the runner

Fixes #3717

* test(hooks): isolate runner liveness checks

Inject pnpm liveness and bunx presence in resolver unit tests so they do
not depend on installed package managers. Use fixture-only PATHs and
POSIX/Windows launchers to cover failed pnpm probes, banner output,
fallback execution, argument forwarding, and child exit codes.

* fix(cli): prepare native binary before identity

Restore skipped LadybugDB install copies before capturing analyzer
identity, while keeping native probes and analyzer imports after it.
Exercise real npm, pnpm and Bun tarball installs on Linux, macOS and
Windows so runner selection is backed by successful indexing.

---------

Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
Co-authored-by: Gergő Magyar <gergomagyar@icloud.com>
2026-10-25 21:05:02 +00:00

150 lines
5.3 KiB
TypeScript

import { describe, expect, it, vi } from 'vitest';
import { createAnalyzerLbugLazyAction, createLazyAction } from '../../src/cli/lazy-action.js';
const { checkLbugNativeMock, prepareLbugNativeBinaryMock } = vi.hoisted(() => ({
prepareLbugNativeBinaryMock: vi.fn(),
checkLbugNativeMock: vi.fn<typeof import('../../src/core/lbug/native-check.js').checkLbugNative>(
() => ({ ok: true }),
),
}));
vi.mock('../../src/core/lbug/native-check.js', () => ({
checkLbugNative: checkLbugNativeMock,
prepareLbugNativeBinary: prepareLbugNativeBinaryMock,
}));
describe('createLazyAction', () => {
it('does not import target module until invoked', async () => {
const loader = vi.fn(async () => ({
run: vi.fn(async () => 'ok'),
}));
const action = createLazyAction(loader, 'run');
expect(loader).not.toHaveBeenCalled();
await expect(action('arg-1')).resolves.toBeUndefined();
expect(loader).toHaveBeenCalledTimes(1);
});
it('throws a clear error when export is not a function', async () => {
const action = createLazyAction(async () => ({ notAFunction: 'string-value' }), 'notAFunction');
await expect(action()).rejects.toThrow('notAFunction');
});
});
describe('createLbugLazyAction', () => {
it('fails before importing the target module when LadybugDB native cannot load', async () => {
checkLbugNativeMock.mockReturnValueOnce({
ok: false,
message:
'LadybugDB native binary (lbugjs.node) exists but failed to load:\n' + ' dlopen failed',
});
const stderrSpy = vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
process.exitCode = undefined;
const loader = vi.fn(async () => ({
run: vi.fn(async () => 'ok'),
}));
try {
const { createLbugLazyAction } = await import('../../src/cli/lazy-action.js');
const action = createLbugLazyAction(loader, 'run');
await expect(action('arg-1')).resolves.toBeUndefined();
expect(loader).not.toHaveBeenCalled();
expect(process.exitCode).toBe(1);
expect(stderrSpy).toHaveBeenCalledWith(
expect.stringContaining('LadybugDB native binary (lbugjs.node) exists but failed to load:'),
);
} finally {
stderrSpy.mockRestore();
process.exitCode = undefined;
}
});
});
describe('createAnalyzerLbugLazyAction', () => {
it('captures identity before probing native code or importing the analyzer graph', async () => {
const events: string[] = [];
prepareLbugNativeBinaryMock.mockImplementationOnce(() => events.push('binary-prepared'));
checkLbugNativeMock.mockImplementationOnce(() => {
events.push('native-probed');
return { ok: true };
});
const receipt = { schemaVersion: 4 };
const run = vi.fn(async () => undefined);
const identityLoader = vi.fn(async () => {
events.push('identity-module');
return {
captureAnalyzerIdentityBeforeLoad: async (_url: string, loader: () => Promise<unknown>) => {
events.push('receipt-captured');
const loaded = await loader();
return { runnerIdentity: receipt, loaded };
},
};
});
const analyzerLoader = vi.fn(async () => {
events.push('analyzer-module');
return { run };
});
const action = createAnalyzerLbugLazyAction(
identityLoader as never,
analyzerLoader,
'run',
'file:///fixture/dist/cli/index.js',
);
await action('repo', { force: true });
expect(events).toEqual([
'binary-prepared',
'identity-module',
'receipt-captured',
'native-probed',
'analyzer-module',
]);
expect(run).toHaveBeenCalledWith(receipt, 'repo', { force: true });
});
it('sets exit code 1 and skips the analyzer import when native load fails', async () => {
// Regression guard for #2441: a LadybugDB native-load failure must fail
// closed — no analyzer import, no index write, non-zero exit — not the
// pre-fix "print help then exit 0" silent success. Mirrors the
// createLbugLazyAction failure test above for the analyze-only wrapper.
checkLbugNativeMock.mockReturnValueOnce({
ok: false,
message:
'LadybugDB native binary (lbugjs.node) exists but failed to load:\n' + ' dlopen failed',
});
const stderrSpy = vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
process.exitCode = undefined;
const run = vi.fn(async () => undefined);
const analyzerLoader = vi.fn(async () => ({ run }));
const identityLoader = vi.fn(async () => ({
captureAnalyzerIdentityBeforeLoad: async (_url: string, loader: () => Promise<unknown>) => {
const loaded = await loader();
return { runnerIdentity: { schemaVersion: 4 }, loaded };
},
}));
const action = createAnalyzerLbugLazyAction(
identityLoader as never,
analyzerLoader,
'run',
'file:///fixture/dist/cli/index.js',
);
try {
await expect(action('repo', { force: true })).resolves.toBeUndefined();
expect(analyzerLoader).not.toHaveBeenCalled();
expect(run).not.toHaveBeenCalled();
expect(process.exitCode).toBe(1);
expect(stderrSpy).toHaveBeenCalledWith(
expect.stringContaining('LadybugDB native binary (lbugjs.node) exists but failed to load:'),
);
} finally {
stderrSpy.mockRestore();
process.exitCode = undefined;
}
});
});