import http from 'k6/http'; import { check, sleep } from 'k6'; import { Counter } from 'k6/metrics'; const BASE_URL = __ENV.BASE_URL || 'http://app:3000'; const seed = JSON.parse(open('./seed-data.json')); const writeFailures = new Counter('write_failures'); export const options = { stages: [ { duration: '20s', target: 50 }, { duration: '20s', target: 100 }, { duration: '30s', target: 150 }, { duration: '30s', target: 200 }, { duration: '30s', target: 250 }, { duration: '30s', target: 300 }, { duration: '30s', target: 350 }, { duration: '30s', target: 400 }, { duration: '30s', target: 450 }, { duration: '30s', target: 500 }, { duration: '30s', target: 550 }, { duration: '30s', target: 600 }, { duration: '20s', target: 0 }, ], thresholds: { http_req_failed: ['rate<0.02'], http_req_duration: ['p(95)<500'], write_failures: ['count==0'], }, }; export default function () { const unique = `${__VU}-${__ITER}-${Date.now()}`; const startTime = new Date(Date.now() + 60 * 60 * 1000); const endTime = new Date(startTime.getTime() + 30 * 60 * 1000); // 1. Insert a new user const userResponse = http.post( `${BASE_URL}/users/`, JSON.stringify({ fullName: `Load Test ${unique}`, email: `load-${unique}@example.invalid`, phone: `9${String(__VU).padStart(1, '0')}${String(__ITER).padStart(8, '0')}`.slice(0, 10), }), { headers: { 'Content-Type': 'application/json' }, tags: { name: 'POST /users' } } ); const userOk = check(userResponse, { 'user inserted (201)': (r) => r.status === 201, }); if (!userOk) { writeFailures.add(1); return; } const userId = userResponse.json('result._id'); // 2. Insert a session for the existing seeded teacher const sessionResponse = http.post( `${BASE_URL}/sessions/`, JSON.stringify({ teacherId: seed.teacher.id, startTime: startTime.toISOString(), endTime: endTime.toISOString(), }), { headers: { 'Content-Type': 'application/json' }, tags: { name: 'POST /sessions' } } ); const sessionOk = check(sessionResponse, { 'session inserted (201)': (r) => r.status === 201, }); if (!sessionOk) { writeFailures.add(1); return; } const sessionId = sessionResponse.json('result._id'); // 3. Book the newly created session const bookingResponse = http.post( `${BASE_URL}/sessions/${sessionId}/book`, JSON.stringify({ userId }), { headers: { 'Content-Type': 'application/json' }, tags: { name: 'POST /sessions/:id/book' } } ); const bookingOk = check(bookingResponse, { 'session booked (200)': (r) => r.status === 200, }); if (!bookingOk) { writeFailures.add(1); return; } // 4. Complete the booked session const completeResponse = http.patch( `${BASE_URL}/sessions/${sessionId}/complete`, null, { tags: { name: 'PATCH /sessions/:id/complete' } } ); const completeOk = check(completeResponse, { 'session completed (200)': (r) => r.status === 200, }); if (!completeOk) { writeFailures.add(1); return; } // 5. Read user session history const historyResponse = http.get( `${BASE_URL}/sessions/${userId}/sessions`, { tags: { name: 'GET /sessions/:userId/sessions' } } ); check(historyResponse, { 'user history returned (200)': (r) => r.status === 200, }); sleep(1); } export function handleSummary(data) { return { '/results/endpoint-summary.json': JSON.stringify(data, null, 2), }; }