chore: linting and prettier

This commit is contained in:
Eyal Toledano
2025-05-22 04:17:06 -04:00
parent 5a91941913
commit 0c55ce0165
20 changed files with 2303 additions and 1785 deletions

View File

@@ -368,14 +368,13 @@ describe('Unified AI Services', () => {
// 3. The system throws an appropriate error if all providers' API keys are missing
// 4. Ollama is a special case where API key is optional and not checked
// 5. Session context is correctly used for API key checks
test('should skip provider with missing API key and try next in fallback sequence', async () => {
// Setup isApiKeySet to return false for anthropic but true for perplexity
mockIsApiKeySet
.mockImplementation((provider, session, root) => {
if (provider === 'anthropic') return false; // Main provider has no key
return true; // Other providers have keys
});
mockIsApiKeySet.mockImplementation((provider, session, root) => {
if (provider === 'anthropic') return false; // Main provider has no key
return true; // Other providers have keys
});
// Mock perplexity text response (since we'll skip anthropic)
mockGeneratePerplexityText.mockResolvedValue({
@@ -388,49 +387,59 @@ describe('Unified AI Services', () => {
prompt: 'Skip main provider test',
session: { env: {} }
};
const result = await generateTextService(params);
// Should have gotten the perplexity response
expect(result.mainResult).toBe('Perplexity response (skipped to research)');
expect(result.mainResult).toBe(
'Perplexity response (skipped to research)'
);
// Should check API keys
expect(mockIsApiKeySet).toHaveBeenCalledWith('anthropic', params.session, fakeProjectRoot);
expect(mockIsApiKeySet).toHaveBeenCalledWith('perplexity', params.session, fakeProjectRoot);
expect(mockIsApiKeySet).toHaveBeenCalledWith(
'anthropic',
params.session,
fakeProjectRoot
);
expect(mockIsApiKeySet).toHaveBeenCalledWith(
'perplexity',
params.session,
fakeProjectRoot
);
// Should log a warning
expect(mockLog).toHaveBeenCalledWith(
'warn',
expect.stringContaining(`Skipping role 'main' (Provider: anthropic): API key not set or invalid.`)
expect.stringContaining(
`Skipping role 'main' (Provider: anthropic): API key not set or invalid.`
)
);
// Should NOT call anthropic provider
expect(mockGenerateAnthropicText).not.toHaveBeenCalled();
// Should call perplexity provider
expect(mockGeneratePerplexityText).toHaveBeenCalledTimes(1);
});
test('should skip multiple providers with missing API keys and use first available', async () => {
// Setup: Main and fallback providers have no keys, only research has a key
mockIsApiKeySet
.mockImplementation((provider, session, root) => {
if (provider === 'anthropic') return false; // Main and fallback are both anthropic
if (provider === 'perplexity') return true; // Research has a key
return false;
});
mockIsApiKeySet.mockImplementation((provider, session, root) => {
if (provider === 'anthropic') return false; // Main and fallback are both anthropic
if (provider === 'perplexity') return true; // Research has a key
return false;
});
// Define different providers for testing multiple skips
mockGetFallbackProvider.mockReturnValue('openai'); // Different from main
mockGetFallbackModelId.mockReturnValue('test-openai-model');
// Mock isApiKeySet to return false for both main and fallback
mockIsApiKeySet
.mockImplementation((provider, session, root) => {
if (provider === 'anthropic') return false; // Main provider has no key
if (provider === 'openai') return false; // Fallback provider has no key
return true; // Research provider has a key
});
mockIsApiKeySet.mockImplementation((provider, session, root) => {
if (provider === 'anthropic') return false; // Main provider has no key
if (provider === 'openai') return false; // Fallback provider has no key
return true; // Research provider has a key
});
// Mock perplexity text response (since we'll skip to research)
mockGeneratePerplexityText.mockResolvedValue({
@@ -443,31 +452,49 @@ describe('Unified AI Services', () => {
prompt: 'Skip multiple providers test',
session: { env: {} }
};
const result = await generateTextService(params);
// Should have gotten the perplexity (research) response
expect(result.mainResult).toBe('Research response after skipping main and fallback');
expect(result.mainResult).toBe(
'Research response after skipping main and fallback'
);
// Should check API keys for all three roles
expect(mockIsApiKeySet).toHaveBeenCalledWith('anthropic', params.session, fakeProjectRoot);
expect(mockIsApiKeySet).toHaveBeenCalledWith('openai', params.session, fakeProjectRoot);
expect(mockIsApiKeySet).toHaveBeenCalledWith('perplexity', params.session, fakeProjectRoot);
expect(mockIsApiKeySet).toHaveBeenCalledWith(
'anthropic',
params.session,
fakeProjectRoot
);
expect(mockIsApiKeySet).toHaveBeenCalledWith(
'openai',
params.session,
fakeProjectRoot
);
expect(mockIsApiKeySet).toHaveBeenCalledWith(
'perplexity',
params.session,
fakeProjectRoot
);
// Should log warnings for both skipped providers
expect(mockLog).toHaveBeenCalledWith(
'warn',
expect.stringContaining(`Skipping role 'main' (Provider: anthropic): API key not set or invalid.`)
expect.stringContaining(
`Skipping role 'main' (Provider: anthropic): API key not set or invalid.`
)
);
expect(mockLog).toHaveBeenCalledWith(
'warn',
expect.stringContaining(`Skipping role 'fallback' (Provider: openai): API key not set or invalid.`)
expect.stringContaining(
`Skipping role 'fallback' (Provider: openai): API key not set or invalid.`
)
);
// Should NOT call skipped providers
expect(mockGenerateAnthropicText).not.toHaveBeenCalled();
expect(mockGenerateOpenAIText).not.toHaveBeenCalled();
// Should call perplexity provider
expect(mockGeneratePerplexityText).toHaveBeenCalledTimes(1);
});
@@ -490,23 +517,31 @@ describe('Unified AI Services', () => {
// Should log warnings for all skipped providers
expect(mockLog).toHaveBeenCalledWith(
'warn',
expect.stringContaining(`Skipping role 'main' (Provider: anthropic): API key not set or invalid.`)
expect.stringContaining(
`Skipping role 'main' (Provider: anthropic): API key not set or invalid.`
)
);
expect(mockLog).toHaveBeenCalledWith(
'warn',
expect.stringContaining(`Skipping role 'fallback' (Provider: anthropic): API key not set or invalid.`)
expect.stringContaining(
`Skipping role 'fallback' (Provider: anthropic): API key not set or invalid.`
)
);
expect(mockLog).toHaveBeenCalledWith(
'warn',
expect.stringContaining(`Skipping role 'research' (Provider: perplexity): API key not set or invalid.`)
expect.stringContaining(
`Skipping role 'research' (Provider: perplexity): API key not set or invalid.`
)
);
// Should log final error
expect(mockLog).toHaveBeenCalledWith(
'error',
expect.stringContaining('All roles in the sequence [main, fallback, research] failed.')
expect.stringContaining(
'All roles in the sequence [main, fallback, research] failed.'
)
);
// Should NOT call any providers
expect(mockGenerateAnthropicText).not.toHaveBeenCalled();
expect(mockGeneratePerplexityText).not.toHaveBeenCalled();
@@ -516,7 +551,7 @@ describe('Unified AI Services', () => {
// Setup: Set main provider to ollama
mockGetMainProvider.mockReturnValue('ollama');
mockGetMainModelId.mockReturnValue('llama3');
// Mock Ollama text generation to succeed
mockGenerateOllamaText.mockResolvedValue({
text: 'Ollama response (no API key required)',
@@ -528,17 +563,17 @@ describe('Unified AI Services', () => {
prompt: 'Ollama special case test',
session: { env: {} }
};
const result = await generateTextService(params);
// Should have gotten the Ollama response
expect(result.mainResult).toBe('Ollama response (no API key required)');
// isApiKeySet shouldn't be called for Ollama
// Note: This is indirect - the code just doesn't check isApiKeySet for ollama
// so we're verifying ollama provider was called despite isApiKeySet being mocked to false
mockIsApiKeySet.mockReturnValue(false); // Should be ignored for Ollama
// Should call Ollama provider
expect(mockGenerateOllamaText).toHaveBeenCalledTimes(1);
});
@@ -546,14 +581,13 @@ describe('Unified AI Services', () => {
test('should correctly use the provided session for API key check', async () => {
// Mock custom session object with env vars
const customSession = { env: { ANTHROPIC_API_KEY: 'session-api-key' } };
// Setup API key check to verify the session is passed correctly
mockIsApiKeySet
.mockImplementation((provider, session, root) => {
// Only return true if the correct session was provided
return session === customSession;
});
mockIsApiKeySet.mockImplementation((provider, session, root) => {
// Only return true if the correct session was provided
return session === customSession;
});
// Mock the anthropic response
mockGenerateAnthropicText.mockResolvedValue({
text: 'Anthropic response with session key',
@@ -565,12 +599,16 @@ describe('Unified AI Services', () => {
prompt: 'Session API key test',
session: customSession
};
const result = await generateTextService(params);
// Should check API key with the custom session
expect(mockIsApiKeySet).toHaveBeenCalledWith('anthropic', customSession, fakeProjectRoot);
expect(mockIsApiKeySet).toHaveBeenCalledWith(
'anthropic',
customSession,
fakeProjectRoot
);
// Should have gotten the anthropic response
expect(result.mainResult).toBe('Anthropic response with session key');
});

View File

@@ -182,7 +182,7 @@ describe('Config Manager Module', () => {
// Dynamically import the module under test AFTER mocking dependencies
configManager = await import('../../scripts/modules/config-manager.js');
// --- Default Mock Implementations ---
mockFindProjectRoot.mockReturnValue(MOCK_PROJECT_ROOT); // Default for utils.findProjectRoot
mockExistsSync.mockReturnValue(true); // Assume files exist by default
@@ -256,7 +256,10 @@ describe('Config Manager Module', () => {
configManager.validateProviderModelCombination('ollama', 'any-model')
).toBe(false);
expect(
configManager.validateProviderModelCombination('openrouter', 'any/model')
configManager.validateProviderModelCombination(
'openrouter',
'any/model'
)
).toBe(false);
});
@@ -367,7 +370,8 @@ describe('Config Manager Module', () => {
test('should merge defaults for partial config file', () => {
// Arrange
mockReadFileSync.mockImplementation((filePath) => {
if (filePath === MOCK_CONFIG_PATH) return JSON.stringify(PARTIAL_CONFIG);
if (filePath === MOCK_CONFIG_PATH)
return JSON.stringify(PARTIAL_CONFIG);
if (path.basename(filePath) === 'supported-models.json') {
return JSON.stringify({
openai: [{ id: 'gpt-4-turbo' }],
@@ -391,7 +395,10 @@ describe('Config Manager Module', () => {
// Assert: Construct expected merged config
const expectedMergedConfig = {
models: {
main: { ...DEFAULT_CONFIG.models.main, ...PARTIAL_CONFIG.models.main },
main: {
...DEFAULT_CONFIG.models.main,
...PARTIAL_CONFIG.models.main
},
research: { ...DEFAULT_CONFIG.models.research },
fallback: { ...DEFAULT_CONFIG.models.fallback }
},
@@ -456,7 +463,9 @@ describe('Config Manager Module', () => {
// Assert
expect(config).toEqual(DEFAULT_CONFIG);
expect(consoleErrorSpy).toHaveBeenCalledWith(
expect.stringContaining(`Permission denied. Using default configuration.`)
expect.stringContaining(
`Permission denied. Using default configuration.`
)
);
});
@@ -688,72 +697,182 @@ describe('Config Manager Module', () => {
// Test cases: [providerName, envVarName, keyValue, expectedResult, testName]
const testCases = [
// Valid Keys
['anthropic', 'ANTHROPIC_API_KEY', 'sk-valid-key', true, 'valid Anthropic key'],
['openai', 'OPENAI_API_KEY', 'sk-another-valid-key', true, 'valid OpenAI key'],
['perplexity', 'PERPLEXITY_API_KEY', 'pplx-valid', true, 'valid Perplexity key'],
['google', 'GOOGLE_API_KEY', 'google-valid-key', true, 'valid Google key'],
['mistral', 'MISTRAL_API_KEY', 'mistral-valid-key', true, 'valid Mistral key'],
['openrouter', 'OPENROUTER_API_KEY', 'or-valid-key', true, 'valid OpenRouter key'],
[
'anthropic',
'ANTHROPIC_API_KEY',
'sk-valid-key',
true,
'valid Anthropic key'
],
[
'openai',
'OPENAI_API_KEY',
'sk-another-valid-key',
true,
'valid OpenAI key'
],
[
'perplexity',
'PERPLEXITY_API_KEY',
'pplx-valid',
true,
'valid Perplexity key'
],
[
'google',
'GOOGLE_API_KEY',
'google-valid-key',
true,
'valid Google key'
],
[
'mistral',
'MISTRAL_API_KEY',
'mistral-valid-key',
true,
'valid Mistral key'
],
[
'openrouter',
'OPENROUTER_API_KEY',
'or-valid-key',
true,
'valid OpenRouter key'
],
['xai', 'XAI_API_KEY', 'xai-valid-key', true, 'valid XAI key'],
['azure', 'AZURE_OPENAI_API_KEY', 'azure-valid-key', true, 'valid Azure key'],
[
'azure',
'AZURE_OPENAI_API_KEY',
'azure-valid-key',
true,
'valid Azure key'
],
// Ollama (special case - no key needed)
['ollama', 'OLLAMA_API_KEY', undefined, true, 'Ollama provider (no key needed)'], // OLLAMA_API_KEY might not be in keyMap
[
'ollama',
'OLLAMA_API_KEY',
undefined,
true,
'Ollama provider (no key needed)'
], // OLLAMA_API_KEY might not be in keyMap
// Invalid / Missing Keys
['anthropic', 'ANTHROPIC_API_KEY', undefined, false, 'missing Anthropic key'],
[
'anthropic',
'ANTHROPIC_API_KEY',
undefined,
false,
'missing Anthropic key'
],
['anthropic', 'ANTHROPIC_API_KEY', null, false, 'null Anthropic key'],
['openai', 'OPENAI_API_KEY', '', false, 'empty OpenAI key'],
['perplexity', 'PERPLEXITY_API_KEY', ' ', false, 'whitespace Perplexity key'],
[
'perplexity',
'PERPLEXITY_API_KEY',
' ',
false,
'whitespace Perplexity key'
],
// Placeholder Keys
['google', 'GOOGLE_API_KEY', 'YOUR_GOOGLE_API_KEY_HERE', false, 'placeholder Google key (YOUR_..._HERE)'],
['mistral', 'MISTRAL_API_KEY', 'MISTRAL_KEY_HERE', false, 'placeholder Mistral key (..._KEY_HERE)'],
['openrouter', 'OPENROUTER_API_KEY', 'ENTER_OPENROUTER_KEY_HERE', false, 'placeholder OpenRouter key (general ...KEY_HERE)'],
[
'google',
'GOOGLE_API_KEY',
'YOUR_GOOGLE_API_KEY_HERE',
false,
'placeholder Google key (YOUR_..._HERE)'
],
[
'mistral',
'MISTRAL_API_KEY',
'MISTRAL_KEY_HERE',
false,
'placeholder Mistral key (..._KEY_HERE)'
],
[
'openrouter',
'OPENROUTER_API_KEY',
'ENTER_OPENROUTER_KEY_HERE',
false,
'placeholder OpenRouter key (general ...KEY_HERE)'
],
// Unknown provider
['unknownprovider', 'UNKNOWN_KEY', 'any-key', false, 'unknown provider'],
['unknownprovider', 'UNKNOWN_KEY', 'any-key', false, 'unknown provider']
];
testCases.forEach(([providerName, envVarName, keyValue, expectedResult, testName]) => {
test(`should return ${expectedResult} for ${testName} (CLI context)`, () => {
// CLI context (resolveEnvVariable uses process.env or .env via projectRoot)
mockResolveEnvVariable.mockImplementation((key) => {
return key === envVarName ? keyValue : undefined;
testCases.forEach(
([providerName, envVarName, keyValue, expectedResult, testName]) => {
test(`should return ${expectedResult} for ${testName} (CLI context)`, () => {
// CLI context (resolveEnvVariable uses process.env or .env via projectRoot)
mockResolveEnvVariable.mockImplementation((key) => {
return key === envVarName ? keyValue : undefined;
});
expect(
configManager.isApiKeySet(providerName, null, MOCK_PROJECT_ROOT)
).toBe(expectedResult);
if (providerName !== 'ollama' && providerName !== 'unknownprovider') {
// Ollama and unknown don't try to resolve
expect(mockResolveEnvVariable).toHaveBeenCalledWith(
envVarName,
null,
MOCK_PROJECT_ROOT
);
}
});
expect(configManager.isApiKeySet(providerName, null, MOCK_PROJECT_ROOT)).toBe(expectedResult);
if (providerName !== 'ollama' && providerName !== 'unknownprovider') { // Ollama and unknown don't try to resolve
expect(mockResolveEnvVariable).toHaveBeenCalledWith(envVarName, null, MOCK_PROJECT_ROOT);
}
});
test(`should return ${expectedResult} for ${testName} (MCP context)`, () => {
// MCP context (resolveEnvVariable uses session.env)
const mcpSession = { env: { [envVarName]: keyValue } };
mockResolveEnvVariable.mockImplementation((key, sessionArg) => {
return sessionArg && sessionArg.env ? sessionArg.env[key] : undefined;
test(`should return ${expectedResult} for ${testName} (MCP context)`, () => {
// MCP context (resolveEnvVariable uses session.env)
const mcpSession = { env: { [envVarName]: keyValue } };
mockResolveEnvVariable.mockImplementation((key, sessionArg) => {
return sessionArg && sessionArg.env
? sessionArg.env[key]
: undefined;
});
expect(
configManager.isApiKeySet(providerName, mcpSession, null)
).toBe(expectedResult);
if (providerName !== 'ollama' && providerName !== 'unknownprovider') {
expect(mockResolveEnvVariable).toHaveBeenCalledWith(
envVarName,
mcpSession,
null
);
}
});
expect(configManager.isApiKeySet(providerName, mcpSession, null)).toBe(expectedResult);
if (providerName !== 'ollama' && providerName !== 'unknownprovider') {
expect(mockResolveEnvVariable).toHaveBeenCalledWith(envVarName, mcpSession, null);
}
});
});
}
);
test('isApiKeySet should log a warning for an unknown provider', () => {
mockLog.mockClear(); // Clear previous log calls
configManager.isApiKeySet('nonexistentprovider');
expect(mockLog).toHaveBeenCalledWith('warn', expect.stringContaining('Unknown provider name: nonexistentprovider'));
expect(mockLog).toHaveBeenCalledWith(
'warn',
expect.stringContaining('Unknown provider name: nonexistentprovider')
);
});
test('isApiKeySet should handle provider names case-insensitively for keyMap lookup', () => {
mockResolveEnvVariable.mockReturnValue('a-valid-key');
expect(configManager.isApiKeySet('Anthropic', null, MOCK_PROJECT_ROOT)).toBe(true);
expect(mockResolveEnvVariable).toHaveBeenCalledWith('ANTHROPIC_API_KEY', null, MOCK_PROJECT_ROOT);
expect(
configManager.isApiKeySet('Anthropic', null, MOCK_PROJECT_ROOT)
).toBe(true);
expect(mockResolveEnvVariable).toHaveBeenCalledWith(
'ANTHROPIC_API_KEY',
null,
MOCK_PROJECT_ROOT
);
mockResolveEnvVariable.mockReturnValue('another-valid-key');
expect(configManager.isApiKeySet('OPENAI', null, MOCK_PROJECT_ROOT)).toBe(true);
expect(mockResolveEnvVariable).toHaveBeenCalledWith('OPENAI_API_KEY', null, MOCK_PROJECT_ROOT);
expect(configManager.isApiKeySet('OPENAI', null, MOCK_PROJECT_ROOT)).toBe(
true
);
expect(mockResolveEnvVariable).toHaveBeenCalledWith(
'OPENAI_API_KEY',
null,
MOCK_PROJECT_ROOT
);
});
});
});