268 lines
8.7 KiB
JavaScript
268 lines
8.7 KiB
JavaScript
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
import { loadConfig } from '../../src/config/env.js';
|
|
|
|
const originalEnv = { ...process.env };
|
|
|
|
function setEnv(partial) {
|
|
for (const key of Object.keys(process.env)) {
|
|
if (key.startsWith('OPENAI_') || key.startsWith('CHATGPT_MCP_') || key.startsWith('OLLAMA_')) {
|
|
delete process.env[key];
|
|
}
|
|
}
|
|
Object.assign(process.env, partial);
|
|
}
|
|
|
|
beforeEach(() => {
|
|
setEnv({ OPENAI_API_KEY: 'test-key' });
|
|
});
|
|
|
|
afterEach(() => {
|
|
for (const key of Object.keys(process.env)) {
|
|
if (key.startsWith('OPENAI_') || key.startsWith('CHATGPT_MCP_') || key.startsWith('OLLAMA_')) {
|
|
delete process.env[key];
|
|
}
|
|
}
|
|
Object.assign(process.env, originalEnv);
|
|
});
|
|
|
|
describe('loadConfig', () => {
|
|
it('throws when OPENAI_API_KEY is missing', () => {
|
|
delete process.env.OPENAI_API_KEY;
|
|
expect(() => loadConfig()).toThrow(
|
|
'Configuration error: OPENAI_API_KEY is missing.'
|
|
);
|
|
});
|
|
|
|
it('returns defaults when no optional vars are set', () => {
|
|
const cfg = loadConfig();
|
|
expect(cfg.openaiApiKey).toBe('test-key');
|
|
expect(cfg.openaiModel).toBe('gpt-5.1');
|
|
expect(cfg.temperature).toBe(0.2);
|
|
expect(cfg.maxOutputTokens).toBe(2000);
|
|
expect(cfg.logLevel).toBe('info');
|
|
expect(cfg.enableFileContext).toBe(false);
|
|
expect(cfg.contextDir).toBe('./context');
|
|
expect(cfg.maxInputChars).toBe(30000);
|
|
expect(cfg.maxFileChars).toBe(12000);
|
|
expect(cfg.maxFiles).toBe(5);
|
|
expect(cfg.maxLogChars).toBe(10000);
|
|
expect(cfg.redactSecrets).toBe(true);
|
|
expect(cfg.ollamaBaseUrl).toBe('http://localhost:11434');
|
|
expect(cfg.ollamaModel).toBe('qwen3:latest');
|
|
expect(cfg.ollamaTemperature).toBe(0.2);
|
|
expect(cfg.ollamaTimeout).toBe(60);
|
|
});
|
|
|
|
it('applies custom string values', () => {
|
|
setEnv({
|
|
OPENAI_API_KEY: 'test-key',
|
|
OPENAI_MODEL: 'gpt-4',
|
|
CHATGPT_MCP_LOG_LEVEL: 'debug',
|
|
CHATGPT_MCP_CONTEXT_DIR: './custom',
|
|
});
|
|
const cfg = loadConfig();
|
|
expect(cfg.openaiModel).toBe('gpt-4');
|
|
expect(cfg.logLevel).toBe('debug');
|
|
expect(cfg.contextDir).toBe('./custom');
|
|
});
|
|
|
|
it('applies custom numeric values', () => {
|
|
setEnv({
|
|
OPENAI_API_KEY: 'test-key',
|
|
OPENAI_TEMPERATURE: '0.5',
|
|
OPENAI_MAX_OUTPUT_TOKENS: '4096',
|
|
CHATGPT_MCP_MAX_INPUT_CHARS: '15000',
|
|
CHATGPT_MCP_MAX_FILE_CHARS: '8000',
|
|
CHATGPT_MCP_MAX_FILES: '3',
|
|
CHATGPT_MCP_MAX_LOG_CHARS: '5000',
|
|
});
|
|
const cfg = loadConfig();
|
|
expect(cfg.temperature).toBe(0.5);
|
|
expect(cfg.maxOutputTokens).toBe(4096);
|
|
expect(cfg.maxInputChars).toBe(15000);
|
|
expect(cfg.maxFileChars).toBe(8000);
|
|
expect(cfg.maxFiles).toBe(3);
|
|
expect(cfg.maxLogChars).toBe(5000);
|
|
});
|
|
|
|
it('applies custom boolean values', () => {
|
|
setEnv({
|
|
OPENAI_API_KEY: 'test-key',
|
|
CHATGPT_MCP_ENABLE_FILE_CONTEXT: 'true',
|
|
CHATGPT_MCP_REDACT_SECRETS: 'false',
|
|
});
|
|
const cfg = loadConfig();
|
|
expect(cfg.enableFileContext).toBe(true);
|
|
expect(cfg.redactSecrets).toBe(false);
|
|
});
|
|
|
|
it('throws on invalid numeric values', () => {
|
|
setEnv({
|
|
OPENAI_API_KEY: 'test-key',
|
|
CHATGPT_MCP_MAX_INPUT_CHARS: 'abc',
|
|
});
|
|
expect(() => loadConfig()).toThrow(
|
|
'Configuration error: CHATGPT_MCP_MAX_INPUT_CHARS must be a positive number, got "abc".'
|
|
);
|
|
});
|
|
|
|
it('throws on invalid boolean values', () => {
|
|
setEnv({
|
|
OPENAI_API_KEY: 'test-key',
|
|
CHATGPT_MCP_ENABLE_FILE_CONTEXT: 'yes',
|
|
});
|
|
expect(() => loadConfig()).toThrow(
|
|
'Configuration error: CHATGPT_MCP_ENABLE_FILE_CONTEXT must be true or false, got "yes".'
|
|
);
|
|
});
|
|
|
|
it('throws on negative numeric values', () => {
|
|
setEnv({
|
|
OPENAI_API_KEY: 'test-key',
|
|
CHATGPT_MCP_MAX_FILES: '-1',
|
|
});
|
|
expect(() => loadConfig()).toThrow(
|
|
'Configuration error: CHATGPT_MCP_MAX_FILES must be a positive number, got "-1".'
|
|
);
|
|
});
|
|
|
|
it('throws on NaN numeric values', () => {
|
|
setEnv({
|
|
OPENAI_API_KEY: 'test-key',
|
|
CHATGPT_MCP_MAX_FILES: 'NaN',
|
|
});
|
|
expect(() => loadConfig()).toThrow(
|
|
'Configuration error: CHATGPT_MCP_MAX_FILES must be a positive number, got "NaN".'
|
|
);
|
|
});
|
|
|
|
it('throws on Infinity numeric values', () => {
|
|
setEnv({
|
|
OPENAI_API_KEY: 'test-key',
|
|
CHATGPT_MCP_MAX_FILES: 'Infinity',
|
|
});
|
|
expect(() => loadConfig()).toThrow(
|
|
'Configuration error: CHATGPT_MCP_MAX_FILES must be a positive number, got "Infinity".'
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('loadConfig chatgptMcpProvider', () => {
|
|
it('defaults to "openai" when CHATGPT_MCP_PROVIDER is not set', () => {
|
|
delete process.env.CHATGPT_MCP_PROVIDER;
|
|
const cfg = loadConfig();
|
|
expect(cfg.chatgptMcpProvider).toBe('openai');
|
|
});
|
|
|
|
it('accepts "openai" value', () => {
|
|
setEnv({ CHATGPT_MCP_PROVIDER: 'openai', OPENAI_API_KEY: 'sk-test' });
|
|
const cfg = loadConfig();
|
|
expect(cfg.chatgptMcpProvider).toBe('openai');
|
|
});
|
|
|
|
it('accepts any string value (validation is in factory)', () => {
|
|
setEnv({ CHATGPT_MCP_PROVIDER: 'ollama', OPENAI_API_KEY: 'sk-test' });
|
|
const cfg = loadConfig();
|
|
expect(cfg.chatgptMcpProvider).toBe('ollama');
|
|
});
|
|
});
|
|
|
|
// --- Ollama env vars ---
|
|
|
|
describe('loadConfig ollama env vars', () => {
|
|
it('returns default ollama values when no OLLAMA_ vars set', () => {
|
|
const cfg = loadConfig();
|
|
expect(cfg.ollamaBaseUrl).toBe('http://localhost:11434');
|
|
expect(cfg.ollamaModel).toBe('qwen3:latest');
|
|
expect(cfg.ollamaTemperature).toBe(0.2);
|
|
expect(cfg.ollamaTimeout).toBe(60);
|
|
});
|
|
|
|
it('applies custom OLLAMA_BASE_URL', () => {
|
|
setEnv({ OPENAI_API_KEY: 'test-key', OLLAMA_BASE_URL: 'http://localhost:11435' });
|
|
const cfg = loadConfig();
|
|
expect(cfg.ollamaBaseUrl).toBe('http://localhost:11435');
|
|
});
|
|
|
|
it('returns raw OLLAMA_BASE_URL — trailing slash normalization is in the provider', () => {
|
|
setEnv({ OPENAI_API_KEY: 'test-key', OLLAMA_BASE_URL: 'http://localhost:11434/' });
|
|
const cfg = loadConfig();
|
|
expect(cfg.ollamaBaseUrl).toBe('http://localhost:11434/');
|
|
});
|
|
|
|
it('applies custom OLLAMA_MODEL', () => {
|
|
setEnv({ OPENAI_API_KEY: 'test-key', OLLAMA_MODEL: 'llama3' });
|
|
const cfg = loadConfig();
|
|
expect(cfg.ollamaModel).toBe('llama3');
|
|
});
|
|
|
|
it('applies custom OLLAMA_TEMPERATURE', () => {
|
|
setEnv({ OPENAI_API_KEY: 'test-key', OLLAMA_TEMPERATURE: '0.7' });
|
|
const cfg = loadConfig();
|
|
expect(cfg.ollamaTemperature).toBe(0.7);
|
|
});
|
|
|
|
it('applies custom OLLAMA_TIMEOUT', () => {
|
|
setEnv({ OPENAI_API_KEY: 'test-key', OLLAMA_TIMEOUT: '120' });
|
|
const cfg = loadConfig();
|
|
expect(cfg.ollamaTimeout).toBe(120);
|
|
});
|
|
|
|
it('throws on invalid OLLAMA_TEMPERATURE', () => {
|
|
setEnv({ OPENAI_API_KEY: 'test-key', OLLAMA_TEMPERATURE: 'abc' });
|
|
expect(() => loadConfig()).toThrow(
|
|
'Configuration error: OLLAMA_TEMPERATURE must be a positive number, got "abc".'
|
|
);
|
|
});
|
|
|
|
it('throws on negative OLLAMA_TIMEOUT', () => {
|
|
setEnv({ OPENAI_API_KEY: 'test-key', OLLAMA_TIMEOUT: '-10' });
|
|
expect(() => loadConfig()).toThrow(
|
|
'Configuration error: OLLAMA_TIMEOUT must be a positive number, got "-10".'
|
|
);
|
|
});
|
|
|
|
it('works with ollama provider and all ollama env vars', () => {
|
|
setEnv({
|
|
OPENAI_API_KEY: 'sk-test',
|
|
CHATGPT_MCP_PROVIDER: 'ollama',
|
|
OLLAMA_BASE_URL: 'http://ollama-server:8080',
|
|
OLLAMA_MODEL: 'mistral',
|
|
OLLAMA_TEMPERATURE: '0.5',
|
|
OLLAMA_TIMEOUT: '90',
|
|
});
|
|
const cfg = loadConfig();
|
|
expect(cfg.chatgptMcpProvider).toBe('ollama');
|
|
expect(cfg.ollamaBaseUrl).toBe('http://ollama-server:8080');
|
|
expect(cfg.ollamaModel).toBe('mistral');
|
|
expect(cfg.ollamaTemperature).toBe(0.5);
|
|
expect(cfg.ollamaTimeout).toBe(90);
|
|
});
|
|
});
|
|
|
|
describe('loadConfig ollama env vars with trailing slash', () => {
|
|
it('returns raw base URL — normalization happens in the provider', () => {
|
|
setEnv({ OPENAI_API_KEY: 'test-key', OLLAMA_BASE_URL: 'http://localhost:11434//' });
|
|
const cfg = loadConfig();
|
|
expect(cfg.ollamaBaseUrl).toBe('http://localhost:11434//');
|
|
});
|
|
|
|
it('returns raw base URL with multiple slashes', () => {
|
|
setEnv({ OPENAI_API_KEY: 'test-key', OLLAMA_BASE_URL: 'http://localhost:11434///' });
|
|
const cfg = loadConfig();
|
|
expect(cfg.ollamaBaseUrl).toBe('http://localhost:11434///');
|
|
});
|
|
|
|
it('handles base URL without trailing slash', () => {
|
|
setEnv({ OPENAI_API_KEY: 'test-key', OLLAMA_BASE_URL: 'http://localhost:11434' });
|
|
const cfg = loadConfig();
|
|
expect(cfg.ollamaBaseUrl).toBe('http://localhost:11434');
|
|
});
|
|
|
|
it('handles empty base URL as default', () => {
|
|
setEnv({ OPENAI_API_KEY: 'test-key', OLLAMA_BASE_URL: '' });
|
|
const cfg = loadConfig();
|
|
expect(cfg.ollamaBaseUrl).toBe('http://localhost:11434');
|
|
});
|
|
});
|