76 lines
2.6 KiB
TypeScript
76 lines
2.6 KiB
TypeScript
/* eslint-disable no-console -- CLI tool: stdout/stderr are the user interface. */
|
|
/**
|
|
* Sentry smoke-test driver for apps/web.
|
|
*
|
|
* Hits the test routes registered in apps/web/app/routes/__test.* and
|
|
* verifies that the HTTP responses look right. Use against a deployed
|
|
* staging environment after enabling SENTRY_TEST_ROUTES=true.
|
|
*
|
|
* Usage:
|
|
* pnpm exec tsx apps/web/scripts/smoke-sentry.ts https://app.staging.example
|
|
*
|
|
* Exit codes:
|
|
* 0 — all probes returned the expected status code
|
|
* 1 — at least one probe failed; details are printed to stderr
|
|
*
|
|
* @see docs/improvement-plan/02-observability/sentry-wiring-web.md (Step 11)
|
|
*/
|
|
|
|
interface Probe {
|
|
path: string;
|
|
note: string;
|
|
expectedStatus: number | number[];
|
|
}
|
|
|
|
const probes: Probe[] = [
|
|
{
|
|
path: "/__test/client-throw",
|
|
note: "Client-side throw smoke route (loader returns 200, component throws on render)",
|
|
expectedStatus: 200,
|
|
},
|
|
];
|
|
|
|
async function runProbe(baseUrl: string, probe: Probe): Promise<{ probe: Probe; ok: boolean; status: number; bodySnippet: string }> {
|
|
const url = new URL(probe.path, baseUrl).toString();
|
|
const response = await fetch(url, { redirect: "manual" });
|
|
const body = await response.text();
|
|
const expectedStatuses = Array.isArray(probe.expectedStatus) ? probe.expectedStatus : [probe.expectedStatus];
|
|
return {
|
|
probe,
|
|
ok: expectedStatuses.includes(response.status),
|
|
status: response.status,
|
|
bodySnippet: body.slice(0, 200),
|
|
};
|
|
}
|
|
|
|
export async function runSmoke(baseUrl: string): Promise<number> {
|
|
if (!baseUrl) {
|
|
console.error("usage: smoke-sentry.ts <baseUrl>");
|
|
return 1;
|
|
}
|
|
console.log(`[smoke-sentry] target=${baseUrl}`);
|
|
const results = await Promise.all(probes.map((p) => runProbe(baseUrl, p)));
|
|
let failures = 0;
|
|
for (const r of results) {
|
|
if (r.ok) {
|
|
console.log(`[ok] ${r.probe.path} → ${r.status} (${r.probe.note})`);
|
|
} else {
|
|
failures += 1;
|
|
console.error(`[fail] ${r.probe.path} → ${r.status} expected=${r.probe.expectedStatus} body=${r.bodySnippet}`);
|
|
}
|
|
}
|
|
if (failures > 0) {
|
|
console.error(`[smoke-sentry] ${failures} probe(s) failed`);
|
|
return 1;
|
|
}
|
|
console.log("[smoke-sentry] all probes passed");
|
|
console.log("[smoke-sentry] verify in Sentry UI: a client-throw event with stack-trace pointing to __test.client-throw.tsx");
|
|
return 0;
|
|
}
|
|
|
|
const isMain = typeof process !== "undefined" && Array.isArray(process.argv) && process.argv[1] && import.meta.url === `file://${process.argv[1]}`;
|
|
if (isMain) {
|
|
const baseUrl = process.argv[2] ?? process.env.SENTRY_SMOKE_BASE_URL ?? "";
|
|
runSmoke(baseUrl).then((code) => process.exit(code));
|
|
}
|