177 lines
No EOL
5.4 KiB
JavaScript
177 lines
No EOL
5.4 KiB
JavaScript
import { faker } from "https://cdn.jsdelivr.net/npm/@faker-js/faker@9.4.0/+esm";
|
||
import http from "k6/http";
|
||
import { check, sleep } from "k6";
|
||
import { Trend } from "k6/metrics";
|
||
import { textSummary } from 'https://jslib.k6.io/k6-summary/0.0.1/index.js';
|
||
import { SharedArray } from 'k6/data';
|
||
import encoding from 'k6/encoding';
|
||
|
||
const petCategory = [
|
||
"Dog",
|
||
"Cat",
|
||
"Fish",
|
||
"Bird",
|
||
"Hamster",
|
||
"Gerbil",
|
||
"Guinea Pig",
|
||
"Rabbit",
|
||
"Bearded Dragon",
|
||
"Leopard Gecko",
|
||
"Corn Snake",
|
||
];
|
||
const musicGenre = [
|
||
"Boy Band",
|
||
"Soul",
|
||
"Country & Western",
|
||
"Hip-Hop",
|
||
"K-Pop",
|
||
"New Romantic",
|
||
];
|
||
const BASE_URL = "https://valentinesong.oliver.digital/back";
|
||
|
||
const randomItem = (arr) => arr[Math.floor(Math.random() * arr.length)];
|
||
|
||
// Custom metrics for tracking journey stages
|
||
const submissionTime = new Trend("submission_duration");
|
||
const pollingWaitTime = new Trend("polling_wait_duration");
|
||
const resultFetchTime = new Trend("result_fetch_duration");
|
||
const totalJourneyTime = new Trend("total_journey_duration");
|
||
const pollAttempts = new Trend("poll_attempts_count");
|
||
|
||
// Load the image file and convert to base64
|
||
// Use SharedArray to load file only once and share across VUs
|
||
const imageBase64 = new SharedArray('dogImage', function() {
|
||
const binFile = open('./dog-test.jpeg', 'b');
|
||
return [encoding.b64encode(binFile)];
|
||
});
|
||
|
||
// Define the load test configuration
|
||
export const options = {
|
||
vus: 2, // number of virtual users running simultaneously
|
||
iterations: 2, // number of journey/iteration to complete across all VUS
|
||
maxDuration: "10m", // Allow up to 10 mins for the journey to finish
|
||
};
|
||
|
||
export default function () {
|
||
const journeyStartTime = Date.now();
|
||
|
||
const payload = JSON.stringify({
|
||
pet_name: faker.person.firstName(),
|
||
pet_type: randomItem(petCategory),
|
||
music_vibe: randomItem(musicGenre),
|
||
owner_name: faker.person.firstName(),
|
||
cookie_id: `cookie_test_${faker.string.alphanumeric(10)}`,
|
||
// photo: "data:image/gif;base64,R0lGODlhAQABAAAAACw=",
|
||
photo: `data:image/jpeg;base64,${imageBase64[0]}`
|
||
});
|
||
|
||
const params = {
|
||
headers: {
|
||
"Content-Type": "application/json",
|
||
Accept: "application/json",
|
||
},
|
||
};
|
||
|
||
// Perform the POST request
|
||
const submissionStart = Date.now();
|
||
const res = http.post(`${BASE_URL}/api/submissions`, payload, params);
|
||
const submissionDuration = Date.now() - submissionStart;
|
||
submissionTime.add(submissionDuration);
|
||
|
||
// Validate the response
|
||
const submissionCheck = check(res, {
|
||
"status is 200": (r) => r.status === 201 || r.status === 200,
|
||
"response time < 2s": (r) => r.timings.duration < 2000,
|
||
valid_session_id: (r) => r.json().session_id.length > 10,
|
||
});
|
||
|
||
if (!submissionCheck) {
|
||
console.error(`❌ Submission failed for session`);
|
||
return; // Exit if submission fails
|
||
}
|
||
|
||
const session_id = res.json().session_id;
|
||
console.log(`✅ Submission successful. Session ID: ${session_id}`);
|
||
|
||
// 2. Poll for Results
|
||
let isReady = false;
|
||
const pollingInterval = 20; // seconds
|
||
const timeoutInSeconds = 10 * 60; // 10 minutes total wait time
|
||
const pollingStartTime = Date.now();
|
||
let pollCount = 0;
|
||
|
||
while (!isReady) {
|
||
// 1. Check if we have timed out
|
||
if ((Date.now() - pollingStartTime) / 1000 > timeoutInSeconds) {
|
||
console.error(`⏱️ Polling timed out for session: ${session_id}`);
|
||
break;
|
||
}
|
||
// Wait before next polling
|
||
sleep(pollingInterval);
|
||
pollCount++;
|
||
|
||
const pollRes = http.get(
|
||
`${BASE_URL}/api/submissions/${session_id}/status`,
|
||
);
|
||
|
||
check(pollRes, {
|
||
"polling response received (status = 200)": (r) => r.status === 200,
|
||
});
|
||
|
||
if (pollRes.status === 200 && pollRes.json().status === "success") {
|
||
isReady = true;
|
||
console.log(
|
||
`🎉 Result ready after ${pollCount} poll attempts (${((Date.now() - pollingStartTime) / 1000).toFixed(1)}s)`,
|
||
);
|
||
}
|
||
}
|
||
|
||
const pollingDuration = Date.now() - pollingStartTime;
|
||
pollingWaitTime.add(pollingDuration);
|
||
pollAttempts.add(pollCount);
|
||
|
||
// 3. Final Result
|
||
if (isReady) {
|
||
const resultFetchStart = Date.now();
|
||
const resultRes = http.get(`${BASE_URL}/api/results/${session_id}`);
|
||
const resultFetchDuration = Date.now() - resultFetchStart;
|
||
resultFetchTime.add(resultFetchDuration);
|
||
|
||
const resultCheck = check(resultRes, {
|
||
"result fetched": (r) => r.status === 200,
|
||
"status msg has success": (r) => r.json().status === "success",
|
||
"has lyrics": (r) => r.json().lyrics.length > 10,
|
||
"has video_url": (r) => r.json().video_url.length > 10,
|
||
});
|
||
|
||
if (resultCheck) {
|
||
const totalDuration = Date.now() - journeyStartTime;
|
||
totalJourneyTime.add(totalDuration);
|
||
console.log(
|
||
`✅ Complete journey successful! Total time: ${(totalDuration / 1000).toFixed(1)}s`,
|
||
);
|
||
} else {
|
||
console.error(`❌ Result validation failed for session: ${session_id}`);
|
||
}
|
||
// User stays on result page for a bit
|
||
sleep(20);
|
||
} else {
|
||
console.error(
|
||
`❌ Journey incomplete - result never became ready for session: ${session_id}`,
|
||
);
|
||
}
|
||
|
||
// Wait 1 second between requests per user
|
||
sleep(1);
|
||
}
|
||
|
||
export function handleSummary(data) {
|
||
console.log('\n' + '='.repeat(80));
|
||
console.log('📊 CUSTOM METRICS SUMMARY');
|
||
console.log('ℹ️ All durations are in milliseconds (ms). 1 second = 1,000ms | 1 minute = 60,000ms');
|
||
console.log('='.repeat(80) + '\n');
|
||
|
||
return {
|
||
'stdout': textSummary(data, { indent: ' ', enableColors: true }),
|
||
};
|
||
} |