Fix: Implement Test Queue and Unique Profiles for Concurrency

This commit is contained in:
2025-12-28 00:41:38 +11:00
parent c475c2cee0
commit 7206412704

View File

@@ -2,13 +2,57 @@
const fs = require('fs');
const path = require('path');
// Simple Queue Implementation to prevent concurrency crashes
class TestQueue {
constructor() {
this.queue = [];
this.running = false;
}
add(task) {
return new Promise((resolve, reject) => {
this.queue.push({ task, resolve, reject });
this.process();
});
}
async process() {
if (this.running || this.queue.length === 0) return;
this.running = true;
const { task, resolve, reject } = this.queue.shift();
try {
const result = await task();
resolve(result);
} catch (error) {
reject(error);
} finally {
this.running = false;
this.process(); // Process next item
}
}
}
const runnerQueue = new TestQueue();
/**
* Run a performance test using Lighthouse
* Run a performance test using Lighthouse (Serialized)
* @param {string} url - The URL to test
* @param {object} options - Configuration options (mobile vs desktop, etc.)
* @returns {Promise<object>} - Test results
*/
async function runTest(url, options = {}) {
// Wrap the actual test logic in a queue task
return runnerQueue.add(async () => {
return _executeTest(url, options);
});
}
/**
* Internal function to execute the test (NOT exported directly)
*/
async function _executeTest(url, options) {
// Dynamically import dependencies (ESM)
const { default: lighthouse } = await import('lighthouse');
const chromeLauncher = await import('chrome-launcher');
@@ -25,10 +69,25 @@ async function runTest(url, options = {}) {
// Launch Chrome
console.log('Launching Chrome...');
const chromePath = process.platform === 'linux' ? '/usr/bin/chromium' : undefined;
// Use a unique user data dir to prevent profile locking issues
const userDataDir = path.join(os.tmpdir(), `lh-profile-${uuidv4()}`);
// Ensure tmp dir exists if likely to crash
if (!fs.existsSync(userDataDir)) {
fs.mkdirSync(userDataDir, { recursive: true });
}
const chrome = await chromeLauncher.launch({
chromeFlags: ['--headless', '--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage'],
chromeFlags: [
'--headless',
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-dev-shm-usage',
`--user-data-dir=${userDataDir}` // Unique profile for this run
],
chromePath: chromePath,
port: 0 // Force random port to avoid concurrency issues with default 9222
port: 0 // Force random port
});
// Lighthouse Config
@@ -87,6 +146,13 @@ async function runTest(url, options = {}) {
fs.writeFileSync(jsonPath, JSON.stringify(summary, null, 2));
await chrome.kill();
// Cleanup User Data Dir
try {
fs.rmSync(userDataDir, { recursive: true, force: true });
} catch (e) {
console.error('Failed to cleanup temp profile:', e);
}
// Insert into Database
// We expect user_uuid and user_ip to be passed in options, or handle gracefully if not
@@ -120,6 +186,8 @@ async function runTest(url, options = {}) {
} catch (error) {
if (chrome) await chrome.kill();
// Try cleanup again in error case
try { fs.rmSync(userDataDir, { recursive: true, force: true }); } catch (e) {}
throw error;
}
}
@@ -160,6 +228,9 @@ async function getHistory(userUuid, userIp) {
}
}
// Need os for tmpdir
const os = require('os');
module.exports = {
runTest,
getHistory