Files
support_backend/tests/load/admin-reporting.load.ts
T
saqib mirandClaude Sonnet 5 8400a8db84 feat(016-load-concurrency-testing): US5 — autocannon load-test tooling
tests/load/autocannon.config.ts is a thin shared wrapper over autocannon's
programmatic API producing this feature's own report shape (throughput,
latency p50/p90/p99, non-2xx, rate-limited count) — printed and written to
tests/load/reports/ (gitignored) for every run. No pass/fail threshold is
applied (FR-009): throughput/latency targets are an OPEN BUSINESS DECISION
per the roadmap's own convention, never invented.

Three scripts cover the named critical endpoint groups: ticket-creation
(pure DB path), admin-reporting (015-reporting-dashboards, pure DB path),
and ai-support-flow (005-ai-support's real Anthropic API calls — clearly
flagged as real, billed cost, run only at a small bounded amount rather than
an open-ended duration). All three were run once at a small scale against the
real dev server to confirm the tooling works end-to-end and cleans up fully
after itself (verified via direct DB checks, not just script exit codes).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-10 11:50:12 +05:30

70 lines
2.3 KiB
TypeScript

/**
* specs/016-load-concurrency-testing User Story 5 / FR-007: load/throughput baseline for the
* 015-reporting-dashboards admin endpoints — pure database aggregation reads, no third-party
* cost (unlike ai-support-flow.load.ts). Run against a real running dev server:
* `npx tsx tests/load/admin-reporting.load.ts`.
*
* Signs in as the project's own seeded admin account once (a session JWT is reusable across
* requests, unlike 002-saas-integration's single-use integration tokens), then cycles across
* all four dashboards so the report reflects a realistic mix of the endpoint group, not just one
* route.
*/
import { runLoadTest } from './autocannon.config';
const API_URL = process.env.LOAD_TEST_API_URL ?? 'http://localhost:4501';
const CONNECTIONS = Number(process.env.LOAD_TEST_CONNECTIONS ?? 10);
const DURATION_SEC = Number(process.env.LOAD_TEST_DURATION_SEC ?? 10);
const ADMIN_EMAIL = process.env.LOAD_TEST_ADMIN_EMAIL ?? 'admin@supporthub.internal';
const ADMIN_PASSWORD = process.env.LOAD_TEST_ADMIN_PASSWORD ?? 'ChangeMe123!';
const DASHBOARD_PATHS = [
'/admin/reports/management',
'/admin/reports/support',
'/admin/reports/ai',
];
async function signIn(): Promise<string> {
const response = await fetch(`${API_URL}/auth/login`, {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ email: ADMIN_EMAIL, password: ADMIN_PASSWORD }),
});
if (!response.ok) {
throw new Error(
`Admin sign-in failed (${response.status}) — set LOAD_TEST_ADMIN_EMAIL/PASSWORD if the seeded admin credentials differ.`,
);
}
const body = (await response.json()) as { data: { token: string } };
return body.data.token;
}
async function main(): Promise<void> {
const token = await signIn();
let requestIndex = 0;
await runLoadTest('admin-reporting', {
url: API_URL,
connections: CONNECTIONS,
duration: DURATION_SEC,
requests: [
{
method: 'GET',
headers: { authorization: `Bearer ${token}` },
setupRequest: (request) => {
request.path = DASHBOARD_PATHS[requestIndex % DASHBOARD_PATHS.length];
requestIndex += 1;
return request;
},
},
],
});
}
main()
.then(() => process.exit(0))
.catch((error) => {
// eslint-disable-next-line no-console
console.error(error);
process.exit(1);
});