221 lines
No EOL
6.6 KiB
JavaScript
221 lines
No EOL
6.6 KiB
JavaScript
import ws from 'k6/ws';
|
|
import { check, sleep } from 'k6';
|
|
import { Rate, Trend, Counter } from 'k6/metrics';
|
|
import { randomString } from 'https://jslib.k6.io/k6-utils/1.2.0/index.js';
|
|
import http from 'k6/http';
|
|
|
|
// Load environment variables
|
|
const API_ORIGIN = __ENV.API_ORIGIN || 'https://api.lab.veza';
|
|
const CHAT_ORIGIN = __ENV.CHAT_ORIGIN || 'wss://chat.lab.veza';
|
|
const TEST_EMAIL_PREFIX = __ENV.TEST_EMAIL_PREFIX || 'user+ws';
|
|
const TEST_EMAIL_DOMAIN = __ENV.TEST_EMAIL_DOMAIN || 'lab.veza';
|
|
const TEST_PASSWORD_PREFIX = __ENV.TEST_PASSWORD_PREFIX || 'V3za!ws-';
|
|
|
|
// Custom metrics
|
|
const wsConnectionTime = new Trend('ws_connection_time');
|
|
const wsMessageSendTime = new Trend('ws_message_send_time');
|
|
const wsMessageReceiveTime = new Trend('ws_message_receive_time');
|
|
const wsConnectionFailures = new Rate('ws_connection_failures');
|
|
const wsMessageFailures = new Rate('ws_message_failures');
|
|
const messagesReceived = new Counter('messages_received');
|
|
const messagesSent = new Counter('messages_sent');
|
|
|
|
// Test configuration
|
|
export const options = {
|
|
scenarios: {
|
|
websocket_chat: {
|
|
executor: 'ramping-vus',
|
|
stages: [
|
|
{ duration: '30s', target: 10 }, // Ramp up to 10 connections
|
|
{ duration: '2m', target: 50 }, // Ramp up to 50 connections
|
|
{ duration: '2m', target: 50 }, // Stay at 50 connections
|
|
{ duration: '30s', target: 0 }, // Ramp down
|
|
],
|
|
gracefulRampDown: '30s',
|
|
},
|
|
},
|
|
thresholds: {
|
|
ws_connection_time: ['p(95)<500', 'p(99)<1000'],
|
|
ws_message_send_time: ['p(95)<100', 'p(99)<200'],
|
|
ws_connection_failures: ['rate<0.05'], // Less than 5% connection failures
|
|
ws_message_failures: ['rate<0.01'], // Less than 1% message failures
|
|
},
|
|
};
|
|
|
|
// Helper to create and authenticate a user
|
|
function createAuthenticatedUser() {
|
|
const user = {
|
|
email: `${TEST_EMAIL_PREFIX}${randomString(8)}@${TEST_EMAIL_DOMAIN}`,
|
|
password: `${TEST_PASSWORD_PREFIX}${randomString(8)}`,
|
|
};
|
|
|
|
// Register user
|
|
const registerRes = http.post(`${API_ORIGIN}/auth/register`, JSON.stringify(user), {
|
|
headers: { 'Content-Type': 'application/json' },
|
|
});
|
|
|
|
if (registerRes.status !== 201) {
|
|
console.error(`Failed to register user: ${registerRes.status}`);
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
const body = JSON.parse(registerRes.body);
|
|
return {
|
|
...user,
|
|
accessToken: body.access_token,
|
|
userId: body.user.id,
|
|
};
|
|
} catch (e) {
|
|
console.error('Failed to parse registration response');
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// Setup: Create test users
|
|
export function setup() {
|
|
const users = [];
|
|
const userCount = 60; // Create more than we need
|
|
|
|
console.log(`Creating ${userCount} test users for WebSocket test...`);
|
|
|
|
for (let i = 0; i < userCount; i++) {
|
|
const user = createAuthenticatedUser();
|
|
if (user) {
|
|
users.push(user);
|
|
}
|
|
sleep(0.1); // Small delay to avoid overwhelming the API
|
|
}
|
|
|
|
console.log(`Created ${users.length} authenticated users`);
|
|
return { users };
|
|
}
|
|
|
|
export default function (data) {
|
|
const { users } = data;
|
|
|
|
// Pick a random user
|
|
const user = users[Math.floor(Math.random() * users.length)];
|
|
if (!user) {
|
|
console.error('No user available');
|
|
return;
|
|
}
|
|
|
|
const url = `${CHAT_ORIGIN}?token=${user.accessToken}`;
|
|
const params = { tags: { user: user.email } };
|
|
|
|
let connectionStartTime = new Date();
|
|
let connected = false;
|
|
let messageCount = 0;
|
|
let lastMessageTime = new Date();
|
|
|
|
const res = ws.connect(url, params, function (socket) {
|
|
socket.on('open', () => {
|
|
const connectionEndTime = new Date();
|
|
wsConnectionTime.add(connectionEndTime - connectionStartTime);
|
|
connected = true;
|
|
|
|
console.log(`User ${user.email} connected to WebSocket`);
|
|
|
|
// Join general room
|
|
socket.send(JSON.stringify({
|
|
type: 'join',
|
|
room: 'general',
|
|
}));
|
|
|
|
// Send periodic messages
|
|
socket.setInterval(() => {
|
|
if (connected) {
|
|
const message = {
|
|
type: 'message',
|
|
room: 'general',
|
|
content: `Test message from ${user.email} at ${new Date().toISOString()}`,
|
|
};
|
|
|
|
const sendStart = new Date();
|
|
socket.send(JSON.stringify(message));
|
|
messagesSent.add(1);
|
|
|
|
// Record send time (approximation)
|
|
wsMessageSendTime.add(new Date() - sendStart);
|
|
}
|
|
}, 5000); // Send message every 5 seconds
|
|
|
|
// Send ping every 30 seconds to keep connection alive
|
|
socket.setInterval(() => {
|
|
if (connected) {
|
|
socket.send(JSON.stringify({ type: 'ping' }));
|
|
}
|
|
}, 30000);
|
|
});
|
|
|
|
socket.on('message', (data) => {
|
|
const receiveTime = new Date();
|
|
messagesReceived.add(1);
|
|
messageCount++;
|
|
|
|
try {
|
|
const message = JSON.parse(data);
|
|
|
|
// Calculate receive time since last message
|
|
wsMessageReceiveTime.add(receiveTime - lastMessageTime);
|
|
lastMessageTime = receiveTime;
|
|
|
|
// Check message validity
|
|
check(message, {
|
|
'message has type': (m) => m.type !== undefined,
|
|
'message is valid': (m) => m.error === undefined,
|
|
});
|
|
|
|
// Handle different message types
|
|
switch (message.type) {
|
|
case 'joined':
|
|
console.log(`User ${user.email} joined room`);
|
|
break;
|
|
case 'message':
|
|
// Received a chat message
|
|
break;
|
|
case 'error':
|
|
console.error(`WebSocket error for ${user.email}: ${message.message}`);
|
|
wsMessageFailures.add(1);
|
|
break;
|
|
}
|
|
} catch (e) {
|
|
console.error(`Failed to parse message: ${data}`);
|
|
wsMessageFailures.add(1);
|
|
}
|
|
});
|
|
|
|
socket.on('close', () => {
|
|
connected = false;
|
|
console.log(`User ${user.email} disconnected. Received ${messageCount} messages`);
|
|
});
|
|
|
|
socket.on('error', (e) => {
|
|
console.error(`WebSocket error for ${user.email}: ${e}`);
|
|
wsConnectionFailures.add(1);
|
|
});
|
|
|
|
// Simulate user activity for 30-60 seconds
|
|
const activityDuration = 30 + Math.random() * 30;
|
|
socket.setTimeout(() => {
|
|
console.log(`User ${user.email} ending session after ${activityDuration}s`);
|
|
socket.close();
|
|
}, activityDuration * 1000);
|
|
});
|
|
|
|
// Check connection result
|
|
check(res, {
|
|
'WebSocket connection successful': (r) => r && r.status === 101,
|
|
});
|
|
|
|
if (!res || res.status !== 101) {
|
|
wsConnectionFailures.add(1);
|
|
}
|
|
}
|
|
|
|
// Teardown
|
|
export function teardown(data) {
|
|
console.log('WebSocket chat test completed');
|
|
console.log(`Total users created: ${data.users.length}`);
|
|
} |