diff --git a/docs/quality/evidence/qa-live-runtime.json b/docs/quality/evidence/qa-live-runtime.json index 9f2a9ee..7713cb4 100644 --- a/docs/quality/evidence/qa-live-runtime.json +++ b/docs/quality/evidence/qa-live-runtime.json @@ -1,19 +1,37 @@ { - "date": "2026-09-06", - "url": "https://ihaleist.com", + "base": "https://ihaleist.com", + "readOnly": true, + "checkedAt": "2026-09-06T16:13:35.286Z", "checks": { - "apiHealth": {"status": 200, "body": {"ok": true}}, - "apiReady": {"status": 404, "body": {"success": false, "error": "not found"}}, - "securityHeadersObserved": { - "apiHealth": { - "access-control-allow-origin": "*", + "home": { + "status": 200, + "headers": { "x-content-type-options": null, "referrer-policy": null, "permissions-policy": null } }, - "servedAsset": "/assets/index-BtYUXYcw.js" + "health": { + "status": 200, + "headers": { + "x-content-type-options": null, + "referrer-policy": null, + "permissions-policy": null + } + }, + "ready": { + "status": 404, + "headers": { + "x-content-type-options": null, + "referrer-policy": null, + "permissions-policy": null + } + } }, - "scope": "Read-only live probe; no login, mutation, payment, message, or tender submission performed.", - "interpretation": "Live deployment does not yet contain the local readiness endpoint or the local API security headers. Deployment remains pending." + "pass": false, + "missingHeaders": [ + "x-content-type-options", + "referrer-policy", + "permissions-policy" + ] } diff --git a/docs/quality/uygulama-durumu.md b/docs/quality/uygulama-durumu.md index d5cc605..d631cf4 100644 --- a/docs/quality/uygulama-durumu.md +++ b/docs/quality/uygulama-durumu.md @@ -2200,3 +2200,9 @@ Doğrulama: browser QA başarılı; analitik erişilebilirlik kontrolü, ICS, ç Belge son kullanma uyarısı artık “bugün” anahtarını UTC tarihinden değil İstanbul tarihinden üretiyor. Yalnız tarih içeren son kullanma değerleri `+03:00` gece yarısı olarak ayrıştırılıyor; böylece Türkiye’de gün değişimi sırasında 30 gün/son kullanma uyarısı bir gün kaymıyor. Geçersiz tarih sessizce yaklaşan belge olarak sayılmıyor. Doğrulama: 293 otomatik test ve üretim derlemesi başarılı. Gece yarısı sınırı için gerçek saat kontrollü tarayıcı kabulü ayrıca yapılmalı. + +### 2026-09-06 · Tekrarlanabilir canlı drift probe’u + +`npm run qa:live -- https://ihaleist.com` komutu eklendi. Probe yalnızca ana sayfa, `/api/health` ve `/api/ready` uç noktalarını GET ile kontrol ediyor; authentication, veri yazma, ödeme veya sağlayıcı çağrısı yapmıyor. Ana sayfa ve health için güvenlik başlıklarını da zorunlu tutup JSON kanıtı `qa-live-runtime.json` içine yazıyor. + +6 Eylül 2026 çalıştırmasında ana sayfa ve health 200, readiness 404 döndü; üç güvenlik başlığı canlı yanıtlarda bulunmadı. Probe bu nedenle beklenen şekilde başarısız oldu. Bu sonuç yerel düzeltmelerin canlıya alınmadığını gösteren drift kanıtıdır. diff --git a/package.json b/package.json index 36d1b9a..131ba22 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "dev": "vite", "build": "vite build", "qa:browser": "node tests/browser-acceptance.mjs", + "qa:live": "node tests/live-runtime-probe.mjs", "preview": "vite preview", "server": "node server/index.js", "test": "node --test tests/*.test.mjs" diff --git a/tests/live-runtime-probe.mjs b/tests/live-runtime-probe.mjs new file mode 100644 index 0000000..c5bad05 --- /dev/null +++ b/tests/live-runtime-probe.mjs @@ -0,0 +1,43 @@ +// 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;