const test=require('node:test'); const assert=require('node:assert/strict'); const fs=require('node:fs'); const path=require('node:path'); const vm=require('node:vm'); const source=fs.readFileSync(path.resolve('work/database-enrichment-20261110/collect.cjs'),'utf8'); async function simulate(httpStatus,size){ const directory=path.resolve('work/test-collector'),files=new Map();let calls=0; const products=Array.from({length:size},(_,i)=>({productId:'jd:'+i,itemId:String(i),platform:'jd',missingReviews:true})); files.set(path.join(directory,'product-queue.json'),JSON.stringify(products)); files.set(path.join(directory,'social-gaps.json'),'[]'); const fakeFs={readFileSync:p=>{if(!files.has(p))throw Error('UNEXPECTED_FILE_READ');return files.get(p);}, writeFileSync:(p,b)=>files.set(p,b),existsSync:p=>files.has(p),mkdirSync(){}, copyFileSync:(a,b)=>files.set(b,files.get(a)),renameSync:(a,b)=>{files.set(b,files.get(a));files.delete(a);}}; const fromVoc=id=>{ if(id.includes('catalog'))return {searchVocApis:async()=>({}),getVocApiDoc:async()=>({status:'ok',data:{endpoint:{method:'GET',channel:'ecommerce',params:[]}}})}; if(id.includes('credentials'))return {readCredentialContext:()=>({newApiToken:'synthetic-test-token'})}; const call=async()=>{calls++;throw Object.assign(Error('SIMULATED_GATEWAY_RESPONSE'),{httpStatus,kind:'upstream'});}; return {callSocialGateway:call,callEcommerceGateway:call}; }; const fakeProcess={argv:['node','collect.cjs','--all'],exitCode:0}; const sandbox={__dirname:directory,Buffer,AbortSignal,Date,Promise,process:fakeProcess, console:{log(){},error(){}},fetch:()=>{throw Error('NETWORK_NOT_ALLOWED_IN_TEST');}, setTimeout:fn=>{fn();return 0;}, require:id=>id==='node:fs'?fakeFs:id==='node:module'?{createRequire:()=>fromVoc}:require(id)}; sandbox.global=sandbox; await vm.runInNewContext(source,sandbox); return {calls,result:JSON.parse(files.get(path.join(directory,'all-collection-result.json'))),exitCode:fakeProcess.exitCode}; } test('terminal gateway failures stop the queue without claiming source deletion',async()=>{ for(const status of [401,402,403]){ const actual=await simulate(status,5000); assert.ok(actual.calls<=6,'Only the active workers may complete'); assert.equal(actual.result.stopReason.code,'UPSTREAM_HTTP_'+status); assert.equal(actual.result.stopReason.sourceRemovalConfirmed,false); assert.ok(actual.result.remainingJobs>4990); assert.equal(actual.exitCode,2); } }); test('a transient upstream failure does not mark all remaining links unavailable',async()=>{ const actual=await simulate(503,8); assert.equal(actual.calls,8); assert.equal(actual.result.stopReason,null); assert.equal(actual.result.failed,8); });