sharded-gotify/benchmark/k6/websocket-test.js

121 lines
3.7 KiB
JavaScript

import ws from 'k6/ws';
import { check, sleep } from 'k6';
import http from 'k6/http';
import encoding from 'k6/encoding';
// Test configuration
export const options = {
stages: [
{ duration: '30s', target: 1000 }, // Ramp up to 1000 connections
{ duration: '1m', target: 1000 }, // Stay at 1000 connections
{ duration: '30s', target: 5000 }, // Ramp up to 5000 connections
{ duration: '2m', target: 5000 }, // Stay at 5000 connections
{ duration: '30s', target: 10000 }, // Ramp up to 10000 connections
{ duration: '2m', target: 10000 }, // Stay at 10000 connections
{ duration: '30s', target: 0 }, // Ramp down
],
thresholds: {
'ws_connecting': ['p(95)<500'], // 95% of connections should connect in <500ms
'ws_session_duration': ['p(95)<1000'], // 95% of sessions should last <1s
'ws_ping': ['p(95)<100'], // 95% of pings should be <100ms
'ws_messages_sent': ['rate>100'], // Should send >100 messages/sec
'ws_messages_received': ['rate>100'], // Should receive >100 messages/sec
},
};
// Configuration - can be overridden via environment variables
const BASE_URL = __ENV.BASE_URL || 'http://gotify-256:80';
const USERNAME = __ENV.USERNAME || 'admin';
const PASSWORD = __ENV.PASSWORD || 'admin';
const CONNECTIONS_PER_VU = __ENV.CONNECTIONS_PER_VU || 1;
// Global variables for authentication
let clientToken = null;
// Setup: Create a client token using basic auth
export function setup() {
const baseUrl = BASE_URL.replace(/^http/, 'http');
// Encode basic auth credentials
const credentials = encoding.b64encode(`${USERNAME}:${PASSWORD}`);
// Create a client token for WebSocket connection using basic auth
const clientRes = http.post(`${baseUrl}/client`, JSON.stringify({
name: `k6-client-${__VU}-${__ITER}-${Date.now()}`,
}), {
headers: {
'Content-Type': 'application/json',
'Authorization': `Basic ${credentials}`,
},
});
if (clientRes.status !== 200) {
console.error('Failed to create client:', clientRes.status, clientRes.body);
return {};
}
clientToken = JSON.parse(clientRes.body).token;
return {
clientToken: clientToken,
baseUrl: baseUrl,
};
}
export default function (data) {
if (!data.clientToken) {
console.error('No client token available');
return;
}
const wsUrl = data.baseUrl.replace(/^http/, 'ws') + '/stream?token=' + data.clientToken;
const params = { tags: { name: 'WebSocket Stream' } };
const response = ws.connect(wsUrl, params, function (socket) {
socket.on('open', function () {
// Connection established
console.log(`VU ${__VU}: WebSocket connection opened`);
});
socket.on('message', function (data) {
// Message received
const message = JSON.parse(data);
check(message, {
'message received': (msg) => msg.id !== undefined,
'message has content': (msg) => msg.message !== undefined,
});
});
socket.on('ping', function () {
// Server ping received
});
socket.on('pong', function () {
// Pong received
});
socket.on('close', function () {
// Connection closed
console.log(`VU ${__VU}: WebSocket connection closed`);
});
socket.on('error', function (e) {
if (e.error() !== 'websocket: close sent') {
console.error(`VU ${__VU}: WebSocket error:`, e.error());
}
});
// Keep connection alive for the duration of the test
// The server will send ping messages periodically
sleep(60); // Keep connection for 60 seconds
});
check(response, {
'status is 101': (r) => r && r.status === 101,
'protocol is websocket': (r) => r && r.url && r.url.startsWith('ws'),
});
sleep(1);
}