import {test} from 'node:test'; import assert from 'node:assert/strict'; import http from 'node:http'; import {requestLifetime} from '../server/request-lifetime.mjs'; import {analyzeSpec} from '../server/ai.mjs'; import {auditOfferInParts} from '../server/offer-audit-parts.mjs'; import {requestOfferAssistant} from '../src/services/offerAssistant.js'; test('premature real HTTP response closure aborts work after the request body is complete', {timeout:3000},async()=>{ let ready,aborted; const received=new Promise(resolve=>ready=resolve), cancelled=new Promise(resolve=>aborted=resolve); const server=http.createServer((req,res)=>{ const lifetime=requestLifetime(req,res); lifetime.signal.addEventListener('abort',()=>{lifetime.dispose();aborted();},{once:true}); req.resume();req.on('end',()=>ready(lifetime)); }); await new Promise(resolve=>server.listen(0,'127.0.0.1',resolve)); const client=http.request({hostname:'127.0.0.1',port:server.address().port,method:'POST'}); client.on('error',()=>{});client.end('complete request body'); try { const lifetime=await received;assert.equal(lifetime.signal.aborted,false); client.destroy();await cancelled;assert.equal(lifetime.signal.aborted,true); } finally {client.destroy();await new Promise(resolve=>server.close(resolve));} }); test('caller cancellation aborts provider fetch and never triggers fallback',async()=>{ const original=globalThis.fetch,old=process.env.GEMINI_API_KEY; process.env.GEMINI_API_KEY='synthetic-test-only';let calls=0; const c=new AbortController(); try { globalThis.fetch=async(url,{signal})=>new Promise((resolve,reject)=>{ calls++;signal.addEventListener('abort',()=>reject(new DOMException('Aborted','AbortError')),{once:true});c.abort(); }); await assert.rejects(analyzeSpec({text:'Sentetik kaynak',chain:['gemini'],signal:c.signal}),{name:'AbortError'}); assert.equal(calls,1); } finally {globalThis.fetch=original;if(old===undefined)delete process.env.GEMINI_API_KEY;else process.env.GEMINI_API_KEY=old;} }); test('audit cancellation stops remaining batches and is not returned as a partial result',async()=>{ const c=new AbortController();let calls=0; await assert.rejects(auditOfferInParts(['Numune'],'x'.repeat(50000),async()=>{ calls++;c.abort();throw new DOMException('Aborted','AbortError'); },{signal:c.signal}),{name:'AbortError'}); assert.ok(calls<=2); }); test('closing assistant view aborts its pending HTTP fetch',async()=>{ const c=new AbortController();let aborted=false; await assert.rejects(requestOfferAssistant({url:'/api/ai/belge-sor',body:{},readToken:()=>'test',signal:c.signal, fetcher:async(url,{signal})=>new Promise((resolve,reject)=>{ signal.addEventListener('abort',()=>{aborted=true;reject(new DOMException('Aborted','AbortError'));},{once:true});c.abort(); })}),{name:'AbortError'}); assert.equal(aborted,true); });