// ============================================================================ // AI Testing Suite - Configuration // ============================================================================ import * as path from 'path'; import * as fs from 'fs'; import { SuiteConfig } from './types'; const DEFAULT_CONFIG: SuiteConfig = { projectPath: process.cwd(), outputDir: './tests', reportsDir: './reports', llmProvider: 'openai', llmModel: 'gpt-4o', apiKey: '', temperature: 0.1, maxTokens: 4096, testTypes: ['unit', 'integration', 'e2e', 'security'], testRunner: 'vitest', e2eRunner: 'playwright', coverageTool: 'v8', securityScanDepth: 'deep', checkZeroDay: true, checkOwaspTop10: true, checkDependencies: true, maxAgentIterations: 10, verbose: true, parallel: false, }; export function loadConfig(overrides: Partial = {}): SuiteConfig { // Load .env if present const envPath = path.resolve(process.cwd(), '.env'); if (fs.existsSync(envPath)) { const envContent = fs.readFileSync(envPath, 'utf-8'); for (const line of envContent.split('\n')) { const trimmed = line.trim(); if (trimmed && !trimmed.startsWith('#')) { const eqIdx = trimmed.indexOf('='); if (eqIdx > 0) { const key = trimmed.substring(0, eqIdx).trim(); const value = trimmed.substring(eqIdx + 1).trim(); if (!process.env[key]) { process.env[key] = value; } } } } } const envConfig: Partial = {}; if (process.env.OPENAI_API_KEY) { envConfig.apiKey = process.env.OPENAI_API_KEY; envConfig.llmProvider = 'openai'; } if (process.env.ANTHROPIC_API_KEY) { envConfig.apiKey = process.env.ANTHROPIC_API_KEY; envConfig.llmProvider = 'anthropic'; } if (process.env.LLM_PROVIDER) { envConfig.llmProvider = process.env.LLM_PROVIDER as 'openai' | 'anthropic' | 'openai_compatible' | 'ollama'; // For openai_compatible, use OPENAI_API_KEY if no specific key set if (process.env.LLM_PROVIDER === 'openai_compatible' && !envConfig.apiKey && process.env.OPENAI_API_KEY) { envConfig.apiKey = process.env.OPENAI_API_KEY; } // Ollama runs locally, no API key needed if (process.env.LLM_PROVIDER === 'ollama') { envConfig.apiKey = envConfig.apiKey || 'ollama'; envConfig.llmModel = envConfig.llmModel || process.env.LLM_MODEL || 'llama3.2'; } } if (process.env.LLM_MODEL) { envConfig.llmModel = process.env.LLM_MODEL; } if (process.env.LLM_TEMPERATURE) { envConfig.temperature = parseFloat(process.env.LLM_TEMPERATURE); } if (process.env.LLM_MAX_TOKENS) { envConfig.maxTokens = parseInt(process.env.LLM_MAX_TOKENS, 10); } if (process.env.TARGET_PROJECT_PATH) { envConfig.projectPath = path.resolve(process.env.TARGET_PROJECT_PATH); } if (process.env.TESTS_OUTPUT_DIR) { envConfig.outputDir = process.env.TESTS_OUTPUT_DIR; } if (process.env.REPORTS_OUTPUT_DIR) { envConfig.reportsDir = process.env.REPORTS_OUTPUT_DIR; } if (process.env.SECURITY_SCAN_DEPTH) { envConfig.securityScanDepth = process.env.SECURITY_SCAN_DEPTH as 'basic' | 'standard' | 'deep'; } if (process.env.MAX_AGENT_ITERATIONS) { envConfig.maxAgentIterations = parseInt(process.env.MAX_AGENT_ITERATIONS, 10); } if (process.env.AGENT_VERBOSE) { envConfig.verbose = process.env.AGENT_VERBOSE === 'true'; } if (process.env.TEST_RUNNER) { envConfig.testRunner = process.env.TEST_RUNNER as 'jest' | 'vitest' | 'node'; } if (process.env.E2E_RUNNER) { envConfig.e2eRunner = process.env.E2E_RUNNER as 'supertest' | 'playwright' | 'none'; } if (process.env.COVERAGE_TOOL) { envConfig.coverageTool = process.env.COVERAGE_TOOL as 'istanbul' | 'c8' | 'v8'; } return { ...DEFAULT_CONFIG, ...envConfig, ...overrides }; } export function validateConfig(config: SuiteConfig): string[] { const errors: string[] = []; if (!config.apiKey && config.llmProvider !== 'ollama') { errors.push('API key is required. Set OPENAI_API_KEY or ANTHROPIC_API_KEY in .env'); } if (!fs.existsSync(config.projectPath)) { errors.push(`Project path does not exist: ${config.projectPath}`); } if (!['openai', 'anthropic', 'openai_compatible', 'ollama'].includes(config.llmProvider)) { errors.push(`Invalid LLM provider: ${config.llmProvider}. Use "openai", "anthropic", "openai_compatible", or "ollama".`); } return errors; } export function resolveProjectPath(projectPath: string): string { return path.isAbsolute(projectPath) ? projectPath : path.resolve(process.cwd(), projectPath); }