import { test } from 'node:test'; import assert from 'node:assert/strict'; import { createBriefSender } from '../server/brief-delivery.mjs'; function fixture(overrides={}) { const calls=[]; const prefs=new Map(); const deps={ store:{ listUsers:()=>[{email:'one@example.test'}], getData:(email,key)=>key==='brief'?prefs.get(email):{chatId:'test-chat'} }, buildBrief:()=>({bos:false,hafta:'Test'}),text:()=> 'Test metni',html:()=> '
Test metni
', telegramConfigured:()=>true,emailConfigured:()=>true, sendTelegram:async()=>{calls.push('telegram');return {id:'test-chat:1'};}, sendEmail:async()=>{calls.push('email');return {id:'test-receipt'};}, warn:()=>{}, ...overrides, }; return {calls,prefs,send:createBriefSender(deps)}; } test('manual force cannot bypass weekly brief opt-out', async()=>{ const f=fixture({buildBrief:()=>{throw new Error('Must not build disabled recipient brief');}}); f.prefs.set('one@example.test',{kapali:true}); const result=await f.send(async()=>[],{zorla:true}); assert.equal(result.kapali,1); assert.equal(result.gonderilen,0); assert.equal(result.hata,0); assert.deepEqual(f.calls,[]); }); test('a rejected channel is counted even when the other channel accepts', async()=>{ const f=fixture({sendTelegram:async()=>{throw new Error('Provider unavailable');}}); const result=await f.send(async()=>[]); assert.equal(result.gonderilen,1); assert.equal(result.hata,1); assert.deepEqual(result.kanallar,{telegram:{denenen:1,kabul:0,hata:1},email:{denenen:1,kabul:1,hata:0}}); assert.deepEqual(f.calls,['email']); }); test('false and missing receipts do not become successful sends',async()=>{ const f=fixture({sendTelegram:async()=>false,sendEmail:async()=>({})}); const result=await f.send(async()=>[]); assert.equal(result.gonderilen,0); assert.equal(result.hata,1); assert.equal(result.kanallar.telegram.hata,1); assert.equal(result.kanallar.email.hata,1); }); test('opt-out during Telegram delivery prevents the following email',async()=>{ const f=fixture({sendTelegram:async()=>{f.prefs.set('one@example.test',{kapali:true});return {id:'test-chat:1'};}}); const result=await f.send(async()=>[]); assert.equal(result.gonderilen,1); assert.equal(result.kapali,1); assert.equal(result.kanallar.email.denenen,0); assert.deepEqual(f.calls,[]); }); test('empty brief and unconfigured delivery are distinguished without sending',async()=>{ const empty=fixture({buildBrief:()=>({bos:true})}); assert.equal((await empty.send(async()=>[])).bos,1); assert.deepEqual(empty.calls,[]); const unconfigured=fixture({telegramConfigured:()=>false,emailConfigured:()=>false}); const result=await unconfigured.send(async()=>[]); assert.equal(result.kanalsiz,1); assert.equal(result.gonderilen,0); assert.equal(result.hata,0); assert.deepEqual(unconfigured.calls,[]); }); test('one recipient failure does not skip subsequent recipients; feed failure is propagated',async()=>{ const f=fixture({store:{listUsers:()=>[{email:'bad@example.test'},{email:'good@example.test'}],getData:()=>null}, buildBrief:email=>{if(email==='bad@example.test')throw new Error('Invalid data');return {bos:false,hafta:'Test'};}}); const result=await f.send(async()=>[]); assert.equal(result.kullanici,2); assert.equal(result.hata,1); assert.equal(result.gonderilen,1); assert.deepEqual(f.calls,['email']); await assert.rejects(f.send(async()=>{throw new Error('Feed unavailable');}),/Feed unavailable/); }); test('long brief is sent completely as plain text with each part accounted for',async()=>{ const content=('Ürün