mirror of
https://github.com/proffesor-for-testing/agentic-qe.git
synced 2026-09-19 08:45:47 +08:00
a42c3073e0
ADR-150 graceful-degradation core. AQE's native/optional deps (@ruvector/*,
hnswlib-node, rvlite) must degrade — not crash — when a platform binary or a
Dependabot-pruned lockfile is missing.
- src/shared/optional-module.ts: loadOptionalModule(name, req?) → {available}|
{degraded}. Fixes the #528 anti-pattern in the existing ad-hoc
try{require}catch{} (6+ ruvector wrappers): degrades ONLY on genuine absence
(MODULE_NOT_FOUND) and RE-THROWS real load failures (ABI mismatch / corrupt
.node) instead of masking them. 8 tests incl. the rethrow property.
- .github/workflows/degraded-mode.yml: the absent drill — npm ci --omit=optional
--ignore-scripts, then run the degraded-path tests. Pairs with the regular
install job (proves both paths, #528).
Follow-up (held, CI-verified): migrate the ad-hoc sites + the static import in
sona-wrapper.ts onto loadOptionalModule (changes a production native-load path).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
59 lines
2.6 KiB
TypeScript
59 lines
2.6 KiB
TypeScript
/**
|
|
* Graceful optional-module loading (A9). Degrade on genuine absence, but NEVER
|
|
* mask a real load failure (the #528 lesson). Require is injected — no real
|
|
* native deps needed.
|
|
*/
|
|
import { describe, it, expect } from 'vitest';
|
|
import { loadOptionalModule, optionalModule, isModuleAbsent } from '../../../src/shared/optional-module.js';
|
|
|
|
const absentReq = ((name: string) => { const e = new Error(`Cannot find module '${name}'`) as Error & { code?: string }; e.code = 'MODULE_NOT_FOUND'; throw e; }) as unknown as NodeRequire;
|
|
const brokenReq = (() => { const e = new Error('dlopen failed: wrong ELF class') as Error & { code?: string }; e.code = 'ERR_DLOPEN_FAILED'; throw e; }) as unknown as NodeRequire;
|
|
const okReq = (() => ({ hello: 'world' })) as unknown as NodeRequire;
|
|
|
|
describe('isModuleAbsent', () => {
|
|
it('should recognize MODULE_NOT_FOUND as absence', () => {
|
|
expect(isModuleAbsent({ code: 'MODULE_NOT_FOUND' })).toBe(true);
|
|
expect(isModuleAbsent({ code: 'ERR_MODULE_NOT_FOUND' })).toBe(true);
|
|
expect(isModuleAbsent(new Error("Cannot find module 'x'"))).toBe(true);
|
|
});
|
|
|
|
it('should NOT treat a real load failure as absence', () => {
|
|
expect(isModuleAbsent({ code: 'ERR_DLOPEN_FAILED', message: 'wrong ELF class' })).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('loadOptionalModule', () => {
|
|
it('should return the module when present', () => {
|
|
const r = loadOptionalModule<{ hello: string }>('whatever', okReq);
|
|
expect(r).toMatchObject({ available: true, degraded: false });
|
|
if (r.available) expect(r.module.hello).toBe('world');
|
|
});
|
|
|
|
it('should DEGRADE (no throw) when the module is genuinely absent', () => {
|
|
const r = loadOptionalModule('@ruvector/not-installed', absentReq);
|
|
expect(r).toMatchObject({ available: false, degraded: true, name: '@ruvector/not-installed' });
|
|
});
|
|
|
|
it('should RE-THROW a real load failure — never mask it (#528)', () => {
|
|
expect(() => loadOptionalModule('@ruvector/broken', brokenReq)).toThrow(/dlopen failed/);
|
|
});
|
|
|
|
it('should degrade for a truly-absent real module via the default require', () => {
|
|
const r = loadOptionalModule('@ruvector/definitely-not-installed-xyz-9999');
|
|
expect(r.available).toBe(false);
|
|
expect(r.degraded).toBe(true);
|
|
});
|
|
|
|
it('should load a real present module via the default require', () => {
|
|
const r = loadOptionalModule<typeof import('node:os')>('node:os');
|
|
expect(r.available).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('optionalModule', () => {
|
|
it('should return the module or undefined', () => {
|
|
expect(optionalModule('x', okReq)).toMatchObject({ hello: 'world' });
|
|
expect(optionalModule('x', absentReq)).toBeUndefined();
|
|
});
|
|
});
|