import test,{after} from 'node:test'; import assert from 'node:assert/strict'; import {mkdtempSync,rmSync} from 'node:fs'; import {join} from 'node:path'; import {tmpdir} from 'node:os'; const dir=mkdtempSync(join(tmpdir(),'ihaleist-jobs-'));process.env.IHALEIST_USER_DATA_DIR=dir; const jobs=await import('../server/ai-job-store.mjs'); after(()=>rmSync(dir,{recursive:true,force:true})); test('durable jobs are idempotent and account scoped',()=>{ const first=jobs.createAiJob('a','request-1','teklif-denetim',{ikn:'2026/1',taslak:'private'}); assert.equal(jobs.createAiJob('a','request-1','teklif-denetim',{ikn:'2026/1',taslak:'private'}).id,first.id); assert.throws(()=>jobs.createAiJob('a','request-1','teklif-denetim',{taslak:'other'}),/farklı kaynak/); assert.equal(jobs.getAiJob('b',first.id),null);assert.deepEqual(jobs.listAiJobs('b'),[]); assert.equal(jobs.cancelAiJob('b',first.id),false); const claimed=jobs.claimAiJob();assert.equal(claimed.input.taslak,'private'); assert.equal(jobs.finishAiJob(first.id,'wrong',{success:true}),false); assert.equal(jobs.finishAiJob(first.id,claimed.runId,{success:true,answer:'done'}),true); assert.equal(jobs.getAiJob('a',first.id).result.answer,'done'); assert.equal(jobs.getAiJob('a',first.id).input.taslak,'private'); assert.equal(jobs.listAiJobs('a')[0].input,undefined); }); test('lost workers become interrupted without automatic provider replay; retry and cancellation reject late results',()=>{ const first=jobs.createAiJob('a','request-2','teklif-taslak',{ikn:'2026/2'}); const claimed=jobs.claimAiJob(Date.now()-120000); assert.equal(jobs.claimAiJob(),null); assert.equal(jobs.getAiJob('a',first.id).state,'interrupted'); assert.equal(jobs.finishAiJob(first.id,claimed.runId,{success:true}),false); assert.equal(jobs.retryAiJob('a',first.id),true); const restarted=jobs.claimAiJob();assert.notEqual(restarted.runId,claimed.runId); assert.equal(jobs.cancelAiJob('a',first.id),true); assert.equal(jobs.heartbeatAiJob(first.id,restarted.runId),false); assert.equal(jobs.finishAiJob(first.id,restarted.runId,{success:true}),false); assert.equal(jobs.getAiJob('a',first.id).state,'cancelled'); }); test('runner stores results independently of an HTTP connection',async()=>{ const {startAiJobRunner}=await import('../server/ai-job-runner.mjs'); const job=jobs.createAiJob('runner','request-run','belge-sor',{ikn:'2026/8',soru:'teslim'}); let finish; const runner=startAiJobRunner(async()=>new Promise(resolve=>{finish=resolve;}),{intervalMs:100000}); try { runner.wake();assert.equal(jobs.getAiJob('runner',job.id).state,'running'); finish({success:true,cevap:'Stored answer'}); await new Promise(resolve=>setImmediate(resolve)); assert.equal(jobs.getAiJob('runner',job.id).result.cevap,'Stored answer'); } finally {runner.stop();} });