// Backend/tests/k6/login-load-test.js // Load test สำหรับ Login API โดยใช้ test credentials ที่สร้างไว้ import http from 'k6/http'; import { check, sleep } from 'k6'; import { Rate, Trend, Counter } from 'k6/metrics'; import { SharedArray } from 'k6/data'; // Load test credentials const students = new SharedArray('students', function () { return JSON.parse(open('./test-credentials.json')).students; }); // Custom metrics const errorRate = new Rate('errors'); const loginDuration = new Trend('login_duration'); const successfulLogins = new Counter('successful_logins'); const failedLogins = new Counter('failed_logins'); // Test configuration export const options = { // Ramp-up pattern stages: [ { duration: '10s', target: 10 }, // Ramp up to 10 users { duration: '30s', target: 10 }, // Stay at 10 users { duration: '10s', target: 25 }, // Ramp up to 25 users { duration: '30s', target: 25 }, // Stay at 25 users { duration: '10s', target: 50 }, // Ramp up to 50 users { duration: '30s', target: 50 }, // Stay at 50 users { duration: '10s', target: 0 }, // Ramp down ], thresholds: { http_req_duration: ['p(95)<2000'], // 95% of requests < 2s errors: ['rate<0.1'], // Error rate < 10% login_duration: ['p(95)<2000'], // 95% pof logins < 2s }, }; const BASE_URL = __ENV.BASE_URL || 'http://192.168.1.137:4000'; export default function () { // เลือก student แบบ random จาก credentials const student = students[Math.floor(Math.random() * students.length)]; const payload = JSON.stringify({ email: student.email, password: student.password, }); const params = { headers: { 'Content-Type': 'application/json', }, }; // Test Login const startTime = new Date(); const res = http.post(`${BASE_URL}/api/auth/login`, payload, params); const duration = new Date() - startTime; // Record metrics loginDuration.add(duration); const isSuccess = res.status === 200; errorRate.add(!isSuccess); if (isSuccess) { successfulLogins.add(1); } else { failedLogins.add(1); console.log(`Login failed for ${student.email}: ${res.status} - ${res.body}`); } // Assertions check(res, { 'status is 200': (r) => r.status === 200, 'has access token': (r) => { try { const body = JSON.parse(r.body); return body.data && body.data.accessToken; } catch (e) { return false; } }, 'has refresh token': (r) => { try { const body = JSON.parse(r.body); return body.data && body.data.refreshToken; } catch (e) { return false; } }, 'response time < 2s': (r) => r.timings.duration < 2000, }); // Think time between requests (simulate real user behavior) sleep(Math.random() * 2 + 1); // 1-3 seconds } // Summary handler export function handleSummary(data) { return { 'stdout': textSummary(data, { indent: ' ', enableColors: true }), 'login-load-test-summary.json': JSON.stringify(data, null, 2), }; } function textSummary(data, options) { const metrics = data.metrics; let summary = '\n========== LOGIN LOAD TEST SUMMARY ==========\n\n'; // Request stats if (metrics.http_reqs) { summary += `Total Requests: ${metrics.http_reqs.values.count}\n`; } if (metrics.successful_logins) { summary += `Successful Logins: ${metrics.successful_logins.values.count}\n`; } if (metrics.failed_logins) { summary += `Failed Logins: ${metrics.failed_logins.values.count}\n`; } // Duration stats if (metrics.http_req_duration) { summary += `\nResponse Time:\n`; summary += ` - Avg: ${metrics.http_req_duration.values.avg.toFixed(2)}ms\n`; summary += ` - Min: ${metrics.http_req_duration.values.min.toFixed(2)}ms\n`; summary += ` - Max: ${metrics.http_req_duration.values.max.toFixed(2)}ms\n`; summary += ` - p(95): ${metrics.http_req_duration.values['p(95)'].toFixed(2)}ms\n`; } // Error rate if (metrics.errors) { summary += `\nError Rate: ${(metrics.errors.values.rate * 100).toFixed(2)}%\n`; } summary += '\n==============================================\n'; return summary; }