Pin durable AI source text and requirements across retries

This commit is contained in:
2026-09-06 12:42:16 +03:00
parent 801d4fca8d
commit ca0ed73242
6 changed files with 86 additions and 15 deletions
+38
View File
@@ -45,3 +45,41 @@ test('runner stores results independently of an HTTP connection',async()=>{
assert.equal(jobs.getAiJob('runner',job.id).result.cevap,'Stored answer');
} finally {runner.stop();}
});
test('resolved source survives retry and preserves original idempotency and ownership',async()=>{
const {resolveAiJobInput}=await import('../server/ai-job-source.mjs');
const original={ikn:'2026/99',soru:'teslim',resolvedSource:{text:'untrusted'}};
const created=jobs.createAiJob('source-owner','request-source','belge-sor',original);
const worker=jobs.claimAiJob();let calls=0;
const resolved=await resolveAiJobInput(worker,async()=>{calls++;return {text:'Source A',kaynak:'ilan metni'};},jobs.pinAiJobInput,undefined,()=>['Kalem A']);
assert.deepEqual(resolved.gereklilikler,['Kalem A']);assert.equal(resolved.text,'Source A');assert.equal(resolved.resolvedSource.kaynak,'ilan metni');
assert.equal(resolved.resolvedSource.hash.length,64);
assert.equal(jobs.pinAiJobInput(worker.id,worker.runId,{text:'replacement'}),false);
jobs.finishAiJob(worker.id,worker.runId,null,'provider failed');
assert.equal(jobs.createAiJob('source-owner','request-source','belge-sor',original).id,created.id);
assert.equal(jobs.getAiJob('other-owner',created.id),null);
assert.equal(jobs.retryAiJob('source-owner',created.id),true);
const next=jobs.claimAiJob();
const reused=await resolveAiJobInput(next,async()=>{calls++;return {text:'Source B'};},jobs.pinAiJobInput);
assert.equal(calls,1);assert.deepEqual(reused,resolved);
assert.deepEqual(jobs.getAiJob('source-owner',created.id).input,resolved);
jobs.cancelAiJob('source-owner',created.id);
});
test('cancel during source fetch rejects late source, and empty resolution remains explicit',async()=>{
const {resolveAiJobInput}=await import('../server/ai-job-source.mjs');
const created=jobs.createAiJob('source-owner','request-cancel-source','teklif-taslak',{});
const worker=jobs.claimAiJob();
await assert.rejects(resolveAiJobInput(worker,async()=>{
jobs.cancelAiJob('source-owner',created.id);return {text:'late source'};
},jobs.pinAiJobInput),/artık etkin değil/);
assert.deepEqual(jobs.getAiJob('source-owner',created.id).input,{});
const empty=jobs.createAiJob('source-owner','request-empty-source','teklif-taslak',{});
const emptyWorker=jobs.claimAiJob();
const input=await resolveAiJobInput(emptyWorker,async()=>({text:'',kaynak:null}),jobs.pinAiJobInput);
assert.equal(input.resolvedSource.text,'');assert.equal(input.resolvedSource.kaynak,null);
jobs.finishAiJob(empty.id,emptyWorker.runId,null,'failed');jobs.retryAiJob('source-owner',empty.id);
const retry=jobs.claimAiJob();
assert.deepEqual(await resolveAiJobInput(retry,()=>assert.fail('must not refetch'),jobs.pinAiJobInput),input);
jobs.cancelAiJob('source-owner',empty.id);
});