Cancel disconnected offer analysis and stop remaining provider calls
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
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);
|
||||
});
|
||||
Reference in New Issue
Block a user