155 lines
3.7 KiB
JavaScript
155 lines
3.7 KiB
JavaScript
import { check, sleep } from "k6";
|
|
import http from "k6/http";
|
|
import { Counter } from "k6/metrics";
|
|
import { WebSocket } from "k6/websockets";
|
|
|
|
function required(name) {
|
|
const value = __ENV[name];
|
|
|
|
if (value === undefined || value === "") {
|
|
throw new Error(`${name} is required`);
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
function positiveInteger(name) {
|
|
const value = Number(required(name));
|
|
|
|
if (!Number.isInteger(value) || value <= 0) {
|
|
throw new Error(`${name} must be a positive integer`);
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
function nonNegativeNumber(name) {
|
|
const value = Number(required(name));
|
|
|
|
if (!Number.isFinite(value) || value < 0) {
|
|
throw new Error(`${name} must be a non-negative number`);
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
const baseUrl = required("BASE_URL").replace(/\/+$/, "");
|
|
|
|
if (!/^https?:\/\/[^/?#]+$/.test(baseUrl)) {
|
|
throw new Error("BASE_URL must be an HTTP(S) origin without a path");
|
|
}
|
|
|
|
const websocketUrl =
|
|
baseUrl.replace(/^http/, "ws") + "/live/websocket?vsn=2.0.0";
|
|
const websocketHoldMs = positiveInteger("WS_HOLD_MS");
|
|
const websocketConnectTimeoutMs = positiveInteger("WS_CONNECT_TIMEOUT_MS");
|
|
const httpThinkSeconds = nonNegativeNumber("HTTP_THINK_SECONDS");
|
|
|
|
const websocketOpened = new Counter("wnh_websocket_opened");
|
|
const websocketErrors = new Counter("wnh_websocket_errors");
|
|
const heartbeatReplies = new Counter("wnh_websocket_heartbeat_replies");
|
|
|
|
export const options = {
|
|
discardResponseBodies: true,
|
|
scenarios: {
|
|
public_http: {
|
|
executor: "constant-vus",
|
|
exec: "publicHttp",
|
|
vus: positiveInteger("HTTP_VUS"),
|
|
duration: required("DURATION"),
|
|
},
|
|
phoenix_websocket: {
|
|
executor: "constant-vus",
|
|
exec: "phoenixWebsocket",
|
|
vus: positiveInteger("WS_VUS"),
|
|
duration: required("DURATION"),
|
|
},
|
|
},
|
|
};
|
|
|
|
export function publicHttp() {
|
|
const responses = http.batch([
|
|
{
|
|
method: "GET",
|
|
url: `${baseUrl}/`,
|
|
tags: { endpoint: "home" },
|
|
},
|
|
{
|
|
method: "GET",
|
|
url: `${baseUrl}/safety`,
|
|
tags: { endpoint: "safety" },
|
|
},
|
|
{
|
|
method: "GET",
|
|
url: `${baseUrl}/healthz/ready`,
|
|
tags: { endpoint: "readiness" },
|
|
},
|
|
]);
|
|
|
|
check(responses[0], { "home returned 200": (response) => response.status === 200 });
|
|
check(responses[1], { "safety returned 200": (response) => response.status === 200 });
|
|
check(responses[2], {
|
|
"readiness returned 200": (response) => response.status === 200,
|
|
});
|
|
|
|
sleep(httpThinkSeconds);
|
|
}
|
|
|
|
export function phoenixWebsocket() {
|
|
const socket = new WebSocket(websocketUrl, [], {
|
|
tags: { endpoint: "phoenix_live_socket" },
|
|
});
|
|
const ref = `${__VU}-${__ITER}`;
|
|
let opened = false;
|
|
let closeTimer;
|
|
const connectTimer = setTimeout(() => {
|
|
if (!opened) {
|
|
websocketErrors.add(1);
|
|
socket.close();
|
|
}
|
|
}, websocketConnectTimeoutMs);
|
|
|
|
socket.addEventListener("open", () => {
|
|
opened = true;
|
|
clearTimeout(connectTimer);
|
|
websocketOpened.add(1);
|
|
socket.send(JSON.stringify([null, ref, "phoenix", "heartbeat", {}]));
|
|
closeTimer = setTimeout(() => socket.close(), websocketHoldMs);
|
|
});
|
|
|
|
socket.addEventListener("message", (event) => {
|
|
let frame;
|
|
|
|
try {
|
|
frame = JSON.parse(event.data);
|
|
} catch (_error) {
|
|
return;
|
|
}
|
|
|
|
if (
|
|
Array.isArray(frame) &&
|
|
frame[1] === ref &&
|
|
frame[2] === "phoenix" &&
|
|
frame[3] === "phx_reply" &&
|
|
frame[4] &&
|
|
frame[4].status === "ok"
|
|
) {
|
|
heartbeatReplies.add(1);
|
|
}
|
|
});
|
|
|
|
socket.addEventListener("error", () => {
|
|
websocketErrors.add(1);
|
|
clearTimeout(connectTimer);
|
|
socket.close();
|
|
});
|
|
|
|
socket.addEventListener("close", () => {
|
|
clearTimeout(connectTimer);
|
|
|
|
if (closeTimer !== undefined) {
|
|
clearTimeout(closeTimer);
|
|
}
|
|
});
|
|
}
|