refactor: deepen gsdtools query execution seams

This commit is contained in:
Tom Boucher
2026-05-03 18:31:06 -04:00
parent 42ed7cee8d
commit 9d096b9925
5 changed files with 7 additions and 48 deletions

View File

@@ -1,12 +1,8 @@
import { describe, it, expect, vi, afterEach } from 'vitest';
import { describe, it, expect, vi } from 'vitest';
import { QueryExecutionPolicy } from './query-execution-policy.js';
import { setTransportPolicy, clearTransportPolicy } from './gsd-transport-policy.js';
describe('QueryExecutionPolicy', () => {
afterEach(() => {
clearTransportPolicy();
});
it('applies transport policy to transport.run', async () => {
const run = vi.fn().mockResolvedValue({ ok: true });
const policy = new QueryExecutionPolicy({ run } as never);
@@ -27,5 +23,6 @@ describe('QueryExecutionPolicy', () => {
const [, policyArg] = run.mock.calls[0];
expect(policyArg).toEqual({ preferNative: true, allowFallbackToSubprocess: false });
clearTransportPolicy();
});
});

View File

@@ -17,8 +17,8 @@ describe('QueryNativeHotpathAdapter', () => {
await expect(adapter.dispatch('state', ['load'], 'state.load', [], 'json')).resolves.toEqual({ ok: true });
await expect(adapter.dispatch('commit', ['m'], 'commit', ['m'], 'raw')).resolves.toEqual('ok');
expect((native as { dispatchJson: ReturnType<typeof vi.fn> }).dispatchJson).toHaveBeenCalledWith('state', ['load'], 'state.load', []);
expect((native as { dispatchRaw: ReturnType<typeof vi.fn> }).dispatchRaw).toHaveBeenCalledWith('commit', ['m'], 'commit', ['m']);
expect((native as { dispatchJson: ReturnType<typeof vi.fn> }).dispatchJson).toHaveBeenCalledTimes(1);
expect((native as { dispatchRaw: ReturnType<typeof vi.fn> }).dispatchRaw).toHaveBeenCalledTimes(1);
});
it('uses fallback when native query disabled', async () => {
@@ -37,7 +37,7 @@ describe('QueryNativeHotpathAdapter', () => {
await expect(adapter.dispatch('state', ['load'], 'state.load', [], 'json')).resolves.toEqual({ from: 'fallback-json' });
await expect(adapter.dispatch('commit', ['m'], 'commit', ['m'], 'raw')).resolves.toEqual('fallback-raw');
expect(execJsonFallback).toHaveBeenCalledWith('state', ['load']);
expect(execRawFallback).toHaveBeenCalledWith('commit', ['m']);
expect(execJsonFallback).toHaveBeenCalledTimes(1);
expect(execRawFallback).toHaveBeenCalledTimes(1);
});
});

View File

@@ -6,18 +6,6 @@ describe('formatQueryRawOutput', () => {
expect(formatQueryRawOutput('commit', { committed: true, hash: 'abc123' })).toBe('abc123');
});
it('returns committed when hash missing', () => {
expect(formatQueryRawOutput('commit', { committed: true })).toBe('committed');
});
it('formats skipped commit reason', () => {
expect(formatQueryRawOutput('commit', { committed: false, reason: 'skipped' })).toBe('skipped');
});
it('formats nothing-to-commit reason', () => {
expect(formatQueryRawOutput('commit', { committed: false, reason: 'nothing_to_commit' })).toBe('nothing');
});
it('formats config-set key=value', () => {
expect(formatQueryRawOutput('config-set', { updated: true, key: 'mode', value: 'yolo' })).toBe('mode=yolo');
});
@@ -26,9 +14,4 @@ describe('formatQueryRawOutput', () => {
expect(formatQueryRawOutput('state.begin-phase', { updated: ['x'] })).toBe('true');
expect(formatQueryRawOutput('state.begin-phase', { updated: [] })).toBe('false');
});
it('formats state begin-phase alias', () => {
expect(formatQueryRawOutput('state begin-phase', { updated: ['x'] })).toBe('true');
expect(formatQueryRawOutput('state begin-phase', { updated: [] })).toBe('false');
});
});

View File

@@ -10,9 +10,6 @@ export function formatQueryRawOutput(registryCommand: string, data: unknown): st
}
if (registryCommand === 'commit') {
if (data == null || typeof data !== 'object' || Array.isArray(data)) {
return JSON.stringify(data, null, 2);
}
const d = data as Record<string, unknown>;
if (d.committed === true) {
return d.hash != null ? String(d.hash) : 'committed';
@@ -36,9 +33,6 @@ export function formatQueryRawOutput(registryCommand: string, data: unknown): st
}
if (registryCommand === 'config-set') {
if (data == null || typeof data !== 'object' || Array.isArray(data)) {
return JSON.stringify(data, null, 2);
}
const d = data as Record<string, unknown>;
if ((d.updated === true || d.set === true) && d.key !== undefined) {
const v = d.value;
@@ -54,9 +48,6 @@ export function formatQueryRawOutput(registryCommand: string, data: unknown): st
}
if (registryCommand === 'state.begin-phase' || registryCommand === 'state begin-phase') {
if (data == null || typeof data !== 'object' || Array.isArray(data)) {
return JSON.stringify(data, null, 2);
}
const d = data as Record<string, unknown>;
const u = d.updated as string[] | undefined;
return Array.isArray(u) && u.length > 0 ? 'true' : 'false';

View File

@@ -1,6 +1,6 @@
import { execFile } from 'node:child_process';
import { readFile } from 'node:fs/promises';
import type { GSDToolsError } from './gsd-tools-error.js';
import type { GSDToolsError } from './gsd-tools.js';
export interface QuerySubprocessAdapterDeps {
projectDir: string;
@@ -103,18 +103,6 @@ export class QuerySubprocessAdapter {
(error, stdout, stderr) => {
const stderrStr = stderr?.toString() ?? '';
if (error) {
if (error.killed || (error as NodeJS.ErrnoException).code === 'ETIMEDOUT') {
reject(
this.deps.createToolsError(
`gsd-tools timed out after ${this.deps.timeoutMs}ms: ${command} ${args.join(' ')}`,
command,
args,
null,
stderrStr,
),
);
return;
}
reject(
this.deps.createToolsError(
`gsd-tools exited with code ${error.code ?? 'unknown'}: ${command} ${args.join(' ')}${stderrStr ? `\n${stderrStr}` : ''}`,