Files
wehub-resource-sync 7a0da7932b
OSV-Scanner (Scheduled) / scan-scheduled (push) Failing after 0s
Create Release / test-gate (push) Has been cancelled
Create Release / release-gate (push) Has been cancelled
Create Release / ci-gate (push) Has been cancelled
Create Release / version-check (push) Has been cancelled
Create Release / e2e-test-gate (push) Has been cancelled
Create Release / responsive-test-gate (push) Has been cancelled
Create Release / compat-test-gate (push) Has been cancelled
Create Release / compose-integration-gate (push) Has been cancelled
Create Release / vulture-gate (push) Has been cancelled
Create Release / build (push) Has been cancelled
Create Release / provenance (push) Has been cancelled
Create Release / prerelease-docker (push) Has been cancelled
Create Release / publish-docker (push) Has been cancelled
Create Release / create-release (push) Has been cancelled
Create Release / cleanup-changelog (push) Has been cancelled
Create Release / trigger-pypi (push) Has been cancelled
Create Release / monitor-pypi (push) Has been cancelled
Create Release / Clean up orphan prerelease tags and signatures (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Has been cancelled
Docker Tests (Consolidated) / Accessibility Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Unit Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Example Tests (push) Has been cancelled
Docker Tests (Consolidated) / Production Image Smoke Test (push) Has been cancelled
Docker Tests (Consolidated) / Infrastructure Tests (push) Has been cancelled
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Has been cancelled
Backwards Compatibility / Verify Encryption Constants (push) Has been cancelled
Backwards Compatibility / PyPI Version Compatibility (push) Has been cancelled
Backwards Compatibility / Database Migration Tests (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Docker Tests (Consolidated) / detect-changes (push) Has been cancelled
Docker Tests (Consolidated) / Build Test Image (push) Has been cancelled
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:08:55 +08:00

341 lines
12 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 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
}
}
})();