veza/loadtests/stream/hls.js
senke b103a09a25 chore: consolidate CI, E2E, backend and frontend updates
- CI: workflows updates (cd, ci), remove playwright.yml
- E2E: global-setup, auth/playlists/profile specs
- Remove playwright-report and test-results artifacts from tracking
- Backend: auth, handlers, services, workers, migrations
- Frontend: components, features, vite config
- Add e2e-results.json to gitignore
- Docs: REMEDIATION_PROGRESS, audit archive
- Rust: chat-server, stream-server updates
2026-02-17 16:43:21 +01:00

112 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);
}