import http from 'k6/http'; import {check, sleep} from 'k6'; import exec from 'k6/execution'; import {getHeaders, login, parseIdToString} from "./common.js"; import {randomIntBetween} from 'https://jslib.k6.io/k6-utils/1.2.0/index.js'; const BASE_URL = __ENV.BASE_URL || 'http://localhost:8080'; const TOTAL_ITERATIONS = 85212; export const options = { scenarios: { schedule_creation: { executor: 'shared-iterations', vus: 263, iterations: TOTAL_ITERATIONS, maxDuration: '10m', }, }, thresholds: { 'http_req_duration': ['p(95)<3000'], 'http_req_failed': ['rate<0.1'], }, }; export function setup() { console.log('=== Setup: 테스트 데이터 및 작업 목록 준비 중 ==='); const themesRes = http.get(`${BASE_URL}/tests/themes`); const themes = parseIdToString(themesRes).results const accountsRes = http.get(`${BASE_URL}/tests/admin/accounts`); const accounts = JSON.parse(accountsRes.body).results; const dates = generateDates(7); console.log(`총 매장 수: ${accounts.length}`); console.log(`총 테마 수: ${themes.length}`); console.log(`생성 기간: ${dates.length}일`); const tasks = []; for (const account of accounts) { const loginResult = login(account.account, account.password, 'ADMIN'); if (loginResult === null) { console.error(`[Setup] 로그인 실패: ${account.account}`); continue; } const { storeId, accessToken } = loginResult; // 5 ~ ${themes.size} 인 random 숫자 생성 const selectedThemes = selectRandomThemes(themes, randomIntBetween(5, themes.length)); for (const theme of selectedThemes) { for (const date of dates) { for (const time of theme.times) { tasks.push({ storeId, accessToken, date, time: time.startFrom, themeId: theme.id, }); } } } } console.log(`총 생성할 스케줄 수(iterations): ${tasks.length}`); return { tasks }; } export default function(data) { // 👈 3. 현재 반복 횟수가 준비된 작업 수를 초과하는지 확인 const taskIndex = exec.scenario.iterationInTest; if (taskIndex >= data.tasks.length) { // 첫 번째로 이 조건에 도달한 VU가 테스트를 종료 if (taskIndex === data.tasks.length) { console.log('모든 스케쥴 생성 완료. 테스트 종료'); exec.test.abort(); } return; } const task = data.tasks[taskIndex]; if (!task) { console.log(`[VU ${__VU}] 알 수 없는 오류: task가 없습니다. (index: ${taskIndex})`); return; } createSchedule(task.storeId, task.accessToken, task); } function createSchedule(storeId, accessToken, schedule) { const payload = JSON.stringify({ date: schedule.date, time: schedule.time, themeId: schedule.themeId, }); const params = getHeaders(accessToken) const res = http.post(`${BASE_URL}/admin/stores/${storeId}/schedules`, payload, params); const success = check(res, {'일정 생성 성공': (r) => r.status === 200 || r.status === 201}); if (!success) { console.error(`일정 생성 실패 [${res.status}]: 매장=${storeId}, ${schedule.date} ${schedule.time} (테마: ${schedule.themeId}) | 응답: ${res.body}`); } sleep(5) return success; } function generateDates(days) { const dates = []; const today = new Date(); for (let i = 1; i < days; i++) { const date = new Date(today); date.setDate(today.getDate() + i); dates.push(date.toISOString().split('T')[0]); } return dates; } function selectRandomThemes(themes, count) { const shuffled = [...themes].sort(() => 0.5 - Math.random()); return shuffled.slice(0, Math.min(count, themes.length)); } export function teardown(data) { if (data.tasks) { console.log(`\n=== 테스트 완료: 총 ${data.tasks.length}개의 스케줄 생성을 시도했습니다. ===`); } else { console.log('\n=== 테스트 완료: setup 단계에서 오류가 발생하여 작업을 실행하지 못했습니다. ==='); } }