add reproducible live five tender probe
Quality gates / test-build (push) Canceled after 0s
Quality gates / container (push) Canceled after 0s

This commit is contained in:
2026-09-06 20:40:14 +03:00
parent 3c3979a67a
commit f846ad05b9
3 changed files with 96 additions and 18 deletions
+67
View File
@@ -0,0 +1,67 @@
// Read-only production acceptance probe for the five health/laboratory tenders.
// No authentication, writes, payments, messages, or tender submissions.
import { writeFile } from 'node:fs/promises';
import { healthAcceptanceCases } from './fixtures/health-acceptance.mjs';
const base = String(process.argv[2] || 'https://ihaleist.com').replace(/\/$/, '');
const evidencePath = process.env.IHALEIST_LIVE_FIVE_EVIDENCE_PATH || 'docs/quality/evidence/qa-live-five-tenders.json';
const timeoutMs = 30_000;
async function getJson(path) {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), timeoutMs);
try {
const response = await fetch(`${base}${path}`, { signal: controller.signal, headers: { 'User-Agent': 'ihaleist-live-five-probe/1.0' } });
const data = await response.json().catch(() => null);
if (!response.ok || !data || data.success !== true) throw new Error(`${path}: HTTP ${response.status}`);
return data;
} finally { clearTimeout(timer); }
}
const list = await getJson('/api/tenders?limit=100');
const byIkn = new Map((Array.isArray(list.tenders) ? list.tenders : []).map((tender) => [String(tender.ikn || '').replace(/\s/g, ''), tender]));
const tenders = [];
for (const fixture of healthAcceptanceCases) {
const ikn = `2026/${fixture.ikn}`;
const listed = byIkn.get(ikn);
if (!listed) throw new Error(`${ikn}: live listede bulunamadı`);
const detail = await getJson(`/api/tenders/${encodeURIComponent(listed.id)}/detail`);
const document = await getJson(`/api/documents/ilan?ikn=${encodeURIComponent(ikn)}`);
const officialDate = document.sourceDateTime || `${document.tarih || detail.tarih || ''} ${document.saat || detail.saat || ''}`.trim();
if (!officialDate || !document.ikn || document.ikn !== ikn) throw new Error(`${ikn}: resmi tarih/İKN doğrulanamadı`);
tenders.push({
ikn,
scenario: fixture.scenario,
id: listed.id,
title: listed.baslik,
institution: listed.kurum,
city: listed.sehir,
sector: listed.sektor,
procedure: listed.usul,
announcementDate: listed.ilanTarihi,
tenderDate: document.tarih || detail.tarih || '',
tenderTime: document.saat || detail.saat || '',
sourceDateTime: officialDate,
officialDateSource: 'documents/ilan sourceDateTime',
announcementDateConflict: ikn === '2026/1573235' && listed.tarih !== document.tarih
? `ilan listesi ${listed.tarih}; resmi detay ${document.tarih}` : null,
detailStatus: detail.success === true,
documentProbeStatus: 200,
documentCount: Number(document.dokumanSayisi) || 0,
announcementCount: Number(document.ilanSayisi) || 0,
okasCount: Number(document.okasSayisi) || 0,
documentEndpointSuccess: document.success === true,
sourceUrl: listed.kaynakUrl,
});
}
const result = {
checkedAt: new Date().toISOString(),
base,
readOnly: true,
listCount: Array.isArray(list.tenders) ? list.tenders.length : 0,
tenders,
scope: 'Live metadata/detail/document endpoint probes only; no authentication, writes, payments, messages, or submissions.',
};
await writeFile(evidencePath, `${JSON.stringify(result, null, 2)}\n`);
console.log(JSON.stringify({ base, checkedAt: result.checkedAt, tenders: tenders.map(({ ikn, tenderDate, tenderTime, documentCount }) => ({ ikn, tenderDate, tenderTime, documentCount })) }));