import {test} from 'node:test'; import assert from 'node:assert/strict'; import {qualifyOfferDraft} from '../server/offer-draft.mjs'; const requested=['Kapak','Teknik','Ekler']; test('missing sections are explicit editable placeholders, not a complete dossier',()=>{ const out=qualifyOfferDraft({bolumler:[{baslik:'Kapak',icerik:'Kapak metni'}]},requested); assert.equal(out.draftReview.status,'partial'); assert.deepEqual(out.draftReview.missingSections,['Teknik','Ekler']); assert.match(out.bolumler[1].icerik,/DOLDURUN/);assert.equal(out.bolumler[0].icerik,'Kapak metni'); }); test('long sections retain their last conditions and requested section order',()=>{ const text='Teknik şart '.repeat(1500)+'SON KOŞUL: Numune zorunludur.'; const out=qualifyOfferDraft({bolumler:[{baslik:'Ekler',icerik:'Ekler metni'},{baslik:'Teknik',icerik:text},{baslik:'Kapak',icerik:'Kapak'}]},requested); assert.deepEqual(out.bolumler.map(row=>row.baslik),requested); assert.equal(out.bolumler[1].icerik,text);assert.equal(out.draftReview.status,'draft'); }); test('unexpected content is preserved but never substitutes for a requested section',()=>{ const out=qualifyOfferDraft({bolumler:[{baslik:'Başka bölüm',icerik:'Ek içerik'}]},requested); assert.equal(out.draftReview.status,'partial');assert.equal(out.draftReview.missingSections.length,3); assert.equal(out.bolumler.at(-1).icerik,'Ek içerik'); }); test('malformed or repeated sections cannot silently overwrite user-visible content',()=>{ for(const bolumler of [[],[null],[{baslik:'Kapak',icerik:{text:'invalid'}}],[{baslik:'Kapak',icerik:'a'},{baslik:'Kapak',icerik:'b'}]]) assert.throws(()=>qualifyOfferDraft({bolumler},requested)); }); const {draftOfferInParts}=await import('../server/offer-draft-parts.mjs'); test('long draft input processes every character and retains late section text in deterministic order',async()=>{ const source='Kalem ve firma şartları. '.repeat(2100)+'SON KALEM 101: cihaz kurulum yükümlülüğü'; const seen=[]; const out=await draftOfferInParts(source,requested,async(part)=>{ seen.push(part); return {bolumler:requested.map(baslik=>({baslik,icerik:part.text}))}; }); assert.equal(seen.sort((a,b)=>a.index-b.index).map(p=>p.text).join(''),source); assert.ok(seen.every(p=>p.text.length<=16000)); assert.match(out.bolumler[1].icerik,/SON KALEM 101/); assert.equal(out.draftReview.status,'partial'); assert.equal(out.draftReview.parts.at(-1).end,source.length); }); test('failed draft parts remain explicit, not silently omitted or marked complete',async()=>{ const out=await draftOfferInParts('X'.repeat(40000),requested,async part=>{ if(part.index===1)throw new Error('provider unavailable'); return {bolumler:requested.map(baslik=>({baslik,icerik:'Useful draft'}))}; }); assert.deepEqual(out.draftReview.failedParts,[2]); assert.equal(out.draftReview.parts[1].status,'failed'); assert.match(out.bolumler[0].icerik,/üretilemedi/); assert.equal(out.draftReview.status,'partial'); }); test('draft cancellation stops subsequent batches',async()=>{ const controller=new AbortController();let calls=0; await assert.rejects(draftOfferInParts('X'.repeat(80000),requested,async()=>{ calls++;controller.abort();return {bolumler:[{baslik:'Kapak',icerik:'draft'}]}; },{signal:controller.signal}),{name:'AbortError'}); assert.ok(calls<=2); });