Files
ihaleist/tests/notifier-delivery.test.mjs

74 lines
5.4 KiB
JavaScript
Raw Permalink 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.
import test from 'node:test';
import assert from 'node:assert/strict';
import {bildirimleriIsle,bildirilebilir} from '../server/notifier.mjs';
const future={id:'future-tender',tarih:'10.09.2026',saat:'10:00',durum:'Açık',baslik:'Laboratuvar'};
const now=()=>new Date('2026-09-06T10:00:00Z');
test('notification eligibility excludes expired cancelled and unknown deadlines',()=>{
assert.equal(bildirilebilir(future,now()),true);
for(const patch of [{durum:'İptal'},{durum:'Sonuçlandı'},{id:'hist-1'},{tarih:'01.09.2026'},{saat:''},{tarih:''}])assert.equal(bildirilebilir({...future,...patch},now()),false);
});
test('delivery failure retains pending items and batches acknowledge only included tenders',async()=>{
const rows=Array.from({length:28},(_,i)=>({...future,id:'t-'+i,ikn:'2026/'+i,baslik:'Laboratuvar '+i}));
const subscription={id:1,email:'qa@example.test',initialized:true,sonGorulenler:[]};
const sent=[],messages=[];let fail=true;
const dependencies={now,configured:()=>true,telegramReady:()=>false,readSubscriptions:()=>[subscription],reserve:(id,payload,ids)=>({id:'test-delivery',payload,tenderIds:ids}),accepted:delivery=>{sent.push(delivery.tenderIds);subscription.sonGorulenler.push(...delivery.tenderIds);},failed:()=>{},deliverEmail:async message=>{if(fail)throw new Error('provider unavailable');messages.push(message);return {id:'provider-test'};}};
assert.equal((await bildirimleriIsle(async()=>({tenders:rows}),{},dependencies)).hata,1);assert.equal(sent.length,0);
fail=false;assert.equal((await bildirimleriIsle(async()=>({tenders:rows}),{},dependencies)).gonderilen,1);assert.equal(sent[0].length,25);assert.match(messages[0].html,/>Laboratuvar 24</);assert.doesNotMatch(messages[0].html,/>Laboratuvar 25</);
await bildirimleriIsle(async()=>({tenders:rows}),{},dependencies);assert.equal(sent[1].length,3);assert.match(messages[1].html,/>Laboratuvar 27</);
await bildirimleriIsle(async()=>({tenders:rows}),{},dependencies);assert.equal(sent.length,2);
});
test('baseline is distinct from delivery and overlapping turns do not send twice',async()=>{
let release;const pending=new Promise(resolve=>release=resolve);let baseline=0;
const dependencies={now,configured:()=>true,telegramReady:()=>false,readSubscriptions:()=>[{id:1,initialized:false}],saveBaseline:()=>baseline++,deliverEmail:()=>assert.fail('baseline must not send')};
const first=bildirimleriIsle(async()=>{await pending;return {tenders:[future]};},{},dependencies);
assert.equal((await bildirimleriIsle(()=>assert.fail(),{},dependencies)).atlandi,'önceki tur sürüyor');
release();await first;assert.equal(baseline,1);
});
test('unreadable source never establishes baseline or touches delivery state, and next turn recovers',async()=>{
let baseline=0;
const deps={now,configured:()=>true,readSubscriptions:()=>[{id:1,initialized:false}],
saveBaseline:()=>baseline++,reserve:()=>assert.fail('invalid source must not reserve'),
accepted:()=>assert.fail('invalid source must not acknowledge'),deliverEmail:()=>assert.fail('must not deliver')};
for(const payload of [null,{}, {success:false,tenders:[]},{tenders:{}},{tenders:[null]},
{tenders:[{...future,id:''}]},{tenders:[{...future,baslik:''}]},
{tenders:[future,future]},{tenders:[{...future,items:{}}]},
{tenders:[{...future,items:[null]}]}]) {
await assert.rejects(bildirimleriIsle(async()=>payload,{},deps),/kaynağı geçersiz/);
assert.equal(baseline,0);
}
await bildirimleriIsle(async()=>({success:true,tenders:[]}),{},deps);
assert.equal(baseline,1,'a valid empty feed is distinct from an unreadable feed');
});
test('subscription criteria remain plain text in outgoing digest HTML',async()=>{
let message;
const criteria='<a href="https://example.test">Laboratuvar</a>';
const deps={now,configured:()=>true,telegramReady:()=>false,
readSubscriptions:()=>[{id:1,email:'qa@example.test',initialized:true,anahtar:criteria}],
reserve:(id,payload,tenderIds)=>({id:'escaped-test',payload,tenderIds}),accepted:()=>{},failed:()=>{},
deliverEmail:async payload=>{message=payload;return {id:'local-test'};}};
await bildirimleriIsle(async()=>({tenders:[{...future,baslik:criteria}]}),{},deps);
assert.ok(message);
assert.ok(message.html.includes('Kriteriniz: Anahtar: &lt;a href=&quot;https://example.test&quot;&gt;Laboratuvar&lt;/a&gt;'));
assert.ok(!message.html.includes(criteria));
assert.ok(message.subject.includes(criteria),'the non-HTML subject retains the literal criterion');
});
test('pending digest is held before provider replay when its deadline or eligibility changed',async()=>{
const pinned={...future};
for(const current of [[],[{...future,durum:'İptal'}],[{...future,tarih:'01.09.2026'}],
[{...future,saat:''}],[{...future,tarih:'11.09.2026'}]]) {
let held=0;
const delivery={id:'pending',lease:'test',tenderIds:[future.id],payload:{digest:[pinned],html:'original'}};
const deps={now,configured:()=>true,telegramReady:()=>false,
readSubscriptions:()=>[{id:1,email:'qa',initialized:true,deliveryPending:true}],reserve:()=>delivery,
hold:(job,reason)=>{assert.equal(job,delivery);assert.match(reason,/incelemesi/);held++;},
accepted:()=>assert.fail('must not mark as delivered'),deliverEmail:()=>assert.fail('must not replay stale email')};
const result=await bildirimleriIsle(async()=>({tenders:current}),{},deps);
assert.equal(held,1);assert.equal(result.hata,1);assert.equal(result.gonderilen,0);
assert.equal(delivery.payload.html,'original');
}
});