test(cliproxy): add catalog --json tests and clarify flag priority

- Add unit tests verifying JSON output structure, field filtering,
  and minified format
- Document that --json takes priority over subcommands (refresh/reset)

Built [OnSteroids](https://onsteroids.ai)
This commit is contained in:
Sergey Galuza
2026-04-11 18:27:39 +02:00
parent f090c32c2f
commit c1d462ea41
2 changed files with 66 additions and 0 deletions
+2
View File
@@ -147,6 +147,8 @@ export async function handleCliproxyCommand(args: string[]): Promise<void> {
// Catalog commands
if (command === 'catalog') {
// --json takes priority over subcommands (refresh/reset) — it always
// outputs the current resolved catalog regardless of other arguments.
if (hasAnyFlag(remainingArgs, ['--json'])) {
handleCatalogJson();
return;
@@ -0,0 +1,64 @@
import { afterEach, beforeEach, describe, expect, it } from 'bun:test';
let originalConsoleLog: typeof console.log;
let capturedOutput: string[];
beforeEach(() => {
originalConsoleLog = console.log;
capturedOutput = [];
console.log = (...args: unknown[]) => {
capturedOutput.push(args.map(String).join(' '));
};
});
afterEach(() => {
console.log = originalConsoleLog;
});
describe('cliproxy catalog --json output', () => {
it('outputs valid JSON mapping provider names to model arrays', async () => {
const { handleCatalogJson } = await import(
`../../../src/commands/cliproxy/catalog-subcommand?catalog-json-format=${Date.now()}`
);
handleCatalogJson();
expect(capturedOutput).toHaveLength(1);
const parsed = JSON.parse(capturedOutput[0]) as Record<
string,
Array<{ id: string; name: string }>
>;
// Must be a non-empty object (static catalog always has providers)
expect(typeof parsed).toBe('object');
expect(Object.keys(parsed).length).toBeGreaterThan(0);
// Every provider entry must be an array of { id, name } objects
for (const [provider, models] of Object.entries(parsed)) {
expect(Array.isArray(models)).toBe(true);
expect(models.length).toBeGreaterThan(0);
for (const model of models) {
expect(typeof model.id).toBe('string');
expect(typeof model.name).toBe('string');
// Only id and name — no extra metadata leaks
expect(Object.keys(model).sort()).toEqual(['id', 'name']);
}
// Provider key should be a non-empty string
expect(provider.length).toBeGreaterThan(0);
}
});
it('outputs minified JSON (single line, no whitespace formatting)', async () => {
const { handleCatalogJson } = await import(
`../../../src/commands/cliproxy/catalog-subcommand?catalog-json-minified=${Date.now()}`
);
handleCatalogJson();
const output = capturedOutput[0];
// Minified JSON has no newlines
expect(output.includes('\n')).toBe(false);
// Valid JSON roundtrip
expect(JSON.stringify(JSON.parse(output))).toBe(output);
});
});