ktx/packages/cli/test/context/connections/resolve-connection.test.ts
Andrey Avtomonov 8a50601582
fix(cli): make connection-not-configured errors actionable and expected (#301)
The MCP sql_execution/sl_query tools and the `ktx sql` CLI threw a plain Error naming no valid connection ids when an agent passed an unconfigured connectionId (or omitted it with multiple connections). The message reached the agent verbatim but gave it nothing to correct with, so it re-guessed for days, and each correct caller-driven rejection filed in PostHog Error Tracking as a ktx fault (issue 019eb10c, 8 occurrences on one install).

Add a shared resolver (resolveConfiguredConnection / resolveRequiredConnectionId) that throws KtxExpectedError listing the configured connections, and route the three SQL-execution call sites through it. Expected-error classification keeps these out of Error Tracking while the actionable message lets agents self-correct.
2026-06-15 14:38:44 +02:00

80 lines
2.9 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import {
buildDefaultKtxProjectConfig,
type KtxProjectConfig,
} from '../../../src/context/project/config.js';
import {
resolveConfiguredConnection,
resolveRequiredConnectionId,
} from '../../../src/context/connections/resolve-connection.js';
import { KtxExpectedError } from '../../../src/errors.js';
function configWith(ids: string[]): KtxProjectConfig {
const config = buildDefaultKtxProjectConfig();
for (const id of ids) {
config.connections[id] = { driver: 'postgres' };
}
return config;
}
describe('resolveConfiguredConnection', () => {
it('returns the connection config when the id is configured', () => {
const config = configWith(['warehouse']);
expect(resolveConfiguredConnection(config, 'warehouse')).toEqual({ driver: 'postgres' });
});
it('throws an expected error that lists the configured connections', () => {
const config = configWith(['analytics', 'warehouse']);
let error: unknown;
try {
resolveConfiguredConnection(config, 'ARK');
} catch (caught) {
error = caught;
}
expect(error).toBeInstanceOf(KtxExpectedError);
expect((error as Error).message).toBe(
'Connection "ARK" is not configured in ktx.yaml. Configured connections: analytics, warehouse.',
);
});
it('reports when no connections are configured at all', () => {
const config = configWith([]);
expect(() => resolveConfiguredConnection(config, 'warehouse')).toThrow(
'Connection "warehouse" is not configured in ktx.yaml. No connections are configured in ktx.yaml.',
);
});
});
describe('resolveRequiredConnectionId', () => {
it('returns the requested id when it is configured', () => {
const config = configWith(['warehouse']);
expect(resolveRequiredConnectionId(config, 'warehouse')).toBe('warehouse');
});
it('throws an expected error listing connections when the requested id is unknown', () => {
const config = configWith(['analytics', 'warehouse']);
expect(() => resolveRequiredConnectionId(config, 'DIG_SMART_REP')).toThrow(KtxExpectedError);
expect(() => resolveRequiredConnectionId(config, 'DIG_SMART_REP')).toThrow(
'Connection "DIG_SMART_REP" is not configured in ktx.yaml. Configured connections: analytics, warehouse.',
);
});
it('defaults to the only connection when the id is omitted', () => {
const config = configWith(['warehouse']);
expect(resolveRequiredConnectionId(config, undefined)).toBe('warehouse');
});
it('throws an expected error listing connections when the id is omitted and several exist', () => {
const config = configWith(['analytics', 'warehouse']);
let error: unknown;
try {
resolveRequiredConnectionId(config, undefined);
} catch (caught) {
error = caught;
}
expect(error).toBeInstanceOf(KtxExpectedError);
expect((error as Error).message).toBe(
'connectionId is required. Configured connections: analytics, warehouse.',
);
});
});