Local branch had diverged from origin/master (sibling commits on the same base). Rewrote local history linearly on top of origin/master, folding in all local content: exa/hot-platform discovery hunters, leaks ledger, nightly sweep orchestrator, updated .gitignore and skill docs, plus the latest hunt output and vault state. Remote-only files (vault keys, channel scripts) were restored rather than dropped, so the resulting tree is a full union of both sides.
1 line
9.0 KiB
JSON
1 line
9.0 KiB
JSON
{"repo": "chen-dixi/nano-bruce", "files": {"bruce-ts/packages/app/src/config/settings.test.ts": {"9823389ca24815c4bcab7dbee3f3cc21278fec0b": {"t": 1786068274.6386135, "n": 8739, "h": "e568065a106134d2", "c": "/**\n * Configuration System Tests\n */\n\nimport { describe, test, expect, beforeEach, afterEach, vi } from 'vitest';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\n\n// Import actual config module\nimport {\n getBruceDir,\n getSettingsPath,\n loadSettingsFromFile,\n mergeSettings,\n getEffectiveConfig,\n initSettings,\n} from './settings.js';\n\nconst REAL_BRUCE_DIR = path.join(os.homedir(), '.bruce');\nconst REAL_SETTINGS_PATH = path.join(REAL_BRUCE_DIR, 'settings.json');\nlet originalSettings: string | null = null;\n\ndescribe('Configuration System', () => {\n describe('Path functions', () => {\n test('getBruceDir returns ~/.bruce', () => {\n const bruceDir = getBruceDir();\n expect(bruceDir).toBe(path.join(os.homedir(), '.bruce'));\n });\n\n test('getSettingsPath returns ~/.bruce/settings.json', () => {\n const settingsPath = getSettingsPath();\n expect(settingsPath).toBe(path.join(os.homedir(), '.bruce', 'settings.json'));\n });\n });\n\n describe('loadSettingsFromFile', () => {\n beforeEach(() => {\n // Backup existing settings\n if (fs.existsSync(REAL_SETTINGS_PATH)) {\n originalSettings = fs.readFileSync(REAL_SETTINGS_PATH, 'utf-8');\n }\n });\n\n afterEach(() => {\n // Restore original settings\n if (originalSettings !== null) {\n fs.writeFileSync(REAL_SETTINGS_PATH, originalSettings);\n originalSettings = null;\n } else if (fs.existsSync(REAL_SETTINGS_PATH)) {\n fs.unlinkSync(REAL_SETTINGS_PATH);\n }\n });\n\n test('returns null when settings file does not exist', () => {\n if (fs.existsSync(REAL_SETTINGS_PATH)) {\n fs.unlinkSync(REAL_SETTINGS_PATH);\n }\n const result = loadSettingsFromFile();\n expect(result).toBeNull();\n });\n\n test('returns parsed settings when file exists', () => {\n const mockSettings = {\n providers: { moonshot: { apiKey: 'test-key' } },\n preferences: { defaultProvider: 'moonshot' }\n };\n fs.writeFileSync(REAL_SETTINGS_PATH, JSON.stringify(mockSettings));\n\n const result = loadSettingsFromFile();\n expect(result?.providers?.moonshot?.apiKey).toBe('test-key');\n });\n\n test('handles malformed JSON gracefully', () => {\n fs.writeFileSync(REAL_SETTINGS_PATH, 'invalid json');\n\n // Should return null and log error\n const result = loadSettingsFromFile();\n expect(result).toBeNull();\n });\n });\n\n describe('mergeSettings', () => {\n beforeEach(() => {\n if (fs.existsSync(REAL_SETTINGS_PATH)) {\n originalSettings = fs.readFileSync(REAL_SETTINGS_PATH, 'utf-8');\n }\n // Clear env vars\n delete process.env.MOONSHOT_API_KEY;\n delete process.env.OPENAI_API_KEY;\n delete process.env.DEEPSEEK_API_KEY;\n });\n\n afterEach(() => {\n if (originalSettings !== null) {\n fs.writeFileSync(REAL_SETTINGS_PATH, originalSettings);\n originalSettings = null;\n } else if (fs.existsSync(REAL_SETTINGS_PATH)) {\n fs.unlinkSync(REAL_SETTINGS_PATH);\n }\n delete process.env.MOONSHOT_API_KEY;\n delete process.env.OPENAI_API_KEY;\n delete process.env.DEEPSEEK_API_KEY;\n });\n\n test('returns default provider when no settings', () => {\n if (fs.existsSync(REAL_SETTINGS_PATH)) {\n fs.unlinkSync(REAL_SETTINGS_PATH);\n }\n\n const merged = mergeSettings();\n expect(merged.preferences?.defaultProvider).toBe('moonshot');\n });\n\n test('environment variable overrides config file', () => {\n const mockSettings = {\n providers: { moonshot: { apiKey: 'file-key' } }\n };\n fs.writeFileSync(REAL_SETTINGS_PATH, JSON.stringify(mockSettings));\n process.env.MOONSHOT_API_KEY = 'env-key';\n\n const merged = mergeSettings();\n expect(merged.providers?.moonshot?.apiKey).toBe('env-key');\n });\n\n test('uses file apiKey when no env var', () => {\n const mockSettings = {\n providers: { moonshot: { apiKey: 'file-key' } }\n };\n fs.writeFileSync(REAL_SETTINGS_PATH, JSON.stringify(mockSettings));\n\n const merged = mergeSettings();\n expect(merged.providers?.moonshot?.apiKey).toBe('file-key');\n });\n\n test('applies default workingDir when not configured', () => {\n if (fs.existsSync(REAL_SETTINGS_PATH)) {\n fs.unlinkSync(REAL_SETTINGS_PATH);\n }\n\n const merged = mergeSettings();\n expect(merged.workingDir?.skillsDir).toBe(path.join(REAL_BRUCE_DIR, 'skills'));\n expect(merged.workingDir?.memoryDir).toBe(path.join(REAL_BRUCE_DIR, 'memory'));\n });\n\n test('respects custom workingDir from config', () => {\n const mockSettings = {\n workingDir: { skillsDir: '/custom/skills', memoryDir: '/custom/memory' }\n };\n fs.writeFileSync(REAL_SETTINGS_PATH, JSON.stringify(mockSettings));\n\n const merged = mergeSettings();\n expect(merged.workingDir?.skillsDir).toBe('/custom/skills');\n expect(merged.workingDir?.memoryDir).toBe('/custom/memory');\n });\n });\n\n describe('getEffectiveConfig', () => {\n beforeEach(() => {\n if (fs.existsSync(REAL_SETTINGS_PATH)) {\n originalSettings = fs.readFileSync(REAL_SETTINGS_PATH, 'utf-8');\n }\n delete process.env.MOONSHOT_API_KEY;\n delete process.env.OPENAI_API_KEY;\n delete process.env.DEEPSEEK_API_KEY;\n });\n\n afterEach(() => {\n if (originalSettings !== null) {\n fs.writeFileSync(REAL_SETTINGS_PATH, originalSettings);\n originalSettings = null;\n } else if (fs.existsSync(REAL_SETTINGS_PATH)) {\n fs.unlinkSync(REAL_SETTINGS_PATH);\n }\n delete process.env.MOONSHOT_API_KEY;\n delete process.env.OPENAI_API_KEY;\n delete process.env.DEEPSEEK_API_KEY;\n });\n\n test('throws error when no API key configured', () => {\n if (fs.existsSync(REAL_SETTINGS_PATH)) {\n fs.unlinkSync(REAL_SETTINGS_PATH);\n }\n\n expect(() => getEffectiveConfig()).toThrow(/No API key configured/);\n });\n\n test('returns valid config with env var', () => {\n process.env.MOONSHOT_API_KEY = 'test-key';\n\n const config = getEffectiveConfig();\n expect(config.provider).toBe('moonshot');\n expect(config.apiKey).toBe('test-key');\n expect(config.model).toBe('kimi-k2-turbo-preview');\n });\n\n test('returns valid config with file apiKey', () => {\n const mockSettings = {\n providers: { moonshot: { apiKey: 'file-key' } }\n };\n fs.writeFileSync(REAL_SETTINGS_PATH, JSON.stringify(mockSettings));\n\n const config = getEffectiveConfig();\n expect(config.apiKey).toBe('file-key');\n });\n\n test('respects defaultProvider preference', () => {\n const mockSettings = {\n providers: {\n moonshot: { apiKey: 'moonshot-key' },\n openai: { apiKey: 'openai-key' }\n },\n preferences: { defaultProvider: 'openai' }\n };\n fs.writeFileSync(REAL_SETTINGS_PATH, JSON.stringify(mockSettings));\n\n const config = getEffectiveConfig();\n expect(config.provider).toBe('openai');\n expect(config.apiKey).toBe('openai-key');\n });\n });\n\n describe('initSettings', () => {\n beforeEach(() => {\n if (fs.existsSync(REAL_SETTINGS_PATH)) {\n originalSettings = fs.readFileSync(REAL_SETTINGS_PATH, 'utf-8');\n fs.unlinkSync(REAL_SETTINGS_PATH);\n }\n // Ensure directory exists\n if (!fs.existsSync(REAL_BRUCE_DIR)) {\n fs.mkdirSync(REAL_BRUCE_DIR, { recursive: true });\n }\n });\n\n afterEach(() => {\n if (originalSettings !== null) {\n fs.writeFileSync(REAL_SETTINGS_PATH, originalSettings);\n originalSettings = null;\n } else if (fs.existsSync(REAL_SETTINGS_PATH)) {\n fs.unlinkSync(REAL_SETTINGS_PATH);\n }\n });\n\n test('creates settings file with template', () => {\n initSettings();\n\n expect(fs.existsSync(REAL_SETTINGS_PATH)).toBe(true);\n const content = JSON.parse(fs.readFileSync(REAL_SETTINGS_PATH, 'utf-8'));\n expect(content.providers).toBeDefined();\n expect(content.preferences).toBeDefined();\n expect(content.providers?.moonshot).toBeDefined();\n expect(content.providers?.openai).toBeDefined();\n expect(content.providers?.deepseek).toBeDefined();\n });\n });\n\n describe('Default models mapping', () => {\n test('default models are correct for each provider', () => {\n const defaultModels = {\n openai: 'gpt-4o-mini',\n moonshot: 'kimi-k2-turbo-preview',\n deepseek: 'deepseek-chat'\n };\n\n expect(defaultModels.openai).toBe('gpt-4o-mini');\n expect(defaultModels.moonshot).toBe('kimi-k2-turbo-preview');\n expect(defaultModels.deepseek).toBe('deepseek-chat');\n });\n });\n});"}}}} |