Skip to content

Instantly share code, notes, and snippets.

@nikoheikkila
Created November 9, 2025 17:13
Show Gist options
  • Save nikoheikkila/bb31c6741e2abf980a5a1984d4c2faff to your computer and use it in GitHub Desktop.
Save nikoheikkila/bb31c6741e2abf980a5a1984d4c2faff to your computer and use it in GitHub Desktop.
TypeScript & Vitest performance test
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import https from "node:https";
interface Result {
success: boolean;
statusCode?: number;
error?: string;
}
/**
* Simulates load testing using a simple Vitest test suite and Node.js's
* built-in HTTPS module.
*/
describe("HTTP Load Test", () => {
const maxRequests = 1000;
const requestTimeout = 30_000;
const timeout = 60_000;
const targetUrl = new URL(process.env.URL ?? "");
let agent: https.Agent;
beforeAll(() => {
/**
* By default, Node.js creates a new TCP connection for every request,
* which includes DNS lookup and TCP handshake overhead. This is extremely
* inefficient for load testing where we're hitting the same endpoint repeatedly.
*
* With keep-alive enabled, connections are reused, which can improve throughput
* and significantly reduce CPU usage.
*/
agent = new https.Agent({
keepAlive: true,
keepAliveMsecs: 3000,
maxSockets: maxRequests,
maxFreeSockets: 256,
timeout,
});
});
afterAll(() => {
agent.destroy();
});
it(
`with ${maxRequests} requests`,
async () => {
const results = {
success: 0,
failures: [] as string[],
};
const requests = Array.from({ length: maxRequests }, () => makeRequest(targetUrl));
const responses = await Promise.all(requests);
responses.forEach((response) => {
if (response.error) {
results.failures.push(response.error);
return;
}
if (response.success) {
results.success++;
return;
}
throw new Error(`Unexpected response: ${JSON.stringify(response)}`);
});
console.log("\n=== Load Test Results ===");
console.log(`Total requests: ${maxRequests}`);
console.log(`Successful: ${results.success}`);
console.log(`Failed: ${results.failures.length}`);
if (results.failures.length > 0) {
const errorCounts = results.failures.reduce(
(total, error) => {
total[error] = (total[error] || 0) + 1;
return total;
},
{} as Record<string, number>,
);
console.log("\n=== Error Summary ===");
Object.entries(errorCounts).forEach(([error, count]) => {
console.log(`${error}: ${count}`);
});
}
const successRate = (results.success / maxRequests) * 100;
expect(successRate).toBeGreaterThanOrEqual(95);
},
timeout,
);
const makeRequest = (url: URL): Promise<Result> => {
return new Promise((resolve) => {
const options = {
agent,
hostname: url.hostname,
port: url.port || 443,
path: url.pathname + url.search,
method: "GET",
};
const request = https.request(options, (response) => {
// We discard the data since we're only testing throughput
response.on("data", () => {});
response.on("end", () => {
return resolve({
success: response.statusCode !== undefined && response.statusCode >= 200 && response.statusCode < 300,
statusCode: response.statusCode,
});
});
});
request.on("error", (error) => {
return resolve({
success: false,
error: error.message,
});
});
// Set a timeout for the entire request
request.setTimeout(requestTimeout, () => {
request.destroy();
return resolve({
success: false,
error: "Request timeout",
});
});
return request.end();
});
};
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment