mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-05 01:50:27 +08:00
- Removed deprecated constants and functions from cli-config-manager.ts. - Introduced new provider model presets in litellm-provider-models.ts for better organization and management of model information. - Created provider-routes.ts to handle API endpoints for retrieving provider information and models. - Added integration tests for provider routes to ensure correct functionality and response structure. - Implemented unit tests for settings persistence functions, covering various scenarios and edge cases. - Enhanced error handling and validation in the new routes and settings functions.
376 lines
14 KiB
TypeScript
376 lines
14 KiB
TypeScript
/**
|
|
* Unit tests for Settings Persistence Functions
|
|
*
|
|
* Tests the new setter/getter functions for ClaudeCliSettingsConfig:
|
|
* - setPromptFormat / getPromptFormat
|
|
* - setDefaultModel / getDefaultModel
|
|
* - setAutoSyncEnabled / getAutoSyncEnabled
|
|
* - setSmartContextEnabled / getSmartContextEnabled
|
|
* - setNativeResume / getNativeResume
|
|
*/
|
|
|
|
import { after, afterEach, before, describe, it } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { existsSync, mkdtempSync, rmSync, writeFileSync, readFileSync } from 'node:fs';
|
|
import { tmpdir } from 'node:os';
|
|
import { join } from 'node:path';
|
|
|
|
// Set up isolated test environment
|
|
const TEST_CCW_HOME = mkdtempSync(join(tmpdir(), 'ccw-settings-test-'));
|
|
process.env.CCW_DATA_DIR = TEST_CCW_HOME;
|
|
|
|
const claudeCliToolsPath = new URL('../dist/tools/claude-cli-tools.js', import.meta.url).href;
|
|
|
|
describe('Settings Persistence Functions', async () => {
|
|
let claudeCliTools: any;
|
|
const testProjectDir = join(TEST_CCW_HOME, 'test-project');
|
|
|
|
before(async () => {
|
|
claudeCliTools = await import(claudeCliToolsPath);
|
|
});
|
|
|
|
after(() => {
|
|
rmSync(TEST_CCW_HOME, { recursive: true, force: true });
|
|
});
|
|
|
|
afterEach(() => {
|
|
// Clean up settings file after each test
|
|
const settingsPath = join(TEST_CCW_HOME, '.claude', 'cli-settings.json');
|
|
if (existsSync(settingsPath)) {
|
|
rmSync(settingsPath);
|
|
}
|
|
});
|
|
|
|
describe('setPromptFormat / getPromptFormat', () => {
|
|
it('should set and get prompt format', () => {
|
|
const result = claudeCliTools.setPromptFormat(testProjectDir, 'yaml');
|
|
assert.equal(result.promptFormat, 'yaml');
|
|
|
|
const retrieved = claudeCliTools.getPromptFormat(testProjectDir);
|
|
assert.equal(retrieved, 'yaml');
|
|
});
|
|
|
|
it('should persist prompt format to file', () => {
|
|
claudeCliTools.setPromptFormat(testProjectDir, 'json');
|
|
|
|
const settingsPath = join(TEST_CCW_HOME, '.claude', 'cli-settings.json');
|
|
assert.ok(existsSync(settingsPath), 'Settings file should exist');
|
|
|
|
const content = JSON.parse(readFileSync(settingsPath, 'utf-8'));
|
|
assert.equal(content.promptFormat, 'json');
|
|
});
|
|
|
|
it('should update existing prompt format', () => {
|
|
claudeCliTools.setPromptFormat(testProjectDir, 'plain');
|
|
claudeCliTools.setPromptFormat(testProjectDir, 'yaml');
|
|
|
|
const retrieved = claudeCliTools.getPromptFormat(testProjectDir);
|
|
assert.equal(retrieved, 'yaml');
|
|
});
|
|
|
|
it('should return default when file does not exist', () => {
|
|
const retrieved = claudeCliTools.getPromptFormat(testProjectDir);
|
|
assert.equal(retrieved, 'plain');
|
|
});
|
|
|
|
it('should accept all valid format values', () => {
|
|
const formats: Array<'plain' | 'yaml' | 'json'> = ['plain', 'yaml', 'json'];
|
|
|
|
for (const format of formats) {
|
|
claudeCliTools.setPromptFormat(testProjectDir, format);
|
|
const retrieved = claudeCliTools.getPromptFormat(testProjectDir);
|
|
assert.equal(retrieved, format, `Format ${format} should be set correctly`);
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('setDefaultModel / getDefaultModel', () => {
|
|
it('should set and get default model', () => {
|
|
const result = claudeCliTools.setDefaultModel(testProjectDir, 'gemini-2.5-pro');
|
|
assert.equal(result.defaultModel, 'gemini-2.5-pro');
|
|
|
|
const retrieved = claudeCliTools.getDefaultModel(testProjectDir);
|
|
assert.equal(retrieved, 'gemini-2.5-pro');
|
|
});
|
|
|
|
it('should persist default model to file', () => {
|
|
claudeCliTools.setDefaultModel(testProjectDir, 'claude-opus-4');
|
|
|
|
const settingsPath = join(TEST_CCW_HOME, '.claude', 'cli-settings.json');
|
|
assert.ok(existsSync(settingsPath), 'Settings file should exist');
|
|
|
|
const content = JSON.parse(readFileSync(settingsPath, 'utf-8'));
|
|
assert.equal(content.defaultModel, 'claude-opus-4');
|
|
});
|
|
|
|
it('should update existing default model', () => {
|
|
claudeCliTools.setDefaultModel(testProjectDir, 'gpt-4.1');
|
|
claudeCliTools.setDefaultModel(testProjectDir, 'gpt-5.2');
|
|
|
|
const retrieved = claudeCliTools.getDefaultModel(testProjectDir);
|
|
assert.equal(retrieved, 'gpt-5.2');
|
|
});
|
|
|
|
it('should return undefined when not set', () => {
|
|
const retrieved = claudeCliTools.getDefaultModel(testProjectDir);
|
|
assert.equal(retrieved, undefined);
|
|
});
|
|
|
|
it('should handle arbitrary model names', () => {
|
|
const models = ['custom-model-1', 'test-model', 'my-fine-tuned-model'];
|
|
|
|
for (const model of models) {
|
|
claudeCliTools.setDefaultModel(testProjectDir, model);
|
|
const retrieved = claudeCliTools.getDefaultModel(testProjectDir);
|
|
assert.equal(retrieved, model, `Model ${model} should be set correctly`);
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('setAutoSyncEnabled / getAutoSyncEnabled', () => {
|
|
it('should set and get auto-sync enabled status', () => {
|
|
const result = claudeCliTools.setAutoSyncEnabled(testProjectDir, true);
|
|
assert.equal(result.autoSyncEnabled, true);
|
|
|
|
const retrieved = claudeCliTools.getAutoSyncEnabled(testProjectDir);
|
|
assert.equal(retrieved, true);
|
|
});
|
|
|
|
it('should persist auto-sync status to file', () => {
|
|
claudeCliTools.setAutoSyncEnabled(testProjectDir, false);
|
|
|
|
const settingsPath = join(TEST_CCW_HOME, '.claude', 'cli-settings.json');
|
|
assert.ok(existsSync(settingsPath), 'Settings file should exist');
|
|
|
|
const content = JSON.parse(readFileSync(settingsPath, 'utf-8'));
|
|
assert.equal(content.autoSyncEnabled, false);
|
|
});
|
|
|
|
it('should update existing auto-sync status', () => {
|
|
claudeCliTools.setAutoSyncEnabled(testProjectDir, true);
|
|
claudeCliTools.setAutoSyncEnabled(testProjectDir, false);
|
|
|
|
const retrieved = claudeCliTools.getAutoSyncEnabled(testProjectDir);
|
|
assert.equal(retrieved, false);
|
|
});
|
|
|
|
it('should return undefined when not set', () => {
|
|
const retrieved = claudeCliTools.getAutoSyncEnabled(testProjectDir);
|
|
assert.equal(retrieved, undefined);
|
|
});
|
|
|
|
it('should toggle between true and false', () => {
|
|
claudeCliTools.setAutoSyncEnabled(testProjectDir, true);
|
|
let retrieved = claudeCliTools.getAutoSyncEnabled(testProjectDir);
|
|
assert.equal(retrieved, true);
|
|
|
|
claudeCliTools.setAutoSyncEnabled(testProjectDir, false);
|
|
retrieved = claudeCliTools.getAutoSyncEnabled(testProjectDir);
|
|
assert.equal(retrieved, false);
|
|
|
|
claudeCliTools.setAutoSyncEnabled(testProjectDir, true);
|
|
retrieved = claudeCliTools.getAutoSyncEnabled(testProjectDir);
|
|
assert.equal(retrieved, true);
|
|
});
|
|
});
|
|
|
|
describe('setSmartContextEnabled / getSmartContextEnabled', () => {
|
|
it('should set and get smart context enabled status', () => {
|
|
const result = claudeCliTools.setSmartContextEnabled(testProjectDir, true);
|
|
assert.equal(result.smartContext.enabled, true);
|
|
|
|
const retrieved = claudeCliTools.getSmartContextEnabled(testProjectDir);
|
|
assert.equal(retrieved, true);
|
|
});
|
|
|
|
it('should persist smart context status to file', () => {
|
|
claudeCliTools.setSmartContextEnabled(testProjectDir, true);
|
|
|
|
const settingsPath = join(TEST_CCW_HOME, '.claude', 'cli-settings.json');
|
|
assert.ok(existsSync(settingsPath), 'Settings file should exist');
|
|
|
|
const content = JSON.parse(readFileSync(settingsPath, 'utf-8'));
|
|
assert.equal(content.smartContext.enabled, true);
|
|
});
|
|
|
|
it('should preserve other smartContext properties', () => {
|
|
// First, load settings to check default maxFiles
|
|
const settings = claudeCliTools.loadClaudeCliSettings(testProjectDir);
|
|
const defaultMaxFiles = settings.smartContext.maxFiles;
|
|
|
|
claudeCliTools.setSmartContextEnabled(testProjectDir, true);
|
|
|
|
const settingsPath = join(TEST_CCW_HOME, '.claude', 'cli-settings.json');
|
|
const content = JSON.parse(readFileSync(settingsPath, 'utf-8'));
|
|
|
|
assert.equal(content.smartContext.enabled, true);
|
|
assert.equal(content.smartContext.maxFiles, defaultMaxFiles, 'maxFiles should be preserved');
|
|
});
|
|
|
|
it('should return false when not set', () => {
|
|
const retrieved = claudeCliTools.getSmartContextEnabled(testProjectDir);
|
|
assert.equal(retrieved, false);
|
|
});
|
|
});
|
|
|
|
describe('setNativeResume / getNativeResume', () => {
|
|
it('should set and get native resume status', () => {
|
|
const result = claudeCliTools.setNativeResume(testProjectDir, false);
|
|
assert.equal(result.nativeResume, false);
|
|
|
|
const retrieved = claudeCliTools.getNativeResume(testProjectDir);
|
|
assert.equal(retrieved, false);
|
|
});
|
|
|
|
it('should persist native resume status to file', () => {
|
|
claudeCliTools.setNativeResume(testProjectDir, false);
|
|
|
|
const settingsPath = join(TEST_CCW_HOME, '.claude', 'cli-settings.json');
|
|
assert.ok(existsSync(settingsPath), 'Settings file should exist');
|
|
|
|
const content = JSON.parse(readFileSync(settingsPath, 'utf-8'));
|
|
assert.equal(content.nativeResume, false);
|
|
});
|
|
|
|
it('should return true when not set (default)', () => {
|
|
const retrieved = claudeCliTools.getNativeResume(testProjectDir);
|
|
assert.equal(retrieved, true);
|
|
});
|
|
});
|
|
|
|
describe('Multiple settings updates', () => {
|
|
it('should handle multiple settings updates in sequence', () => {
|
|
claudeCliTools.setPromptFormat(testProjectDir, 'yaml');
|
|
claudeCliTools.setDefaultModel(testProjectDir, 'gemini-2.5-pro');
|
|
claudeCliTools.setAutoSyncEnabled(testProjectDir, true);
|
|
claudeCliTools.setSmartContextEnabled(testProjectDir, true);
|
|
claudeCliTools.setNativeResume(testProjectDir, false);
|
|
|
|
assert.equal(claudeCliTools.getPromptFormat(testProjectDir), 'yaml');
|
|
assert.equal(claudeCliTools.getDefaultModel(testProjectDir), 'gemini-2.5-pro');
|
|
assert.equal(claudeCliTools.getAutoSyncEnabled(testProjectDir), true);
|
|
assert.equal(claudeCliTools.getSmartContextEnabled(testProjectDir), true);
|
|
assert.equal(claudeCliTools.getNativeResume(testProjectDir), false);
|
|
});
|
|
|
|
it('should preserve existing settings when updating one', () => {
|
|
claudeCliTools.setPromptFormat(testProjectDir, 'json');
|
|
claudeCliTools.setDefaultModel(testProjectDir, 'test-model');
|
|
|
|
// Update only auto-sync
|
|
claudeCliTools.setAutoSyncEnabled(testProjectDir, true);
|
|
|
|
// Verify previous settings are preserved
|
|
assert.equal(claudeCliTools.getPromptFormat(testProjectDir), 'json');
|
|
assert.equal(claudeCliTools.getDefaultModel(testProjectDir), 'test-model');
|
|
assert.equal(claudeCliTools.getAutoSyncEnabled(testProjectDir), true);
|
|
});
|
|
});
|
|
|
|
describe('Performance', () => {
|
|
it('should complete setter operations in under 10ms', () => {
|
|
const operations = [
|
|
() => claudeCliTools.setPromptFormat(testProjectDir, 'yaml'),
|
|
() => claudeCliTools.setDefaultModel(testProjectDir, 'test-model'),
|
|
() => claudeCliTools.setAutoSyncEnabled(testProjectDir, true),
|
|
() => claudeCliTools.setSmartContextEnabled(testProjectDir, true),
|
|
() => claudeCliTools.setNativeResume(testProjectDir, false),
|
|
];
|
|
|
|
for (const operation of operations) {
|
|
const start = Date.now();
|
|
operation();
|
|
const duration = Date.now() - start;
|
|
assert.ok(duration < 10, `Operation should complete in under 10ms (took ${duration}ms)`);
|
|
}
|
|
});
|
|
|
|
it('should complete getter operations in under 10ms', () => {
|
|
// Set up some data first
|
|
claudeCliTools.setPromptFormat(testProjectDir, 'yaml');
|
|
claudeCliTools.setDefaultModel(testProjectDir, 'test-model');
|
|
claudeCliTools.setAutoSyncEnabled(testProjectDir, true);
|
|
|
|
const operations = [
|
|
() => claudeCliTools.getPromptFormat(testProjectDir),
|
|
() => claudeCliTools.getDefaultModel(testProjectDir),
|
|
() => claudeCliTools.getAutoSyncEnabled(testProjectDir),
|
|
() => claudeCliTools.getSmartContextEnabled(testProjectDir),
|
|
() => claudeCliTools.getNativeResume(testProjectDir),
|
|
];
|
|
|
|
for (const operation of operations) {
|
|
const start = Date.now();
|
|
operation();
|
|
const duration = Date.now() - start;
|
|
assert.ok(duration < 10, `Operation should complete in under 10ms (took ${duration}ms)`);
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('File corruption handling', () => {
|
|
it('should handle invalid JSON gracefully', () => {
|
|
const settingsPath = join(TEST_CCW_HOME, '.claude', 'cli-settings.json');
|
|
const claudeDir = join(TEST_CCW_HOME, '.claude');
|
|
|
|
// Create .claude directory if it doesn't exist
|
|
if (!existsSync(claudeDir)) {
|
|
mkdtempSync(claudeDir);
|
|
}
|
|
|
|
// Write invalid JSON
|
|
writeFileSync(settingsPath, '{invalid json}', 'utf-8');
|
|
|
|
// Getters should return defaults
|
|
assert.equal(claudeCliTools.getPromptFormat(testProjectDir), 'plain');
|
|
assert.equal(claudeCliTools.getDefaultModel(testProjectDir), undefined);
|
|
assert.equal(claudeCliTools.getAutoSyncEnabled(testProjectDir), undefined);
|
|
});
|
|
|
|
it('should recover from corrupted file by overwriting', () => {
|
|
const settingsPath = join(TEST_CCW_HOME, '.claude', 'cli-settings.json');
|
|
const claudeDir = join(TEST_CCW_HOME, '.claude');
|
|
|
|
// Create .claude directory if it doesn't exist
|
|
if (!existsSync(claudeDir)) {
|
|
mkdtempSync(claudeDir);
|
|
}
|
|
|
|
// Write invalid JSON
|
|
writeFileSync(settingsPath, '{invalid json}', 'utf-8');
|
|
|
|
// Setter should fix the file
|
|
claudeCliTools.setPromptFormat(testProjectDir, 'yaml');
|
|
|
|
// Should now read correctly
|
|
assert.equal(claudeCliTools.getPromptFormat(testProjectDir), 'yaml');
|
|
|
|
// File should be valid JSON
|
|
const content = JSON.parse(readFileSync(settingsPath, 'utf-8'));
|
|
assert.equal(content.promptFormat, 'yaml');
|
|
});
|
|
});
|
|
|
|
describe('Edge cases', () => {
|
|
it('should handle empty string for defaultModel', () => {
|
|
claudeCliTools.setDefaultModel(testProjectDir, '');
|
|
const retrieved = claudeCliTools.getDefaultModel(testProjectDir);
|
|
assert.equal(retrieved, '');
|
|
});
|
|
|
|
it('should handle very long model names', () => {
|
|
const longModelName = 'a'.repeat(1000);
|
|
claudeCliTools.setDefaultModel(testProjectDir, longModelName);
|
|
const retrieved = claudeCliTools.getDefaultModel(testProjectDir);
|
|
assert.equal(retrieved, longModelName);
|
|
});
|
|
|
|
it('should handle special characters in model names', () => {
|
|
const specialName = 'model-@#$%^&*()_+{}[]|:;<>?,./~`';
|
|
claudeCliTools.setDefaultModel(testProjectDir, specialName);
|
|
const retrieved = claudeCliTools.getDefaultModel(testProjectDir);
|
|
assert.equal(retrieved, specialName);
|
|
});
|
|
});
|
|
});
|