fix(image-analysis): reject unknown backend mappings

This commit is contained in:
Tam Nhu Tran
2026-04-01 15:32:37 -04:00
parent d40cc60a1d
commit 665668579d
2 changed files with 67 additions and 3 deletions
@@ -40,6 +40,13 @@ const IMAGE_ANALYSIS_PROVIDER_ALIASES = Object.freeze(
)
);
function isConfiguredImageAnalysisBackend(
backend: string | null,
providerModels: Record<string, string>
): backend is string {
return Boolean(backend && Object.prototype.hasOwnProperty.call(providerModels, backend));
}
function parseArgs(args: string[]): ImageAnalysisCommandOptions {
const options: ImageAnalysisCommandOptions = {
enable: hasAnyFlag(args, ['--enable']),
@@ -296,7 +303,7 @@ export async function handleConfigImageAnalysisCommand(args: string[]): Promise<
options.setFallback,
Object.keys(imageConfig.provider_models)
);
if (!normalizedBackend) {
if (!isConfiguredImageAnalysisBackend(normalizedBackend, imageConfig.provider_models)) {
console.error(fail(`Invalid fallback backend: ${options.setFallback}`));
process.exit(1);
}
@@ -314,7 +321,7 @@ export async function handleConfigImageAnalysisCommand(args: string[]): Promise<
console.error(fail('Profile name cannot be empty'));
process.exit(1);
}
if (!normalizedBackend) {
if (!isConfiguredImageAnalysisBackend(normalizedBackend, imageConfig.provider_models)) {
console.error(fail(`Invalid backend: ${options.setProfileBackend.backend}`));
process.exit(1);
}
@@ -4,7 +4,7 @@
* Unit tests for ccs config image-analysis subcommand.
*/
import { describe, it, expect, beforeEach, afterEach, spyOn, mock } from 'bun:test';
import { describe, it, expect, beforeEach, afterEach } from 'bun:test';
import * as fs from 'fs';
import * as path from 'path';
import * as os from 'os';
@@ -33,6 +33,13 @@ function createConfigYaml(content: string): void {
fs.writeFileSync(path.join(testDir, 'config.yaml'), content, 'utf8');
}
async function loadHandleConfigImageAnalysisCommand() {
const mod = await import(
`../../../src/commands/config-image-analysis-command?test=${Date.now()}-${Math.random()}`
);
return mod.handleConfigImageAnalysisCommand;
}
describe('config image-analysis command', () => {
describe('config file parsing', () => {
it('should parse enabled status from config.yaml', () => {
@@ -152,6 +159,56 @@ image_analysis:
expect(validProviders.includes(provider)).toBe(false);
}
});
it('rejects invalid fallback backends that are not configured', async () => {
const handleConfigImageAnalysisCommand = await loadHandleConfigImageAnalysisCommand();
const originalProcessExit = process.exit;
process.exit = ((code?: number) => {
throw new Error(`process.exit(${code ?? 0})`);
}) as typeof process.exit;
try {
await expect(
handleConfigImageAnalysisCommand(['--set-fallback', 'unknown-provider'])
).rejects.toThrow('process.exit(1)');
} finally {
process.exit = originalProcessExit;
}
const configPath = path.join(testDir, 'config.yaml');
if (fs.existsSync(configPath)) {
const content = fs.readFileSync(configPath, 'utf8');
expect(content).not.toContain('fallback_backend: unknown-provider');
} else {
expect(fs.existsSync(configPath)).toBe(false);
}
});
it('rejects invalid profile backend mappings that are not configured', async () => {
const handleConfigImageAnalysisCommand = await loadHandleConfigImageAnalysisCommand();
const originalProcessExit = process.exit;
process.exit = ((code?: number) => {
throw new Error(`process.exit(${code ?? 0})`);
}) as typeof process.exit;
try {
await expect(
handleConfigImageAnalysisCommand(['--set-profile-backend', 'orq', 'unknown-provider'])
).rejects.toThrow('process.exit(1)');
} finally {
process.exit = originalProcessExit;
}
const configPath = path.join(testDir, 'config.yaml');
if (fs.existsSync(configPath)) {
const content = fs.readFileSync(configPath, 'utf8');
expect(content).not.toContain('unknown-provider');
} else {
expect(fs.existsSync(configPath)).toBe(false);
}
});
});
describe('default configuration', () => {