mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-19 04:15:45 +00:00
test(auth): add comprehensive tests for GLM auth persistence fix
Add 26 new unit tests covering: - isFirstTimeInstall() legacy config detection (8 tests) - loadSettingsFromFile() path expansion (5 tests) - ProfileDetector detectProfileType() (4 tests) - expandPath() cross-platform handling (10 tests) Test coverage for issue #195: - Legacy config.json/profiles.json detection - Tilde expansion to home directory - Environment variable expansion (${VAR}, $VAR, %VAR%) - Mixed path separator normalization - Corrupted config graceful handling Files: - tests/unit/commands/setup-command.test.ts - tests/unit/auth/profile-detector.test.ts - tests/unit/utils/expand-path.test.ts
This commit is contained in:
1 parent
adb6222bc6
commit
92a79aa78b
7 files changed
+369
-5
No files matched your search
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
* Unit tests for Profile Detector
|
||||
*/
|
||||
import { describe, it, expect, beforeEach, afterEach, spyOn } from 'bun:test';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
import ProfileDetector, { loadSettingsFromFile } from '../../../src/auth/profile-detector';
|
||||
import * as unifiedConfigLoader from '../../../src/config/unified-config-loader';
|
||||
|
||||
describe('ProfileDetector', () => {
|
||||
const tempDir = path.join(os.tmpdir(), `ccs-test-profile-detector-${process.pid}`);
|
||||
|
||||
beforeEach(() => {
|
||||
if (!fs.existsSync(tempDir)) {
|
||||
fs.mkdirSync(tempDir, { recursive: true });
|
||||
}
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
if (fs.existsSync(tempDir)) {
|
||||
fs.rmSync(tempDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
describe('loadSettingsFromFile', () => {
|
||||
it('should load settings from a valid JSON file', () => {
|
||||
const settingsPath = path.join(tempDir, 'valid.settings.json');
|
||||
const settings = { env: { KEY: 'VALUE' } };
|
||||
fs.writeFileSync(settingsPath, JSON.stringify(settings));
|
||||
|
||||
const result = loadSettingsFromFile(settingsPath);
|
||||
expect(result).toEqual({ KEY: 'VALUE' });
|
||||
});
|
||||
|
||||
it('should return empty object for non-existent file', () => {
|
||||
const result = loadSettingsFromFile(path.join(tempDir, 'non-existent.json'));
|
||||
expect(result).toEqual({});
|
||||
});
|
||||
|
||||
it('should return empty object for invalid JSON', () => {
|
||||
const settingsPath = path.join(tempDir, 'invalid.json');
|
||||
fs.writeFileSync(settingsPath, 'invalid json');
|
||||
|
||||
const result = loadSettingsFromFile(settingsPath);
|
||||
expect(result).toEqual({});
|
||||
});
|
||||
|
||||
it('should handle tilde expansion correctly', () => {
|
||||
const mockHome = path.join(tempDir, 'home');
|
||||
fs.mkdirSync(mockHome, { recursive: true });
|
||||
const settingsPath = '~/test.settings.json';
|
||||
const actualPath = path.join(mockHome, 'test.settings.json');
|
||||
fs.writeFileSync(actualPath, JSON.stringify({ env: { HOME_VAR: 'TRUE' } }));
|
||||
|
||||
// Mock os.homedir
|
||||
const homedirSpy = spyOn(os, 'homedir').mockReturnValue(mockHome);
|
||||
|
||||
try {
|
||||
const result = loadSettingsFromFile(settingsPath);
|
||||
expect(result).toEqual({ HOME_VAR: 'TRUE' });
|
||||
} finally {
|
||||
homedirSpy.mockRestore();
|
||||
}
|
||||
});
|
||||
|
||||
it('should handle env var expansion correctly', () => {
|
||||
const settingsPath = path.join(tempDir, '${TEST_VAR}.json');
|
||||
const actualPath = path.join(tempDir, 'actual.json');
|
||||
fs.writeFileSync(actualPath, JSON.stringify({ env: { ENV_VAR: 'EXPANDED' } }));
|
||||
|
||||
process.env.TEST_VAR = 'actual';
|
||||
try {
|
||||
const result = loadSettingsFromFile(settingsPath);
|
||||
expect(result).toEqual({ ENV_VAR: 'EXPANDED' });
|
||||
} finally {
|
||||
delete process.env.TEST_VAR;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('detectProfileType', () => {
|
||||
let detector: ProfileDetector;
|
||||
|
||||
beforeEach(() => {
|
||||
detector = new ProfileDetector();
|
||||
});
|
||||
|
||||
it('should detect CLIProxy profiles', () => {
|
||||
const result = detector.detectProfileType('gemini');
|
||||
expect(result.type).toBe('cliproxy');
|
||||
expect(result.name).toBe('gemini');
|
||||
expect(result.provider).toBe('gemini');
|
||||
});
|
||||
|
||||
it('should detect settings-based profile from unified config', () => {
|
||||
const settingsPath = path.join(tempDir, 'glm.settings.json');
|
||||
fs.writeFileSync(settingsPath, JSON.stringify({ env: { ANTHROPIC_MODEL: 'glm-4' } }));
|
||||
|
||||
const mockUnifiedConfig = {
|
||||
version: 2,
|
||||
profiles: {
|
||||
glm: { settings: settingsPath, type: 'api' }
|
||||
}
|
||||
};
|
||||
|
||||
const isUnifiedModeSpy = spyOn(unifiedConfigLoader, 'isUnifiedMode').mockReturnValue(true);
|
||||
const loadUnifiedConfigSpy = spyOn(unifiedConfigLoader, 'loadUnifiedConfig').mockReturnValue(mockUnifiedConfig as any);
|
||||
|
||||
try {
|
||||
const result = detector.detectProfileType('glm');
|
||||
expect(result.type).toBe('settings');
|
||||
expect(result.name).toBe('glm');
|
||||
expect(result.env).toEqual({ ANTHROPIC_MODEL: 'glm-4' });
|
||||
} finally {
|
||||
isUnifiedModeSpy.mockRestore();
|
||||
loadUnifiedConfigSpy.mockRestore();
|
||||
}
|
||||
});
|
||||
|
||||
it('should detect account-based profile from unified config', () => {
|
||||
const mockUnifiedConfig = {
|
||||
version: 2,
|
||||
accounts: {
|
||||
work: { created: '2025-01-01', last_used: '2025-01-02' }
|
||||
}
|
||||
};
|
||||
|
||||
const isUnifiedModeSpy = spyOn(unifiedConfigLoader, 'isUnifiedMode').mockReturnValue(true);
|
||||
const loadUnifiedConfigSpy = spyOn(unifiedConfigLoader, 'loadUnifiedConfig').mockReturnValue(mockUnifiedConfig as any);
|
||||
|
||||
try {
|
||||
const result = detector.detectProfileType('work');
|
||||
expect(result.type).toBe('account');
|
||||
expect(result.name).toBe('work');
|
||||
expect(result.profile).toBeDefined();
|
||||
expect((result.profile as any).type).toBe('account');
|
||||
} finally {
|
||||
isUnifiedModeSpy.mockRestore();
|
||||
loadUnifiedConfigSpy.mockRestore();
|
||||
}
|
||||
});
|
||||
|
||||
it('should return null for unknown profile (throws error)', () => {
|
||||
const isUnifiedModeSpy = spyOn(unifiedConfigLoader, 'isUnifiedMode').mockReturnValue(false);
|
||||
// Mock readConfig/readProfiles to return empty
|
||||
spyOn(fs, 'existsSync').mockReturnValue(false);
|
||||
|
||||
try {
|
||||
expect(() => detector.detectProfileType('unknown')).toThrow(/Profile not found/);
|
||||
} finally {
|
||||
isUnifiedModeSpy.mockRestore();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user