diff --git a/docs/quality/evidence/qa-live-five-tenders.json b/docs/quality/evidence/qa-live-five-tenders.json index d388093..a311427 100644 --- a/docs/quality/evidence/qa-live-five-tenders.json +++ b/docs/quality/evidence/qa-live-five-tenders.json @@ -1,10 +1,12 @@ { - "checkedAt": "2026-09-06T17:36:00+03:00", + "checkedAt": "2026-09-06T17:39:49.038Z", "base": "https://ihaleist.com", "readOnly": true, + "listCount": 8224, "tenders": [ { "ikn": "2026/1582991", + "scenario": "mikroskop", "id": "ilan-2204986", "title": "Mikroskop satın alınacaktır", "institution": "GİRESUN EĞİTİM VE ARAŞTIRMA HASTANESİ BAŞHEKİMLİĞİ", @@ -14,18 +16,20 @@ "announcementDate": "2026-08-27", "tenderDate": "10.09.2026", "tenderTime": "10:00", + "sourceDateTime": "10.09.2026 10:00", + "officialDateSource": "documents/ilan sourceDateTime", + "announcementDateConflict": null, "detailStatus": true, "documentProbeStatus": 200, "documentCount": 0, - "sourceUrl": "https://www.ilan.gov.tr/ilan/2204986/ihale-duyurulari-mal-alim-ihaleleri-mikroskop-satin-alinacaktir", "announcementCount": 1, "okasCount": 2, - "sourceDateTime": "10.09.2026 10:00", "documentEndpointSuccess": true, - "officialDateSource": "documents/ilan sourceDateTime" + "sourceUrl": "https://www.ilan.gov.tr/ilan/2204986/ihale-duyurulari-mal-alim-ihaleleri-mikroskop-satin-alinacaktir" }, { "ikn": "2026/1534665", + "scenario": "sarf", "id": "ilan-2205024", "title": "26 kalem laboratuvar sarf malzeme alımı", "institution": "KAYSERİ DEVLET HASTANESİ BAŞHEKİMLİĞİ", @@ -35,18 +39,20 @@ "announcementDate": "2026-08-31", "tenderDate": "22.09.2026", "tenderTime": "11:00", + "sourceDateTime": "22.09.2026 11:00", + "officialDateSource": "documents/ilan sourceDateTime", + "announcementDateConflict": null, "detailStatus": true, "documentProbeStatus": 200, "documentCount": 0, - "sourceUrl": "https://www.ilan.gov.tr/ilan/2205024/ihale-duyurulari-mal-alim-ihaleleri-26-kalem-laboratuvar-sarf-malzeme-alimi", "announcementCount": 1, "okasCount": 1, - "sourceDateTime": "22.09.2026 11:00", "documentEndpointSuccess": true, - "officialDateSource": "documents/ilan sourceDateTime" + "sourceUrl": "https://www.ilan.gov.tr/ilan/2205024/ihale-duyurulari-mal-alim-ihaleleri-26-kalem-laboratuvar-sarf-malzeme-alimi" }, { "ikn": "2026/1499061", + "scenario": "cihaz-servis", "id": "ilan-2204525", "title": "Laboratuvar malzemesi alınacaktır", "institution": "AKDENİZ ÜNİVERSİTESİ REKTÖRLÜĞÜ ÜNİVERSİTE HASTANESİ BAŞ MÜDÜRLÜĞÜ", @@ -56,18 +62,20 @@ "announcementDate": "2026-08-27", "tenderDate": "22.09.2026", "tenderTime": "10:30", + "sourceDateTime": "22.09.2026 10:30", + "officialDateSource": "documents/ilan sourceDateTime", + "announcementDateConflict": null, "detailStatus": true, "documentProbeStatus": 200, "documentCount": 0, - "sourceUrl": "https://www.ilan.gov.tr/ilan/2204525/ihale-duyurulari-mal-alim-ihaleleri-laboratuvar-malzemesi-alinacaktir", "announcementCount": 1, "okasCount": 1, - "sourceDateTime": "22.09.2026 10:30", "documentEndpointSuccess": true, - "officialDateSource": "documents/ilan sourceDateTime" + "sourceUrl": "https://www.ilan.gov.tr/ilan/2204525/ihale-duyurulari-mal-alim-ihaleleri-laboratuvar-malzemesi-alinacaktir" }, { "ikn": "2026/1573235", + "scenario": "kismi-teklif", "id": "ilan-2203314", "title": "Laboratuvar sarf malzemeleri satın alınacak", "institution": "AKSARAY ÜNİVERSİTESİ EĞİTİM VE ARAŞTIRMA HASTANESİ", @@ -77,19 +85,20 @@ "announcementDate": "2026-08-24", "tenderDate": "16.09.2026", "tenderTime": "10:00", + "sourceDateTime": "16.09.2026 10:00", + "officialDateSource": "documents/ilan sourceDateTime", + "announcementDateConflict": "ilan listesi 09.09.2026; resmi detay 16.09.2026", "detailStatus": true, "documentProbeStatus": 200, "documentCount": 1, - "sourceUrl": "https://www.ilan.gov.tr/ilan/2203314/ihale-duyurulari-mal-alim-ihaleleri-laboratuvar-sarf-malzemeleri-satin-alinacak", "announcementCount": 1, "okasCount": 1, - "sourceDateTime": "16.09.2026 10:00", "documentEndpointSuccess": true, - "officialDateSource": "documents/ilan sourceDateTime", - "announcementDateConflict": "ilan.gov.tr summary shows 09.09.2026; official detail/document source shows 16.09.2026" + "sourceUrl": "https://www.ilan.gov.tr/ilan/2203314/ihale-duyurulari-mal-alim-ihaleleri-laboratuvar-sarf-malzemeleri-satin-alinacak" }, { "ikn": "2026/1523851", + "scenario": "100-kalem", "id": "ilan-2200563", "title": "Laboratuvar malzemesi satın alınacaktır", "institution": "İZMİR TEPECİK EĞİTİM VE ARAŞTIRMA HASTANESİ", @@ -99,15 +108,16 @@ "announcementDate": "2026-08-21", "tenderDate": "11.09.2026", "tenderTime": "10:00", + "sourceDateTime": "11.09.2026 10:00", + "officialDateSource": "documents/ilan sourceDateTime", + "announcementDateConflict": null, "detailStatus": true, "documentProbeStatus": 200, "documentCount": 1, - "sourceUrl": "https://www.ilan.gov.tr/ilan/2200563/ihale-duyurulari-mal-alim-ihaleleri-laboratuvar-malzemesi-satin-alinacaktir", "announcementCount": 1, "okasCount": 1, - "sourceDateTime": "11.09.2026 10:00", "documentEndpointSuccess": true, - "officialDateSource": "documents/ilan sourceDateTime" + "sourceUrl": "https://www.ilan.gov.tr/ilan/2200563/ihale-duyurulari-mal-alim-ihaleleri-laboratuvar-malzemesi-satin-alinacaktir" } ], "scope": "Live metadata/detail/document endpoint probes only; no authentication, writes, payments, messages, or submissions." diff --git a/package.json b/package.json index 131ba22..4d1a52e 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,8 @@ "qa:live": "node tests/live-runtime-probe.mjs", "preview": "vite preview", "server": "node server/index.js", - "test": "node --test tests/*.test.mjs" + "test": "node --test tests/*.test.mjs", + "qa:live-five": "node tests/live-five-probe.mjs" }, "dependencies": { "canvas-confetti": "^1.9.4", diff --git a/tests/live-five-probe.mjs b/tests/live-five-probe.mjs new file mode 100644 index 0000000..909e66b --- /dev/null +++ b/tests/live-five-probe.mjs @@ -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 })) }));