/** * Integrated API Key Test Suite * * This test combines the best features from multiple test files: * - Dialog detection from test_api_key_settings.js * - CSRF handling from test_api_key_with_csrf.js * - Strong password validation from test_api_key_with_auth.js * - API-only testing from test_api_key_fixed.js * - Persistence verification from test_api_key_persistence.js */ const puppeteer = require('puppeteer'); const AuthHelper = require('./auth_helper'); const { getPuppeteerLaunchOptions } = require('./puppeteer_config'); // Test configuration const TEST_USER = 'apitest_' + Date.now(); const TEST_PASSWORD = 'TestPassword123!'; const TEST_API_KEY = 'sk-test-integrated-key-' + Date.now(); const BASE_URL = 'http://127.0.0.1:5000'; const API_KEY_SETTING = 'llm.openai_endpoint.api_key'; // Color codes for better output const colors = { reset: '\x1b[0m', bright: '\x1b[1m', red: '\x1b[31m', green: '\x1b[32m', yellow: '\x1b[33m', blue: '\x1b[34m', magenta: '\x1b[35m', cyan: '\x1b[36m' }; function log(message, type = 'info') { const timestamp = new Date().toISOString(); const typeColors = { info: colors.blue, success: colors.green, error: colors.red, warning: colors.yellow, debug: colors.cyan }; const color = typeColors[type] || colors.reset; const symbol = { info: 'ℹ', success: 'āœ“', error: 'āœ—', warning: '⚠', debug: 'šŸ”' }[type] || '•'; console.log(`${color}${symbol}${colors.reset} [${timestamp}] ${message}`); } // Helper to get CSRF token async function getCSRFToken(page) { return await page.evaluate(() => { // Try multiple methods to get CSRF token const metaTag = document.querySelector('meta[name="csrf-token"]'); if (metaTag) return metaTag.content; const csrfInput = document.querySelector('input[name="csrf_token"]'); if (csrfInput) return csrfInput.value; // Check cookies const cookies = document.cookie.split(';'); for (const cookie of cookies) { const [name, value] = cookie.trim().split('='); if (name === 'csrf_token' || name === 'X-CSRFToken') { return value; } } return null; }); } // Main test function (async () => { let browser; let authHelper; const dialogMessages = []; try { log('Starting Integrated API Key Test Suite', 'info'); // Launch browser with debugging options browser = await puppeteer.launch(getPuppeteerLaunchOptions()); const page = await browser.newPage(); // Enable console logging page.on('console', msg => { if (msg.type() === 'error') { log(`[BROWSER ERROR] ${msg.text()}`, 'error'); } }); // Dialog detection (critical for user selection bug) page.on('dialog', async dialog => { const message = dialog.message(); const type = dialog.type(); log(`Dialog detected! Type: ${type}, Message: ${message}`, 'warning'); dialogMessages.push({ type, message, timestamp: new Date() }); // Take screenshot when dialog appears try { await page.screenshot({ path: `dialog_screenshot_${Date.now()}.png`, fullPage: true }); } catch { log('Failed to capture screenshot during dialog', 'error'); } // Auto-accept dialogs await dialog.accept(); }); // Network request logging for debugging if (process.env.DEBUG === 'true') { page.on('request', request => { if (request.url().includes('/settings/')) { log(`[REQUEST] ${request.method()} ${request.url()}`, 'debug'); } }); page.on('response', response => { if (response.url().includes('/settings/')) { log(`[RESPONSE] ${response.status()} ${response.url()}`, 'debug'); } }); } authHelper = new AuthHelper(page, BASE_URL); // Test 1: Register new user with strong password validation log('Test 1: User Registration', 'info'); try { await authHelper.register(TEST_USER, TEST_PASSWORD); log('User registered successfully', 'success'); } catch (error) { if (error.message.includes('already exists')) { log('User already exists, attempting login', 'warning'); await authHelper.login(TEST_USER, TEST_PASSWORD); } else { throw error; } } // Test 2: Navigate to settings and wait for full load log('Test 2: Navigate to Settings', 'info'); await page.goto(`${BASE_URL}/settings`, { waitUntil: 'domcontentloaded' }); await page.waitForSelector('input, button', { visible: true, timeout: 10000 }); // Test 3: Get CSRF token log('Test 3: CSRF Token Handling', 'info'); const csrfToken = await getCSRFToken(page); if (csrfToken) { log(`CSRF token found: ${csrfToken.substring(0, 10)}...`, 'success'); } else { log('No CSRF token found, continuing without it', 'warning'); } // Test 4: API-only test (isolate backend from UI) log('Test 4: API-only Backend Test', 'info'); const apiTestResult = await page.evaluate(async (key, value, token) => { const headers = { 'Content-Type': 'application/json', }; if (token) { headers['X-CSRFToken'] = token; headers['X-CSRF-Token'] = token; } // First check if setting exists const checkResponse = await fetch(`/settings/api/${key}`, { method: 'GET', credentials: 'same-origin' }); // Update the setting const updateResponse = await fetch(`/settings/api/${key}`, { method: 'PUT', headers, credentials: 'same-origin', body: JSON.stringify({ value }) }); return { checkStatus: checkResponse.status, updateStatus: updateResponse.status, updateOk: updateResponse.ok, updateData: await updateResponse.json() }; }, API_KEY_SETTING, TEST_API_KEY, csrfToken); log(`API test result: ${JSON.stringify(apiTestResult)}`, 'info'); if (apiTestResult.updateOk) { log('API key updated successfully via API', 'success'); } else { log('API update failed, trying UI approach', 'warning'); } // Test 5: UI-based update (fallback) log('Test 5: UI-based API Key Update', 'info'); // Look for LLM Parameters tab const llmTab = await page.$('a[href="#llm-parameters"], a[href*="llm"], [data-tab="llm"]'); if (llmTab) { await llmTab.click(); await new Promise(resolve => setTimeout(resolve, 1000)); } // Find the API key input const apiKeyInput = await page.$(`input[name="${API_KEY_SETTING}"]`); if (apiKeyInput) { await apiKeyInput.click({ clickCount: 3 }); await apiKeyInput.type(TEST_API_KEY); log('API key entered in UI', 'success'); } else { log('API key input not found', 'error'); } // Find and click save button let saveButton = await page.$('button[type="submit"]:not([type="reset"])'); if (!saveButton) { // Alternative method to find save button const buttons = await page.$$eval('button', btns => btns.map((btn, index) => ({ index, text: btn.textContent.trim(), type: btn.type })) ); const saveButtonInfo = buttons.find(b => b.text.toLowerCase().includes('save') && b.type !== 'reset' ); if (saveButtonInfo) { const allButtons = await page.$$('button'); saveButton = allButtons[saveButtonInfo.index]; } } if (saveButton) { await saveButton.click(); log('Save button clicked', 'success'); await new Promise(resolve => setTimeout(resolve, 2000)); } // Test 6: Verify persistence log('Test 6: Persistence Verification', 'info'); // Reload page await page.reload({ waitUntil: 'domcontentloaded' }); // Check if value persisted via API const persistenceCheck = await page.evaluate(async (key) => { const response = await fetch(`/settings/api/${key}`, { method: 'GET', credentials: 'same-origin' }); if (response.ok) { const data = await response.json(); return data.value; } return null; }, API_KEY_SETTING); if (persistenceCheck === TEST_API_KEY) { log('āœ“ API key persisted successfully!', 'success'); } else { log(`āœ— API key not persisted. Expected: ${TEST_API_KEY}, Got: ${persistenceCheck}`, 'error'); } // Test 7: Check dialog messages if (dialogMessages.length > 0) { log(`\nāš ļø ${dialogMessages.length} dialog(s) detected during test:`, 'warning'); dialogMessages.forEach((dialog, index) => { log(` Dialog ${index + 1}: ${dialog.type} - "${dialog.message}"`, 'warning'); }); log('This might be the user selection bug!', 'error'); } else { log('No dialogs detected during test', 'success'); } // Test 8: Research functionality with API key log('Test 8: Research Functionality Test', 'info'); await page.goto(BASE_URL, { waitUntil: 'domcontentloaded' }); const searchInput = await page.$('input[name="query"]'); if (searchInput) { await searchInput.type('Test research query'); const submitButton = await page.$('button[type="submit"]'); if (submitButton) { await submitButton.click(); await new Promise(resolve => setTimeout(resolve, 3000)); // Check if research started successfully const currentUrl = page.url(); if (currentUrl.includes('/research/')) { log('Research started successfully with API key', 'success'); } else { log('Research may have failed', 'warning'); } } } log('\n=== Test Summary ===', 'info'); log(`Total dialogs detected: ${dialogMessages.length}`, dialogMessages.length > 0 ? 'error' : 'success'); log(`API key persisted: ${persistenceCheck === TEST_API_KEY ? 'Yes' : 'No'}`, persistenceCheck === TEST_API_KEY ? 'success' : 'error'); } catch (error) { log(`Test failed with error: ${error.message}`, 'error'); console.error(error); // Take error screenshot if (browser) { const page = (await browser.pages())[0]; if (page) { await page.screenshot({ path: 'error_screenshot.png', fullPage: true }); } } } finally { if (browser && process.env.DEBUG !== 'true') { await browser.close(); } else if (process.env.DEBUG === 'true') { log('Browser kept open for debugging. Press Ctrl+C to exit.', 'info'); await new Promise(() => {}); // Keep process alive } } })();