342 lines
9.8 KiB
HTML
342 lines
9.8 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<title>Concurrent User Load Test - HOP Worship App</title>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
|
|
<style>
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
|
sans-serif;
|
|
max-width: 1200px;
|
|
margin: 0 auto;
|
|
padding: 20px;
|
|
background: #f5f5f5;
|
|
}
|
|
.container {
|
|
background: white;
|
|
padding: 30px;
|
|
border-radius: 12px;
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
|
}
|
|
h1 {
|
|
color: #667eea;
|
|
margin-bottom: 10px;
|
|
}
|
|
.subtitle {
|
|
color: #666;
|
|
margin-bottom: 30px;
|
|
}
|
|
.test-controls {
|
|
margin-bottom: 30px;
|
|
padding: 20px;
|
|
background: #f8f9fa;
|
|
border-radius: 8px;
|
|
}
|
|
label {
|
|
display: block;
|
|
margin-bottom: 8px;
|
|
font-weight: 600;
|
|
color: #333;
|
|
}
|
|
input[type="number"],
|
|
input[type="text"] {
|
|
width: 100%;
|
|
padding: 10px;
|
|
border: 2px solid #ddd;
|
|
border-radius: 6px;
|
|
font-size: 14px;
|
|
margin-bottom: 15px;
|
|
}
|
|
button {
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
color: white;
|
|
border: none;
|
|
padding: 12px 24px;
|
|
border-radius: 6px;
|
|
cursor: pointer;
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
margin-right: 10px;
|
|
}
|
|
button:hover {
|
|
opacity: 0.9;
|
|
}
|
|
button:disabled {
|
|
background: #ccc;
|
|
cursor: not-allowed;
|
|
}
|
|
.stop-btn {
|
|
background: linear-gradient(135deg, #eb3349 0%, #f45c43 100%);
|
|
}
|
|
.results {
|
|
margin-top: 20px;
|
|
}
|
|
.stat-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
gap: 15px;
|
|
margin-bottom: 20px;
|
|
}
|
|
.stat-card {
|
|
padding: 20px;
|
|
border-radius: 8px;
|
|
text-align: center;
|
|
}
|
|
.stat-card.success {
|
|
background: #d4edda;
|
|
border: 2px solid #28a745;
|
|
}
|
|
.stat-card.error {
|
|
background: #f8d7da;
|
|
border: 2px solid #dc3545;
|
|
}
|
|
.stat-card.info {
|
|
background: #d1ecf1;
|
|
border: 2px solid #17a2b8;
|
|
}
|
|
.stat-value {
|
|
font-size: 32px;
|
|
font-weight: bold;
|
|
margin-bottom: 5px;
|
|
}
|
|
.stat-label {
|
|
font-size: 14px;
|
|
color: #666;
|
|
}
|
|
.log {
|
|
background: #1e1e1e;
|
|
color: #00ff00;
|
|
padding: 15px;
|
|
border-radius: 6px;
|
|
max-height: 400px;
|
|
overflow-y: auto;
|
|
font-family: "Courier New", monospace;
|
|
font-size: 12px;
|
|
line-height: 1.6;
|
|
}
|
|
.log-entry {
|
|
margin-bottom: 5px;
|
|
}
|
|
.log-success {
|
|
color: #00ff00;
|
|
}
|
|
.log-error {
|
|
color: #ff4444;
|
|
}
|
|
.log-info {
|
|
color: #44ccff;
|
|
}
|
|
.log-warning {
|
|
color: #ffaa00;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<h1>🎵 HOP Worship App - Concurrent User Load Test</h1>
|
|
<p class="subtitle">
|
|
Test multiple simultaneous logins to verify system stability
|
|
</p>
|
|
|
|
<div class="test-controls">
|
|
<label>Number of Concurrent Users:</label>
|
|
<input type="number" id="userCount" value="10" min="1" max="100" />
|
|
|
|
<label>Site URL:</label>
|
|
<input
|
|
type="text"
|
|
id="siteUrl"
|
|
value="http://192.168.10.130:3000"
|
|
placeholder="http://localhost:3000"
|
|
/>
|
|
|
|
<label>Username:</label>
|
|
<input type="text" id="username" value="hop" />
|
|
|
|
<label>Password:</label>
|
|
<input type="text" id="password" value="hop@2026ilovejesus" />
|
|
|
|
<button onclick="startTest()" id="startBtn">▶️ Start Load Test</button>
|
|
<button onclick="stopTest()" id="stopBtn" class="stop-btn" disabled>
|
|
⏹️ Stop Test
|
|
</button>
|
|
<button onclick="clearLog()">🗑️ Clear Log</button>
|
|
</div>
|
|
|
|
<div class="results">
|
|
<div class="stat-grid">
|
|
<div class="stat-card success">
|
|
<div class="stat-value" id="successCount">0</div>
|
|
<div class="stat-label">Successful Logins</div>
|
|
</div>
|
|
<div class="stat-card error">
|
|
<div class="stat-value" id="errorCount">0</div>
|
|
<div class="stat-label">Failed Attempts</div>
|
|
</div>
|
|
<div class="stat-card info">
|
|
<div class="stat-value" id="activeCount">0</div>
|
|
<div class="stat-label">Active Sessions</div>
|
|
</div>
|
|
<div class="stat-card info">
|
|
<div class="stat-value" id="avgTime">0ms</div>
|
|
<div class="stat-label">Avg Response Time</div>
|
|
</div>
|
|
</div>
|
|
|
|
<h3>Test Log:</h3>
|
|
<div class="log" id="logContainer"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
let stats = {
|
|
success: 0,
|
|
error: 0,
|
|
active: 0,
|
|
times: [],
|
|
};
|
|
let testRunning = false;
|
|
let windows = [];
|
|
|
|
function log(message, type = "info") {
|
|
const container = document.getElementById("logContainer");
|
|
const timestamp = new Date().toLocaleTimeString();
|
|
const entry = document.createElement("div");
|
|
entry.className = `log-entry log-${type}`;
|
|
entry.textContent = `[${timestamp}] ${message}`;
|
|
container.appendChild(entry);
|
|
container.scrollTop = container.scrollHeight;
|
|
}
|
|
|
|
function updateStats() {
|
|
document.getElementById("successCount").textContent = stats.success;
|
|
document.getElementById("errorCount").textContent = stats.error;
|
|
document.getElementById("activeCount").textContent = stats.active;
|
|
|
|
if (stats.times.length > 0) {
|
|
const avg =
|
|
stats.times.reduce((a, b) => a + b, 0) / stats.times.length;
|
|
document.getElementById("avgTime").textContent =
|
|
Math.round(avg) + "ms";
|
|
}
|
|
}
|
|
|
|
function hashPassword(pwd) {
|
|
return CryptoJS.SHA256(pwd).toString();
|
|
}
|
|
|
|
function simulateLogin(userId, url, username, password) {
|
|
return new Promise((resolve) => {
|
|
const startTime = Date.now();
|
|
|
|
try {
|
|
// Simulate authentication logic (client-side)
|
|
const passwordHash = hashPassword(password);
|
|
const MASTER_PASSWORD_HASH =
|
|
"5cdf907c69ae7a7f0c2e18a67e9b70a4c4fc35f9582637354c1bc45edf092a79";
|
|
|
|
setTimeout(() => {
|
|
const elapsed = Date.now() - startTime;
|
|
|
|
if (username === "hop" && passwordHash === MASTER_PASSWORD_HASH) {
|
|
stats.success++;
|
|
stats.active++;
|
|
stats.times.push(elapsed);
|
|
log(
|
|
`User ${userId}: Login successful (${elapsed}ms)`,
|
|
"success"
|
|
);
|
|
resolve({ success: true, time: elapsed });
|
|
} else {
|
|
stats.error++;
|
|
log(`User ${userId}: Invalid credentials`, "error");
|
|
resolve({ success: false, time: elapsed });
|
|
}
|
|
updateStats();
|
|
}, Math.random() * 100); // Simulate network latency
|
|
} catch (err) {
|
|
stats.error++;
|
|
log(`User ${userId}: Error - ${err.message}`, "error");
|
|
updateStats();
|
|
resolve({ success: false, error: err.message });
|
|
}
|
|
});
|
|
}
|
|
|
|
async function startTest() {
|
|
if (testRunning) return;
|
|
|
|
testRunning = true;
|
|
document.getElementById("startBtn").disabled = true;
|
|
document.getElementById("stopBtn").disabled = false;
|
|
|
|
// Reset stats
|
|
stats = { success: 0, error: 0, active: 0, times: [] };
|
|
updateStats();
|
|
|
|
const userCount = parseInt(document.getElementById("userCount").value);
|
|
const url = document.getElementById("siteUrl").value;
|
|
const username = document.getElementById("username").value;
|
|
const password = document.getElementById("password").value;
|
|
|
|
log(
|
|
`Starting load test with ${userCount} concurrent users...`,
|
|
"warning"
|
|
);
|
|
log(`Target: ${url}`, "info");
|
|
|
|
// Simulate concurrent logins
|
|
const promises = [];
|
|
for (let i = 1; i <= userCount; i++) {
|
|
promises.push(simulateLogin(i, url, username, password));
|
|
}
|
|
|
|
await Promise.all(promises);
|
|
|
|
log(`Load test completed!`, "success");
|
|
log(
|
|
`Success: ${stats.success}/${userCount}, Errors: ${stats.error}`,
|
|
"info"
|
|
);
|
|
|
|
if (stats.times.length > 0) {
|
|
const avg =
|
|
stats.times.reduce((a, b) => a + b, 0) / stats.times.length;
|
|
const min = Math.min(...stats.times);
|
|
const max = Math.max(...stats.times);
|
|
log(
|
|
`Response times - Min: ${min}ms, Max: ${max}ms, Avg: ${Math.round(
|
|
avg
|
|
)}ms`,
|
|
"info"
|
|
);
|
|
}
|
|
|
|
document.getElementById("startBtn").disabled = false;
|
|
document.getElementById("stopBtn").disabled = true;
|
|
testRunning = false;
|
|
}
|
|
|
|
function stopTest() {
|
|
testRunning = false;
|
|
document.getElementById("startBtn").disabled = false;
|
|
document.getElementById("stopBtn").disabled = true;
|
|
log("Test stopped by user", "warning");
|
|
}
|
|
|
|
function clearLog() {
|
|
document.getElementById("logContainer").innerHTML = "";
|
|
log("Log cleared", "info");
|
|
}
|
|
|
|
// Initial log message
|
|
log(
|
|
'Load testing tool ready. Configure settings and click "Start Load Test"',
|
|
"info"
|
|
);
|
|
</script>
|
|
</body>
|
|
</html>
|