From 110eb7a3f86645feeb482283b6f42d0f24c82ec7 Mon Sep 17 00:00:00 2001 From: DeNNiiInc Date: Mon, 29 Dec 2025 10:45:05 +1100 Subject: [PATCH] Remove multi-run feature completely - simplify to single test execution only --- lib/multi-run.js | 268 --------------------------- migrations/001_multi_run_support.sql | 47 ----- migrations/002_add_filmstrip.sql | 6 - migrations/migrate_002.js | 27 --- migrations/run-migration.sh | 56 ------ server.js | 48 +---- 6 files changed, 4 insertions(+), 448 deletions(-) delete mode 100644 lib/multi-run.js delete mode 100644 migrations/001_multi_run_support.sql delete mode 100644 migrations/002_add_filmstrip.sql delete mode 100644 migrations/migrate_002.js delete mode 100644 migrations/run-migration.sh diff --git a/lib/multi-run.js b/lib/multi-run.js deleted file mode 100644 index 8b2c096..0000000 --- a/lib/multi-run.js +++ /dev/null @@ -1,268 +0,0 @@ -/** - * Multi-Run Test Execution Module - * Handles running multiple performance tests and calculating statistics - */ - -const runner = require('./runner'); -const { Pool } = require('pg'); -const dbConfig = require('./db-config'); - -const pool = new Pool(dbConfig); - -/** - * Statistical helper functions - */ -function median(values) { - if (values.length === 0) return 0; - const sorted = [...values].sort((a, b) => a - b); - const mid = Math.floor(sorted.length / 2); - return sorted.length % 2 === 0 - ? (sorted[mid - 1] + sorted[mid]) / 2 - : sorted[mid]; -} - -function average(values) { - if (values.length === 0) return 0; - return values.reduce((sum, v) => sum + v, 0) / values.length; -} - -function stddev(values) { - if (values.length === 0) return 0; - const avg = average(values); - const squareDiffs = values.map(v => Math.pow(v - avg, 2)); - return Math.sqrt(average(squareDiffs)); -} - -function findClosestToMedian(values, medianValue) { - let closestIndex = 0; - let minDiff = Math.abs(values[0] - medianValue); - - values.forEach((v, i) => { - const diff = Math.abs(v - medianValue); - if (diff < minDiff) { - minDiff = diff; - closestIndex = i; - } - }); - - return closestIndex; -} - -/** - * Execute multiple test runs and calculate statistics - */ -async function executeMultipleRuns(suiteId, url, isMobile, runCount, userUuid, userIp) { - const results = []; - const testIds = []; - - console.log(`[Multi-Run] Starting ${runCount} runs for suite ${suiteId}`); - - for (let i = 1; i <= runCount; i++) { - try { - console.log(`[Multi-Run] Running test ${i}/${runCount}...`); - - // Generate unique test ID for this run - const testId = `${suiteId}-run${i}`; - testIds.push(testId); - - // Run individual test - const result = await runner.runTest(url, { - isMobile, - userUuid, - userIp, - suiteId, - runNumber: i, - testId - }); - - results.push(result); - - // Update progress - await updateSuiteProgress(suiteId, i); - - console.log(`[Multi-Run] Completed run ${i}/${runCount}`); - - } catch (error) { - console.error(`[Multi-Run] Run ${i} failed:`, error); - // Continue with other runs even if one fails - } - } - - if (results.length === 0) { - await markSuiteFailed(suiteId); - throw new Error('All test runs failed'); - } - - // Calculate statistics - console.log(`[Multi-Run] Calculating statistics for ${results.length} runs`); - await calculateStatistics(suiteId, results, testIds); - - // Mark suite as complete - await completeSuite(suiteId); - - console.log(`[Multi-Run] Suite ${suiteId} completed`); - - return { - suiteId, - completedRuns: results.length, - totalRuns: runCount - }; -} - -/** - * Calculate statistics for all runs and update suite - */ -async function calculateStatistics(suiteId, results, testIds) { - const metrics = { - performanceScore: [], - lcp: [], - cls: [], - tbt: [] - }; - - // Extract metrics from each result - results.forEach(r => { - const lr = r.lighthouseResult; - metrics.performanceScore.push(lr.categories.performance.score * 100); - metrics.lcp.push(lr.audits['largest-contentful-paint']?.numericValue || 0); - metrics.cls.push(lr.audits['cumulative-layout-shift']?.numericValue || 0); - metrics.tbt.push(lr.audits['total-blocking-time']?.numericValue || 0); - }); - - // Calculate statistics - const stats = { - median_performance_score: median(metrics.performanceScore), - avg_performance_score: average(metrics.performanceScore), - stddev_performance_score: stddev(metrics.performanceScore), - median_lcp: median(metrics.lcp), - avg_lcp: average(metrics.lcp), - stddev_lcp: stddev(metrics.lcp), - median_cls: median(metrics.cls), - avg_cls: average(metrics.cls), - stddev_cls: stddev(metrics.cls), - median_tbt: median(metrics.tbt), - avg_tbt: average(metrics.tbt), - stddev_tbt: stddev(metrics.tbt) - }; - - // Find median run (closest to median performance score) - const medianPerfScore = stats.median_performance_score; - const closestRunIndex = findClosestToMedian(metrics.performanceScore, medianPerfScore); - const medianTestId = testIds[closestRunIndex]; - - console.log(`[Multi-Run] Median run is #${closestRunIndex + 1} (${medianTestId})`); - - // Mark median run - await markMedianRun(medianTestId); - - // Update suite with statistics - await updateSuiteStats(suiteId, stats); -} - -/** - * Database operations - */ -async function createSuite(suiteId, userUuid, url, deviceType, runCount) { - const query = ` - INSERT INTO test_suites (suite_id, user_uuid, url, device_type, run_count, status) - VALUES ($1, $2, $3, $4, $5, 'running') - `; - await pool.query(query, [suiteId, userUuid, url, deviceType, runCount]); -} - -async function updateSuiteProgress(suiteId, completedRuns) { - const query = ` - UPDATE test_suites - SET completed_runs = $1 - WHERE suite_id = $2 - `; - await pool.query(query, [completedRuns, suiteId]); -} - -async function updateSuiteStats(suiteId, stats) { - const query = ` - UPDATE test_suites SET - median_performance_score = $1, - avg_performance_score = $2, - stddev_performance_score = $3, - median_lcp = $4, - avg_lcp = $5, - stddev_lcp = $6, - median_cls = $7, - avg_cls = $8, - stddev_cls = $9, - median_tbt = $10, - avg_tbt = $11, - stddev_tbt = $12 - WHERE suite_id = $13 - `; - await pool.query(query, [ - stats.median_performance_score, - stats.avg_performance_score, - stats.stddev_performance_score, - stats.median_lcp, - stats.avg_lcp, - stats.stddev_lcp, - stats.median_cls, - stats.avg_cls, - stats.stddev_cls, - stats.median_tbt, - stats.avg_tbt, - stats.stddev_tbt, - suiteId - ]); -} - -async function completeSuite(suiteId) { - const query = ` - UPDATE test_suites - SET status = 'completed', completed_at = NOW() - WHERE suite_id = $1 - `; - await pool.query(query, [suiteId]); -} - -async function markSuiteFailed(suiteId) { - const query = ` - UPDATE test_suites - SET status = 'failed' - WHERE suite_id = $1 - `; - await pool.query(query, [suiteId]); -} - -async function markMedianRun(testId) { - const query = ` - UPDATE test_results - SET is_median = TRUE - WHERE test_id = $1 - `; - await pool.query(query, [testId]); -} - -async function getSuiteStatus(suiteId) { - const query = ` - SELECT - s.*, - json_agg( - json_build_object( - 'test_id', r.test_id, - 'run_number', r.run_number, - 'is_median', r.is_median, - 'created_at', r.created_at - ) ORDER BY r.run_number - ) FILTER (WHERE r.test_id IS NOT NULL) as runs - FROM test_suites s - LEFT JOIN test_results r ON s.suite_id = r.suite_id - WHERE s.suite_id = $1 - GROUP BY s.id - `; - const result = await pool.query(query, [suiteId]); - return result.rows[0]; -} - -module.exports = { - executeMultipleRuns, - createSuite, - getSuiteStatus -}; diff --git a/migrations/001_multi_run_support.sql b/migrations/001_multi_run_support.sql deleted file mode 100644 index cd22262..0000000 --- a/migrations/001_multi_run_support.sql +++ /dev/null @@ -1,47 +0,0 @@ --- Multi-Run Statistics Database Migration --- Creates test_suites table and modifies test_results for multi-run support - --- Create test_suites table to group multiple test runs -CREATE TABLE IF NOT EXISTS test_suites ( - id SERIAL PRIMARY KEY, - suite_id TEXT UNIQUE NOT NULL, - user_uuid TEXT NOT NULL, - url TEXT NOT NULL, - device_type TEXT, - run_count INTEGER DEFAULT 1, - completed_runs INTEGER DEFAULT 0, - status TEXT DEFAULT 'running', -- running, completed, failed - created_at TIMESTAMP DEFAULT NOW(), - completed_at TIMESTAMP, - - -- Statistical results (calculated after all runs complete) - median_performance_score NUMERIC, - avg_performance_score NUMERIC, - stddev_performance_score NUMERIC, - median_lcp NUMERIC, - avg_lcp NUMERIC, - stddev_lcp NUMERIC, - median_cls NUMERIC, - avg_cls NUMERIC, - stddev_cls NUMERIC, - median_tbt NUMERIC, - avg_tbt NUMERIC, - stddev_tbt NUMERIC -); - --- Add columns to test_results for multi-run support -ALTER TABLE test_results -ADD COLUMN IF NOT EXISTS suite_id TEXT, -ADD COLUMN IF NOT EXISTS run_number INTEGER DEFAULT 1, -ADD COLUMN IF NOT EXISTS is_median BOOLEAN DEFAULT FALSE; - --- Create index for faster lookups -CREATE INDEX IF NOT EXISTS idx_suite_id ON test_results(suite_id); -CREATE INDEX IF NOT EXISTS idx_test_suites_user ON test_suites(user_uuid); -CREATE INDEX IF NOT EXISTS idx_test_suites_status ON test_suites(status); - --- Comment -COMMENT ON TABLE test_suites IS 'Groups multiple test runs for statistical analysis'; -COMMENT ON COLUMN test_results.suite_id IS 'Links individual run to parent test suite'; -COMMENT ON COLUMN test_results.run_number IS 'Run number within the suite (1-10)'; -COMMENT ON COLUMN test_results.is_median IS 'TRUE if this run represents the median performance'; diff --git a/migrations/002_add_filmstrip.sql b/migrations/002_add_filmstrip.sql deleted file mode 100644 index 0e5c678..0000000 --- a/migrations/002_add_filmstrip.sql +++ /dev/null @@ -1,6 +0,0 @@ --- Add filmstrip column to test_results table -ALTER TABLE test_results -ADD COLUMN filmstrip JSONB DEFAULT '[]'::jsonb; - --- Comment on column -COMMENT ON COLUMN test_results.filmstrip IS 'Array of filmstrip screenshots/thumbnails from Lighthouse'; diff --git a/migrations/migrate_002.js b/migrations/migrate_002.js deleted file mode 100644 index c29e5d3..0000000 --- a/migrations/migrate_002.js +++ /dev/null @@ -1,27 +0,0 @@ -const db = require('../lib/db'); -const fs = require('fs'); -const path = require('path'); - -const migrationFile = path.join(__dirname, '002_add_filmstrip.sql'); - -async function runMigration() { - try { - console.log('Reading migration file:', migrationFile); - const sql = fs.readFileSync(migrationFile, 'utf8'); - - console.log('Applying migration...'); - await db.pool.query(sql); - - console.log('✅ Migration 002 applied successfully!'); - process.exit(0); - } catch (err) { - if (err.code === '42701') { // duplicate_column - console.log('⚠️ Column already exists. Skipping.'); - process.exit(0); - } - console.error('❌ Migration failed:', err); - process.exit(1); - } -} - -runMigration(); diff --git a/migrations/run-migration.sh b/migrations/run-migration.sh deleted file mode 100644 index 2a9a576..0000000 --- a/migrations/run-migration.sh +++ /dev/null @@ -1,56 +0,0 @@ -#!/bin/bash -# Database Migration Script for Multi-Run Support -# Run this on the production server to apply schema changes - -set -e # Exit on error - -echo "=== Multi-Run Statistics Migration ===" -echo "Starting database migration..." - -# Database connection details -DB_HOST="202.171.184.108" -DB_USER="postgres" -DB_NAME="WebPerformance" -DB_PORT="5432" - -# Get script directory -SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -MIGRATION_FILE="$SCRIPT_DIR/001_multi_run_support.sql" - -# Check if migration file exists -if [ ! -f "$MIGRATION_FILE" ]; then - echo "Error: Migration file not found: $MIGRATION_FILE" - exit 1 -fi - -echo "Migration file: $MIGRATION_FILE" -echo "Target database: $DB_NAME on $DB_HOST" -echo "" -read -p "Continue with migration? (yes/no): " confirm - -if [ "$confirm" != "yes" ]; then - echo "Migration cancelled" - exit 0 -fi - -# Run migration -echo "Applying migration..." -PGPASSWORD='X@gon2005!#$' psql -h "$DB_HOST" -U "$DB_USER" -d "$DB_NAME" -p "$DB_PORT" -f "$MIGRATION_FILE" - -if [ $? -eq 0 ]; then - echo "" - echo "✅ Migration completed successfully!" - echo "" - echo "New tables/columns created:" - echo " - test_suites (new table)" - echo " - test_results.suite_id (new column)" - echo " - test_results.run_number (new column)" - echo " - test_results.is_median (new column)" - echo "" - echo "You can now deploy the application code." -else - echo "" - echo "❌ Migration failed!" - echo "Please check the error messages above." - exit 1 -fi diff --git a/server.js b/server.js index b42dd6b..6f74587 100644 --- a/server.js +++ b/server.js @@ -35,63 +35,23 @@ app.get("/api/git-info", (req, res) => { }); }); -// API Endpoint: Run Test (supports multi-run) +// API Endpoint: Run Test app.post("/api/run-test", async (req, res) => { - const { url, isMobile, runs = 1, captureFilmstrip = false } = req.body; + const { url, isMobile, captureFilmstrip = false } = req.body; const userUuid = req.headers['x-user-uuid']; const userIp = req.headers['x-forwarded-for'] || req.socket.remoteAddress; if (!url) return res.status(400).json({ error: "URL is required" }); - - // Validate run count - if (runs < 1 || runs > 10) { - return res.status(400).json({ error: "Runs must be between 1 and 10" }); - } try { - // Single run (original behavior) - if (runs === 1) { - const result = await runner.runTest(url, { isMobile, userUuid, userIp, captureFilmstrip }); - return res.json(result); - } - - // Multi-run - const multiRun = require('./lib/multi-run'); - const suiteId = runner.generateTestId(); - - // Create suite record - await multiRun.createSuite(suiteId, userUuid, url, isMobile ? 'mobile' : 'desktop', runs, captureFilmstrip); - - // Return suite ID immediately - res.json({ suiteId, runs, status: 'running' }); - - // Execute runs asynchronously - multiRun.executeMultipleRuns(suiteId, url, isMobile, runs, userUuid, userIp, captureFilmstrip) - .catch(error => console.error('Multi-run execution failed:', error)); - + const result = await runner.runTest(url, { isMobile, userUuid, userIp, captureFilmstrip }); + return res.json(result); } catch (error) { console.error("Test failed:", error); res.status(500).json({ error: "Test failed", details: error.message }); } }); -// API Endpoint: Suite Status (for multi-run progress tracking) -app.get("/api/suite-status/:suiteId", async (req, res) => { - try { - const multiRun = require('./lib/multi-run'); - const suite = await multiRun.getSuiteStatus(req.params.suiteId); - - if (!suite) { - return res.status(404).json({ error: "Suite not found" }); - } - - res.json(suite); - } catch (error) { - console.error("Suite status error:", error); - res.status(500).json({ error: "Failed to get suite status" }); - } -}); - // API Endpoint: History app.get("/api/history", async (req, res) => { const userUuid = req.headers['x-user-uuid'];