GET/PATCH /api/providers/config, subset POST /refresh, and
GET /api/providers/:id/diagnostic (JSON { diagnostic }, §6.4). PATCH order
is validate→save→reload→clear; a malformed body or invalid merged config
returns 422 without writing, and a save failure returns 500 without
reloading (no file/registry divergence). Web client + types extended.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
86 lines
3.1 KiB
TypeScript
86 lines
3.1 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { getProviderDiagnostic, type DiagnosticAgentRow } from '../provider-diagnostic.js';
|
|
import { buildResolvedRegistry } from '../provider-config-registry.js';
|
|
import { PROVIDERS } from '../provider-registry.js';
|
|
import type { ProviderSnapshotEntry } from '../provider-types.js';
|
|
|
|
const registry = buildResolvedRegistry(PROVIDERS, {
|
|
providers: {
|
|
goose: { enabled: false },
|
|
'amp-acp': { extends: 'acp', label: 'Amp', command: ['amp-acp', '--acp'] },
|
|
},
|
|
});
|
|
|
|
const alwaysAvailable = () => Promise.resolve(true);
|
|
const neverAvailable = () => Promise.resolve(false);
|
|
|
|
describe('getProviderDiagnostic', () => {
|
|
it('reports a disabled built-in (enabled:false, no install)', async () => {
|
|
const report = await getProviderDiagnostic(registry.get('goose')!, undefined, {
|
|
checkAvailable: neverAvailable,
|
|
});
|
|
expect(report).toContain('provider: goose');
|
|
expect(report).toContain('enabled: false');
|
|
expect(report).toContain('installed: false');
|
|
expect(report).toMatch(/command_available:\s*false/);
|
|
});
|
|
|
|
it('reports an installed built-in with its install_path, last_probed_at, model count', async () => {
|
|
const agentRow: DiagnosticAgentRow = {
|
|
name: 'opencode',
|
|
install_path: '/usr/bin/opencode',
|
|
supports_acp: true,
|
|
models: [
|
|
{ id: 'm1', label: 'M1' },
|
|
{ id: 'm2', label: 'M2' },
|
|
],
|
|
last_probed_at: '2026-05-29T12:00:00.000Z',
|
|
};
|
|
const report = await getProviderDiagnostic(registry.get('opencode')!, agentRow, {
|
|
checkAvailable: alwaysAvailable,
|
|
});
|
|
expect(report).toContain('install_path: /usr/bin/opencode');
|
|
expect(report).toContain('2026-05-29T12:00:00.000Z');
|
|
expect(report).toContain('installed: true');
|
|
expect(report).toMatch(/models_in_db:\s*2/);
|
|
expect(report).toMatch(/command_available:\s*true/);
|
|
});
|
|
|
|
it('reports a custom ACP launch command + its binary', async () => {
|
|
const report = await getProviderDiagnostic(registry.get('amp-acp')!, undefined, {
|
|
checkAvailable: alwaysAvailable,
|
|
});
|
|
expect(report).toContain('provider: amp-acp');
|
|
expect(report).toContain('amp-acp --acp');
|
|
expect(report).toContain('customAcp: true');
|
|
});
|
|
|
|
it('surfaces the last probe error from a cached snapshot entry', async () => {
|
|
const cachedEntry: ProviderSnapshotEntry = {
|
|
name: 'opencode',
|
|
label: 'OpenCode',
|
|
transport: 'acp',
|
|
status: 'error',
|
|
enabled: true,
|
|
installed: true,
|
|
models: [],
|
|
modes: [],
|
|
defaultModeId: null,
|
|
commands: [],
|
|
error: 'ACP initialize timed out',
|
|
};
|
|
const report = await getProviderDiagnostic(registry.get('opencode')!, undefined, {
|
|
cachedEntry,
|
|
checkAvailable: alwaysAvailable,
|
|
});
|
|
expect(report).toContain('ACP initialize timed out');
|
|
});
|
|
|
|
it('reports no error when none is cached', async () => {
|
|
const report = await getProviderDiagnostic(registry.get('opencode')!, undefined, {
|
|
checkAvailable: alwaysAvailable,
|
|
});
|
|
expect(report).toMatch(/last_probe_error:\s*\(none/);
|
|
});
|
|
});
|