Files
ccs/tests/unit/cliproxy/model-config.test.js
T
kaitranntt 20b7bae046 refactor(cliproxy): use centralized ui system for model config display
Replace custom formatting with ui.ts imports (color, bold, dim, ok, info,
header, initUI) to maintain consistent styling with ccs doctor. Add model
descriptions to catalog and clarify [Paid API] tier labeling. Update
showCurrentConfig to async for UI initialization. Improves code reuse and
visual consistency across CLI commands.
2025-12-04 05:29:55 -05:00

163 lines
5.8 KiB
JavaScript

/**
* Tests for CLIProxy Model Configuration
* Verifies model configuration logic and settings management
*/
const assert = require('assert');
const fs = require('fs');
const path = require('path');
const os = require('os');
describe('Model Config', () => {
const modelConfig = require('../../../dist/cliproxy/model-config');
const modelCatalog = require('../../../dist/cliproxy/model-catalog');
describe('hasUserSettings', () => {
it('returns false when settings file does not exist', () => {
// Ensure we're checking a non-existent path
const { hasUserSettings } = modelConfig;
// Since we can't easily mock getCcsDir, we test the function logic
// by checking it doesn't throw
const result = hasUserSettings('agy');
assert(typeof result === 'boolean', 'Should return boolean');
});
});
describe('getCurrentModel', () => {
it('returns undefined when settings file does not exist', () => {
const { getCurrentModel } = modelConfig;
// Test with a provider that likely has no settings in test env
const result = getCurrentModel('agy');
// Result depends on whether ~/.ccs/agy.settings.json exists
// Just verify it returns string or undefined
assert(
result === undefined || typeof result === 'string',
'Should return string or undefined'
);
});
});
describe('configureProviderModel', () => {
it('returns false for unsupported provider (codex)', async () => {
const { configureProviderModel } = modelConfig;
const result = await configureProviderModel('codex', true);
assert.strictEqual(result, false);
});
it('returns false for unsupported provider (qwen)', async () => {
const { configureProviderModel } = modelConfig;
const result = await configureProviderModel('qwen', true);
assert.strictEqual(result, false);
});
// Note: Full interactive tests require mocking stdin
// These are smoke tests to verify basic logic
});
describe('showCurrentConfig', () => {
it('does not throw for agy provider', async () => {
const { showCurrentConfig } = modelConfig;
// Just verify it doesn't throw (now async)
await assert.doesNotReject(async () => showCurrentConfig('agy'));
});
it('does not throw for gemini provider', async () => {
const { showCurrentConfig } = modelConfig;
await assert.doesNotReject(async () => showCurrentConfig('gemini'));
});
it('does not throw for unsupported provider', async () => {
const { showCurrentConfig } = modelConfig;
await assert.doesNotReject(async () => showCurrentConfig('codex'));
});
});
describe('Model catalog integration', () => {
it('configureProviderModel uses correct catalog for agy', async () => {
const { getProviderCatalog } = modelCatalog;
const catalog = getProviderCatalog('agy');
// Verify catalog structure is what configureProviderModel expects
assert(catalog.models, 'Should have models array');
assert(catalog.defaultModel, 'Should have defaultModel');
assert(catalog.displayName, 'Should have displayName');
});
it('configureProviderModel uses correct catalog for gemini', async () => {
const { getProviderCatalog } = modelCatalog;
const catalog = getProviderCatalog('gemini');
assert(catalog.models, 'Should have models array');
assert(catalog.defaultModel, 'Should have defaultModel');
assert(catalog.displayName, 'Should have displayName');
});
});
describe('Settings file format', () => {
it('should generate correct settings structure', () => {
// Test the expected settings structure
const expectedStructure = {
env: {
ANTHROPIC_BASE_URL: expect.any(String),
ANTHROPIC_AUTH_TOKEN: expect.any(String),
ANTHROPIC_MODEL: 'gemini-claude-opus-4-5-thinking',
ANTHROPIC_DEFAULT_OPUS_MODEL: 'gemini-claude-opus-4-5-thinking',
ANTHROPIC_DEFAULT_SONNET_MODEL: 'gemini-claude-opus-4-5-thinking',
ANTHROPIC_DEFAULT_HAIKU_MODEL: expect.any(String),
},
};
// Verify the structure is valid JSON
const testSettings = {
env: {
ANTHROPIC_BASE_URL: 'http://127.0.0.1:8317/api/provider/agy',
ANTHROPIC_AUTH_TOKEN: 'ccs-internal-managed',
ANTHROPIC_MODEL: 'gemini-claude-opus-4-5-thinking',
ANTHROPIC_DEFAULT_OPUS_MODEL: 'gemini-claude-opus-4-5-thinking',
ANTHROPIC_DEFAULT_SONNET_MODEL: 'gemini-claude-opus-4-5-thinking',
ANTHROPIC_DEFAULT_HAIKU_MODEL: 'gemini-2.5-flash',
},
};
const json = JSON.stringify(testSettings, null, 2);
const parsed = JSON.parse(json);
assert(parsed.env, 'Should have env object');
assert(parsed.env.ANTHROPIC_MODEL, 'Should have ANTHROPIC_MODEL');
assert.strictEqual(
parsed.env.ANTHROPIC_MODEL,
'gemini-claude-opus-4-5-thinking'
);
});
it('all env values should be strings (PowerShell safety)', () => {
const testSettings = {
env: {
ANTHROPIC_BASE_URL: 'http://127.0.0.1:8317/api/provider/agy',
ANTHROPIC_AUTH_TOKEN: 'ccs-internal-managed',
ANTHROPIC_MODEL: 'gemini-claude-opus-4-5-thinking',
ANTHROPIC_DEFAULT_OPUS_MODEL: 'gemini-claude-opus-4-5-thinking',
ANTHROPIC_DEFAULT_SONNET_MODEL: 'gemini-claude-opus-4-5-thinking',
ANTHROPIC_DEFAULT_HAIKU_MODEL: 'gemini-2.5-flash',
},
};
for (const [key, value] of Object.entries(testSettings.env)) {
assert.strictEqual(
typeof value,
'string',
`env.${key} should be string, got ${typeof value}`
);
}
});
});
});
// Helper for expect-like assertions
const expect = {
any: (type) => ({
_type: type,
toString: () => `expect.any(${type.name})`,
}),
};