113 lines
3.5 KiB
JavaScript
113 lines
3.5 KiB
JavaScript
|
|
/**
|
||
|
|
* Load test: HLS streaming - master playlist and segments
|
||
|
|
* Usage: k6 run loadtests/stream/hls.js
|
||
|
|
* Requires: Stream server running, AUTH_TOKEN (JWT), TRACK_ID (UUID of track with HLS)
|
||
|
|
*
|
||
|
|
* Env: STREAM_ORIGIN, AUTH_TOKEN, TRACK_ID
|
||
|
|
* Example: AUTH_TOKEN=xxx TRACK_ID=550e8400-e29b-41d4-a716-446655440000 k6 run loadtests/stream/hls.js
|
||
|
|
*/
|
||
|
|
import http from 'k6/http';
|
||
|
|
import { check, sleep } from 'k6';
|
||
|
|
import { Rate, Trend } from 'k6/metrics';
|
||
|
|
|
||
|
|
const errorRate = new Rate('hls_errors');
|
||
|
|
const manifestDuration = new Trend('hls_manifest_duration');
|
||
|
|
const segmentDuration = new Trend('hls_segment_duration');
|
||
|
|
|
||
|
|
const STREAM_ORIGIN = __ENV.STREAM_ORIGIN || 'http://localhost:8082';
|
||
|
|
const AUTH_TOKEN = __ENV.AUTH_TOKEN || '';
|
||
|
|
const TRACK_ID = __ENV.TRACK_ID || '';
|
||
|
|
|
||
|
|
export const options = {
|
||
|
|
scenarios: {
|
||
|
|
hls_streaming: {
|
||
|
|
executor: 'constant-arrival-rate',
|
||
|
|
rate: 5,
|
||
|
|
timeUnit: '1s',
|
||
|
|
duration: '2m',
|
||
|
|
preAllocatedVUs: 10,
|
||
|
|
maxVUs: 50,
|
||
|
|
},
|
||
|
|
},
|
||
|
|
thresholds: {
|
||
|
|
http_req_duration: ['p(95)<2000', 'p(99)<5000'],
|
||
|
|
hls_errors: ['rate<0.01'],
|
||
|
|
hls_manifest_duration: ['p(95)<500', 'p(99)<1000'],
|
||
|
|
hls_segment_duration: ['p(95)<2000', 'p(99)<5000'],
|
||
|
|
},
|
||
|
|
};
|
||
|
|
|
||
|
|
function getHeaders() {
|
||
|
|
const h = { Accept: 'application/vnd.apple.mpegurl,*/*' };
|
||
|
|
if (AUTH_TOKEN) {
|
||
|
|
h['Authorization'] = `Bearer ${AUTH_TOKEN}`;
|
||
|
|
}
|
||
|
|
return h;
|
||
|
|
}
|
||
|
|
|
||
|
|
export function setup() {
|
||
|
|
if (!TRACK_ID) {
|
||
|
|
console.warn('TRACK_ID not set - HLS tests will 404. Set TRACK_ID and AUTH_TOKEN for real streaming load.');
|
||
|
|
}
|
||
|
|
if (!AUTH_TOKEN) {
|
||
|
|
console.warn('AUTH_TOKEN not set - HLS routes require JWT. Requests may return 401.');
|
||
|
|
}
|
||
|
|
return { trackId: TRACK_ID || '00000000-0000-0000-0000-000000000000' };
|
||
|
|
}
|
||
|
|
|
||
|
|
export default function (data) {
|
||
|
|
const { trackId } = data;
|
||
|
|
if (!trackId || trackId === '00000000-0000-0000-0000-000000000000') {
|
||
|
|
// Still hit the endpoint to exercise the auth path
|
||
|
|
const url = `${STREAM_ORIGIN}/hls/${trackId}/master.m3u8`;
|
||
|
|
const res = http.get(url, { headers: getHeaders() });
|
||
|
|
errorRate.add(res.status !== 200 && res.status !== 404);
|
||
|
|
sleep(1);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 1. Fetch master playlist
|
||
|
|
const manifestUrl = `${STREAM_ORIGIN}/hls/${trackId}/master.m3u8`;
|
||
|
|
const manifestStart = Date.now();
|
||
|
|
const manifestRes = http.get(manifestUrl, { headers: getHeaders() });
|
||
|
|
manifestDuration.add(Date.now() - manifestStart);
|
||
|
|
|
||
|
|
const manifestOk = check(manifestRes, {
|
||
|
|
'master.m3u8 returns 200': (r) => r.status === 200,
|
||
|
|
});
|
||
|
|
errorRate.add(!manifestOk);
|
||
|
|
if (!manifestOk) {
|
||
|
|
sleep(1);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 2. Parse playlist for segment URLs (simplified: extract quality playlists or segments)
|
||
|
|
const body = manifestRes.body;
|
||
|
|
const qualityMatch = body.match(/\/hls\/[^/]+\/([^/\s]+)\/playlist\.m3u8/);
|
||
|
|
const quality = qualityMatch ? qualityMatch[1] : 'high';
|
||
|
|
|
||
|
|
sleep(0.2);
|
||
|
|
|
||
|
|
// 3. Fetch quality playlist
|
||
|
|
const qualityUrl = `${STREAM_ORIGIN}/hls/${trackId}/${quality}/playlist.m3u8`;
|
||
|
|
const qualityRes = http.get(qualityUrl, { headers: getHeaders() });
|
||
|
|
errorRate.add(qualityRes.status !== 200);
|
||
|
|
if (qualityRes.status !== 200) {
|
||
|
|
sleep(1);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 4. Fetch first segment (if present)
|
||
|
|
const segmentMatch = qualityRes.body.match(/(segment_\d+\.ts)/);
|
||
|
|
if (segmentMatch) {
|
||
|
|
const segmentName = segmentMatch[1];
|
||
|
|
const segmentUrl = `${STREAM_ORIGIN}/hls/${trackId}/${quality}/${segmentName}`;
|
||
|
|
const segmentStart = Date.now();
|
||
|
|
const segmentRes = http.get(segmentUrl, { headers: getHeaders() });
|
||
|
|
segmentDuration.add(Date.now() - segmentStart);
|
||
|
|
errorRate.add(segmentRes.status !== 200);
|
||
|
|
}
|
||
|
|
|
||
|
|
sleep(1);
|
||
|
|
}
|