Verify notification failure recovery and persisted read state in browser
This commit is contained in:
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"checks": {
|
||||||
|
"listFailure": true,
|
||||||
|
"twoUnreadAfterLoad": true,
|
||||||
|
"failedAckPreservesUnread": true,
|
||||||
|
"successfulAck": true,
|
||||||
|
"persistedAfterReload": true,
|
||||||
|
"noDuplicates": true
|
||||||
|
},
|
||||||
|
"scope": "Local browser, seeded synthetic notifications, real API and SQLite; injected read/write 503; no external delivery",
|
||||||
|
"snapshot": "- banner:\n - text: İhale\n - generic: ist\n - textbox \"İhale, kurum veya İKN no ile arayın...\"\n - button \"ARA\":\n - generic: ARA\n - button \"ÜRÜN ARA\":\n - generic: ÜRÜN ARA\n - button \"Detaylı İhale Arama\":\n - button \"AI Şartname Analizi\":\n - generic: AI Şartname Analizi\n - button \"Web Siteni Tara (AI)\":\n - generic: Web Siteni Tara (AI)\n - generic: 7 gün\n - button \"Bildirimler\" [active]:\n - heading \"Bildirimler\" [level=4]\n - button \"Tümünü Okundu İşaretle\" [disabled]\n - generic: QA tarih değişikliği\n - generic: 06.09.2026 12:52:45\n - paragraph: \"Sentetik bildirim: ihale tarihi güncellendi.\"\n - generic: QA sonuç bildirimi\n - generic: 06.09.2026 12:52:45\n - paragraph: Sentetik sonuç inceleme bildirimi.\n - button \"Hesap menüsü\":\n - generic: L\n - generic: Laboratuvar\n- generic: \"5\"\n- generic: Canlı İhale Havuzu\n- generic: \"5\"\n- generic: Yüklü İhale\n- generic: \"0\"\n- generic: Bugün Eklenen\n- generic: \"5\"\n- generic: Kalem\n- generic: \"0\"\n- generic: Sonuç Arşivi\n- generic: \"0\"\n- generic: Takibimdeki\n- button \"Sonraki sekmeler\":\n- button \"Ana İhale Listesi\":\n - generic: Ana İhale Listesi\n- button \"Günlük Bülten\":\n - generic: Günlük Bülten\n- button \"Sonuç Sorgula\":\n - generic: Sonuç Sorgula\n- button \"İhale Takvimi\":\n - generic: İhale Takvimi\n- button \"Sözleşme Sorgula\":\n - generic: Sözleşme Sorgula\n- button \"KDV Hesapla\":\n - generic: KDV Hesapla\n- button \"Sınır Değer\":\n - generic: Sınır Değer\n- button \"Fiyat Analizi\":\n - generic: Fiyat Analizi\n- button \"Kanban Pano Sürükle Takip\":\n - generic: Kanban Pano\n - generic: Sürükle Takip\n- button \"Rakip Radar Canlı Uyar\":\n - generic: Rakip Radar\n - generic: Canlı Uyar\n- button \"Doküman Deposu\":\n - generic: Doküman Deposu\n- button \"Analitik Dashboard\":\n - generic: Analitik Dashboard\n- button \"Kırmızı Bayrak Şeffaflık\":\n - generic: Kırmızı Bayrak\n - generic: Şeffaflık\n- button \"KİK Kurul Kararları\":\n - generic: KİK Kurul Kararları\n- button \"AB İhaleleri (TED)\":\n - generic: AB İhaleleri (TED)\n- button \"Planlar\":\n - generic: Planlar\n- button \"API & Webhooks\":\n - generic: API & Webhooks\n- button \"Mobil PWA\":\n - generic: Mobil PWA\n- main:\n - paragraph: AKTİF İHALELER\n - paragraph: \"5\"\n - paragraph: BUGÜN EKLENENLER\n - paragraph: \"0\"\n - paragraph: TAKİP ETTİKLERİM\n - paragraph: \"0\"\n - generic \"İlk ziyaretiniz — bugün yayımlanan ilanlar gösteriliyor\":\n - paragraph: SON GİRİŞTEN BERİ\n - paragraph: \"0\"\n - paragraph: İNCELENMEMİŞ\n - paragraph: \"5\"\n - complementary:\n "
|
||||||
|
}
|
||||||
@@ -774,3 +774,9 @@ Taslak türü seçicisine erişilebilir ad eklendi. Üretim derlemesi geçti. Ö
|
|||||||
- Gerçek okundu düğmesi sunucu yanıtından sonra yakalanan bildirim kümesini günceller; hata/kısmi yanıtta yerel durum değişmez. İşlem sırasında gelen yeni kayıtlar yanlışlıkla işaretlenmez. Hesap değişimi ve geç eski okuma yanıtları atılır.
|
- Gerçek okundu düğmesi sunucu yanıtından sonra yakalanan bildirim kümesini günceller; hata/kısmi yanıtta yerel durum değişmez. İşlem sırasında gelen yeni kayıtlar yanlışlıkla işaretlenmez. Hesap değişimi ve geç eski okuma yanıtları atılır.
|
||||||
- Sunucu okundu işlemi liste tip/boyut doğrulamasıyla tek transaction'da çalışır; yabancı hesap kayıtlarına erişemez. Desteklenmeyen HTTP metotları 405 döner; depoya erişilememesi boş liste gibi gösterilmez.
|
- Sunucu okundu işlemi liste tip/boyut doğrulamasıyla tek transaction'da çalışır; yabancı hesap kayıtlarına erişemez. Desteklenmeyen HTTP metotları 405 döner; depoya erişilememesi boş liste gibi gösterilmez.
|
||||||
- 173 test ve üretim derlemesi başarılı. Yeni testler kimliklerin/okunmamış durumun korunmasını, kayıt hatası/kısmi yanıtı ve işlem kümesini doğrular. Menü hata→tekrar deneme ve sayfa yenileme sonrası okundu davranışının tarayıcı kabulü henüz yapılmadı. Dış push/e-posta teslimi bu testlerin kapsamı dışındadır.
|
- 173 test ve üretim derlemesi başarılı. Yeni testler kimliklerin/okunmamış durumun korunmasını, kayıt hatası/kısmi yanıtı ve işlem kümesini doğrular. Menü hata→tekrar deneme ve sayfa yenileme sonrası okundu davranışının tarayıcı kabulü henüz yapılmadı. Dış push/e-posta teslimi bu testlerin kapsamı dışındadır.
|
||||||
|
|
||||||
|
### Bildirim menüsü tarayıcı kabulü — 6 Eylül 2026
|
||||||
|
- 5180 yerel QA ortamında iki sentetik bildirim gerçek takip.sqlite deposuna yazıldı; normal test hesabıyla Header menüsü kullanıldı.
|
||||||
|
- GET 503 → görünür hata → yeniden yükle → iki okunmamış kayıt; POST 503 → hata ve okunmamış durumların korunması; hatayı kaldır → açık okundu işlemi → sayfayı yeniden yükle → iki kalıcı okunmuş kayıt ve yinelenmeme doğrulandı.
|
||||||
|
- Kanıt: `qa-notification-acknowledgement.json`, 6 kontrol doğru. Test altyapısına yalnız localhost hata kontrol sayfası ve sentetik kayıt eklendi; üretim kodu bu adımda değişmedi, önceki 173 test/build sonucu yeniden çalıştırılmadı.
|
||||||
|
- Bu kabul gerçek bildirim API/deposunu kullanır; dış e-posta, Telegram veya işletim sistemi push teslimi testi değildir. Planın diğer kabul maddeleri açık.
|
||||||
|
|||||||
@@ -29,7 +29,14 @@ process.env.IHALEIST_SHARED_DATA_DIR=join(dir,'shared');
|
|||||||
const {observeTenders}=await import('../server/tender-history.mjs');
|
const {observeTenders}=await import('../server/tender-history.mjs');
|
||||||
observeTenders(tenders);
|
observeTenders(tenders);
|
||||||
for(let revision=1;revision<=22;revision++) observeTenders([{...tenders[0],saat:revision%2?'11:00':'10:00'}]);
|
for(let revision=1;revision<=22;revision++) observeTenders([{...tenders[0],saat:revision%2?'11:00':'10:00'}]);
|
||||||
|
const qaNotifications=await import('../server/zeyilname.mjs');
|
||||||
|
qaNotifications.bildirimYaz({email:user.email,tur:'zeyilname',ikn:'2026/1582991',baslik:'QA tarih değişikliği',metin:'Sentetik bildirim: ihale tarihi güncellendi.'});
|
||||||
|
qaNotifications.bildirimYaz({email:user.email,tur:'sonuc',ikn:'2026/1534665',baslik:'QA sonuç bildirimi',metin:'Sentetik sonuç inceleme bildirimi.'});
|
||||||
|
let notificationFailure='read';
|
||||||
const ui=await createServer({server:{host:'127.0.0.1',port:0,proxy:{'/api':{target:'http://127.0.0.1:'+port}}},plugins:[{name:'qa-fixtures',configureServer(server){server.middlewares.use(async(req,res,next)=>{
|
const ui=await createServer({server:{host:'127.0.0.1',port:0,proxy:{'/api':{target:'http://127.0.0.1:'+port}}},plugins:[{name:'qa-fixtures',configureServer(server){server.middlewares.use(async(req,res,next)=>{
|
||||||
|
if(req.url==='/qa-notification-controls') {res.setHeader('Content-Type','text/html; charset=utf-8');res.end(`<html lang="tr"><h1>Yerel bildirim testi</h1><button id="read">Liste hatası</button><button id="write">Okundu kayıt hatası</button><button id="none">Hataları kapat</button><p role="status"></p><script>for(const mode of ['read','write','none'])document.getElementById(mode).onclick=async()=>{const r=await fetch('/qa-notification-failure?mode='+mode,{method:'POST'});document.querySelector('[role=status]').textContent=await r.text();};</script></html>`);return;}
|
||||||
|
if(req.url?.startsWith('/qa-notification-failure?')&&req.method==='POST'){notificationFailure=new URL(req.url,'http://localhost').searchParams.get('mode');res.end('Bildirim test durumu: '+notificationFailure);return;}
|
||||||
|
if(req.url==='/api/me/bildirimler'&&((notificationFailure==='read'&&req.method==='GET')||(notificationFailure==='write'&&req.method==='POST'))){res.writeHead(503,{'Content-Type':'application/json'});res.end(JSON.stringify({success:false,error:'QA: Bildirim deposuna erişilemiyor.'}));return;}
|
||||||
if(req.url==='/qa-ai-controls') {res.setHeader('Content-Type','text/html; charset=utf-8');res.end(`<html lang="tr"><h1>Yerel AI işi testi</h1><p>Sağlayıcı taklidi kullanılır; ücretli AI çağrısı yapılmaz.</p><button id="finish">Bekleyen QA işini tamamla</button><p role="status"></p><script>document.querySelector('#finish').onclick=async()=>{const r=await fetch('/qa-ai-complete',{method:'POST'});document.querySelector('[role=status]').textContent=await r.text();};</script></html>`);return;}
|
if(req.url==='/qa-ai-controls') {res.setHeader('Content-Type','text/html; charset=utf-8');res.end(`<html lang="tr"><h1>Yerel AI işi testi</h1><p>Sağlayıcı taklidi kullanılır; ücretli AI çağrısı yapılmaz.</p><button id="finish">Bekleyen QA işini tamamla</button><p role="status"></p><script>document.querySelector('#finish').onclick=async()=>{const r=await fetch('/qa-ai-complete',{method:'POST'});document.querySelector('[role=status]').textContent=await r.text();};</script></html>`);return;}
|
||||||
if(req.url==='/qa-ai-complete' && req.method==='POST') {
|
if(req.url==='/qa-ai-complete' && req.method==='POST') {
|
||||||
for(const job of pendingQaJobs.values()) {
|
for(const job of pendingQaJobs.values()) {
|
||||||
|
|||||||
Reference in New Issue
Block a user