Files
ihaleist/tests/live-runtime-probe.mjs
T
hikmettan fd5db7544e
Quality gates / test-build (push) Canceled after 0s
Quality gates / container (push) Canceled after 0s
probe production document browser capability
2026-09-06 21:10:17 +03:00

63 lines
3.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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 evidencePath = process.env.IHALEIST_LIVE_EVIDENCE_PATH || 'docs/quality/evidence/qa-live-runtime.json';
const timeoutMs = 15_000;
const requiredHeaders = [
'x-content-type-options', 'referrer-policy', 'permissions-policy',
'x-frame-options', 'content-security-policy',
...(base.startsWith('https://') ? ['strict-transport-security'] : []),
];
const observedHeaders = [...new Set([...requiredHeaders, 'strict-transport-security'])];
const checks = {};
async function probe(path, successField = null) {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), timeoutMs);
try {
const response = await fetch(base + path, { signal: controller.signal, redirect: 'manual' });
const result = {
status: response.status,
contentType: response.headers.get('content-type'),
headers: Object.fromEntries(observedHeaders.map(name => [name, response.headers.get(name)])),
};
if (successField) {
const raw = await response.text();
try { result.json = JSON.parse(raw); } catch { result.jsonError = 'JSON yanıtı ayrıştırılamadı.'; }
result.apiSuccess = result.json?.[successField] === true;
}
return result;
} 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', 'ok');
// /api/health and /api/ready use the same { ok: true } probe contract. The
// readiness payload also includes a separate `ready` diagnostic field.
checks.ready = await probe('/api/ready', 'ok');
// The EKAP document fallback depends on the bundled browser being available;
// check its public capability endpoint without opening a document or mutating
// any account data.
checks.documents = await probe('/api/documents/config', 'tarayiciVar');
const missingHeaders = requiredHeaders.filter(name => [checks.home, checks.health, checks.ready, checks.documents].some(check => check?.headers?.[name] == null));
const report = {
base,
readOnly: true,
checkedAt: new Date().toISOString(),
checks,
pass: checks.home?.status === 200 && checks.health?.status === 200 && checks.health.apiSuccess === true && checks.ready?.status === 200 && checks.ready.apiSuccess === true && checks.documents?.status === 200 && checks.documents.apiSuccess === true && missingHeaders.length === 0,
missingHeaders,
};
await mkdir('docs/quality/evidence', { recursive: true });
await writeFile(evidencePath, JSON.stringify(report, null, 2) + '\n');
console.log(JSON.stringify(report));
if (!report.pass) process.exitCode = 1;