test(sdk): enforce runtime bridge seam and explicit no-fallback behavior
This commit is contained in:
@@ -234,6 +234,32 @@ describe('GSDTransport', () => {
|
||||
expect(adapters.execSubprocessJson).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('fails when command is unregistered and subprocess fallback is disabled', async () => {
|
||||
const registry = new QueryRegistry();
|
||||
|
||||
const adapters = {
|
||||
dispatchNative: vi.fn(async () => ({ data: { ok: true } })),
|
||||
execSubprocessJson: vi.fn(async () => ({ ok: 'fallback' })),
|
||||
execSubprocessRaw: vi.fn(async () => 'fallback-raw'),
|
||||
};
|
||||
|
||||
const transport = new GSDTransport(registry, adapters);
|
||||
|
||||
await expect(transport.run({
|
||||
legacyCommand: 'unknown',
|
||||
legacyArgs: [],
|
||||
registryCommand: 'unknown',
|
||||
registryArgs: [],
|
||||
mode: 'json',
|
||||
projectDir: '/tmp',
|
||||
}, {
|
||||
preferNative: true,
|
||||
allowFallbackToSubprocess: false,
|
||||
})).rejects.toThrow("Subprocess fallback disabled");
|
||||
|
||||
expect(adapters.execSubprocessJson).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('forces raw subprocess path when workstream present and mode is raw', async () => {
|
||||
const registry = new QueryRegistry();
|
||||
registry.register('commit', async () => ({ data: { hash: 'abc' } }));
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { QueryResult } from './query/utils.js';
|
||||
import type { QueryRegistry } from './query/registry.js';
|
||||
import type { TransportMode } from './gsd-transport-policy.js';
|
||||
import { toFailureSignal } from './query-failure-classification.js';
|
||||
import { GSDToolsError } from './gsd-tools-error.js';
|
||||
|
||||
export interface TransportRequest {
|
||||
legacyCommand: string;
|
||||
@@ -52,7 +53,16 @@ export class GSDTransport {
|
||||
onDecision?.({ dispatchMode: 'subprocess', reason: 'native_failure_fallback' });
|
||||
}
|
||||
} else {
|
||||
onDecision?.({ dispatchMode: 'subprocess', reason: this.subprocessReason(request, policy) });
|
||||
const reason = this.subprocessReason(request, policy);
|
||||
onDecision?.({ dispatchMode: 'subprocess', reason });
|
||||
if (!policy.allowFallbackToSubprocess) {
|
||||
throw GSDToolsError.failure(
|
||||
`Subprocess fallback disabled: command '${request.registryCommand}' cannot run without native dispatch`,
|
||||
request.legacyCommand,
|
||||
request.legacyArgs,
|
||||
null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return this.dispatchSubprocess(request);
|
||||
|
||||
20
sdk/src/query-runtime-seam-coverage.test.ts
Normal file
20
sdk/src/query-runtime-seam-coverage.test.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { createGSDToolsRuntime } from './query-gsd-tools-runtime.js';
|
||||
|
||||
describe('SDK Runtime Bridge seam coverage', () => {
|
||||
it('exposes bridge as the single runtime seam', () => {
|
||||
const runtime = createGSDToolsRuntime({
|
||||
projectDir: '/tmp/project',
|
||||
gsdToolsPath: '/tmp/gsd-tools.cjs',
|
||||
timeoutMs: 1_000,
|
||||
shouldUseNativeQuery: () => true,
|
||||
execJsonFallback: vi.fn(async () => ({})),
|
||||
execRawFallback: vi.fn(async () => ''),
|
||||
});
|
||||
|
||||
expect(Object.keys(runtime)).toEqual(['bridge']);
|
||||
expect(typeof runtime.bridge.resolve).toBe('function');
|
||||
expect(typeof runtime.bridge.execute).toBe('function');
|
||||
expect(typeof runtime.bridge.dispatchHotpath).toBe('function');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user