44 lines
1.8 KiB
JavaScript
44 lines
1.8 KiB
JavaScript
// Read-only production drift probe. No authentication, writes, payments or
|
|
// provider calls are performed. It intentionally fails when a required
|
|
// deployment contract is missing.
|
|
import { mkdir, writeFile } from 'node:fs/promises';
|
|
|
|
const base = String(process.argv[2] || process.env.IHALEIST_LIVE_URL || 'https://ihaleist.com').replace(/\/$/, '');
|
|
const timeoutMs = 15_000;
|
|
const requiredHeaders = ['x-content-type-options', 'referrer-policy', 'permissions-policy'];
|
|
const checks = {};
|
|
|
|
async function probe(path) {
|
|
const controller = new AbortController();
|
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
try {
|
|
const response = await fetch(base + path, { signal: controller.signal, redirect: 'manual' });
|
|
return {
|
|
status: response.status,
|
|
headers: Object.fromEntries(requiredHeaders.map(name => [name, response.headers.get(name)])),
|
|
};
|
|
} catch (error) {
|
|
return { error: error.name === 'AbortError' ? `timeout after ${timeoutMs}ms` : error.message };
|
|
} finally {
|
|
clearTimeout(timer);
|
|
}
|
|
}
|
|
|
|
checks.home = await probe('/');
|
|
checks.health = await probe('/api/health');
|
|
checks.ready = await probe('/api/ready');
|
|
const missingHeaders = requiredHeaders.filter(name => checks.home?.headers?.[name] == null || checks.health?.headers?.[name] == null);
|
|
const report = {
|
|
base,
|
|
readOnly: true,
|
|
checkedAt: new Date().toISOString(),
|
|
checks,
|
|
pass: checks.home?.status === 200 && checks.health?.status === 200 && checks.ready?.status === 200 && missingHeaders.length === 0,
|
|
missingHeaders,
|
|
};
|
|
|
|
await mkdir('docs/quality/evidence', { recursive: true });
|
|
await writeFile('docs/quality/evidence/qa-live-runtime.json', JSON.stringify(report, null, 2) + '\n');
|
|
console.log(JSON.stringify(report));
|
|
if (!report.pass) process.exitCode = 1;
|