// Runs actual submission code in a JS sandbox; no browser, network or database. const test = require('node:test'); const assert = require('node:assert/strict'); const {harness,response,scopedKey} = require('./helpers/arr_ui_harness.cjs'); const refusal=()=>response(409,null,'REPLAY_DATE_NOT_AVAILABLE'); test('definite unavailable source date clears only the refused intent and permits correction',async()=>{ const h=harness(()=>refusal()); await h.submit(); assert.equal(h.calls.length,1); assert.equal(h.state.arrDownloadIntent,null); assert.equal(h.storage.has('test'),false); assert.equal(h.state.arrDownloadTask.status,'date_unavailable'); assert.equal(h.element('#arr-download-date').disabled,false); assert.equal(h.element('#arr-download-date').value,'2026-09-16'); assert.equal(h.element('#arr-download-button').disabled,true); }); test('old not_received intent is released only after the explicit source-date refusal',async()=>{ const h=harness(()=>refusal()); const intent={request_id:'a'.repeat(32),report_date:'2026-09-16'}; h.rememberARRIntent(intent); h.state.arrDownloadTask={...intent,status:'not_received',can_retry:false}; await h.submit(); assert.equal(JSON.parse(h.calls[0].body).request_id,intent.request_id); assert.equal(h.state.arrDownloadIntent,null); assert.equal(h.element('#arr-download-date').disabled,false); }); test('lost response and 404 keep original identity for explicit resubmission',async()=>{ let posts=0; const h=harness((url,options)=>{ if(options.method==='POST') { posts++; if(posts===1) throw new TypeError('network lost'); return response(202,{...JSON.parse(options.body),status:'queued',job_id:null,can_retry:false}); } return response(404,null,'ARR_DOWNLOAD_NOT_FOUND'); }); await h.submit(); const first=JSON.parse(h.calls[0].body); assert.equal(h.state.arrDownloadTask.status,'not_received'); assert.equal(h.state.arrDownloadIntent.request_id,first.request_id); assert.equal(h.element('#arr-download-date').disabled,false); h.element('#arr-download-date').value='2026-09-17'; h.handleARRDownloadDateChange(); await h.submit(); assert.deepEqual(JSON.parse(h.calls[2].body),first); assert.equal(posts,2); assert.equal(h.state.arrDownloadTask.status,'queued'); assert.equal(h.element('#arr-download-date').value,'2026-09-17'); }); test('other conflict codes never discard an uncertain request',async()=>{ const h=harness((url,options)=>options.method==='POST' ? response(409,null,'ARR_DOWNLOAD_DATE_CONFLICT'):response(404,null,'ARR_DOWNLOAD_NOT_FOUND')); await h.submit(); const first=JSON.parse(h.calls[0].body); assert.equal(h.state.arrDownloadIntent.request_id,first.request_id); assert.equal(h.element('#arr-download-date').disabled,false); h.element('#arr-download-date').value='2026-09-17'; h.handleARRDownloadDateChange(); assert.equal(h.state.arrDownloadIntent.request_id,first.request_id); }); test('retry endpoint errors retain an existing interrupted task identity',async()=>{ const h=harness((url,options)=>options.method==='POST'?refusal():response(503,null,'ARR_DOWNLOAD_UNAVAILABLE')); const intent={request_id:'b'.repeat(32),report_date:'2026-09-15'}; h.rememberARRIntent(intent); h.state.arrDownloadTask={...intent,status:'interrupted',can_retry:true}; h.handleARRDownloadDateChange(); await h.submit(); assert.equal(h.calls[0].url,`/api/arr-downloads/${intent.request_id}/retry`); assert.equal(h.state.arrDownloadIntent.request_id,intent.request_id); assert.equal(h.element('#arr-download-date').disabled,false); assert.equal(h.element('#arr-download-date').value,'2026-09-16'); }); test('selecting a next date during a running task survives polling without submitting or replacing that task',async()=>{ const h=harness(()=>{throw new Error('No request expected');}); const task={request_id:'c'.repeat(32),report_date:'2026-09-15',status:'downloading',can_retry:false}; await h.acceptARRDownloadTask(task,{sync:false}); assert.equal(h.element('#arr-download-date').disabled,false); assert.equal(h.element('#arr-date-toggle').disabled,false); h.element('#arr-download-date').value='2026-09-16'; h.handleARRDownloadDateChange(); await h.acceptARRDownloadTask({...task,status:'processing'},{sync:false}); await h.submit(); assert.equal(h.calls.length,0); assert.equal(h.element('#arr-download-date').value,'2026-09-16'); assert.equal(h.element('#arr-download-button').disabled,true); assert.equal(h.state.arrDownloadIntent.request_id,task.request_id); assert.equal(JSON.parse(h.storage.get('test')).report_date,'2026-09-15'); assert.match(h.element('#arr-download-status').textContent,/2026-09-15/); }); for (const status of ['succeeded','failed']) { test(`the next selected date is only submitted explicitly after ${status}`,async()=>{ const h=harness((url,options)=>response(202,{...JSON.parse(options.body),status:'queued',can_retry:false})); const task={request_id:'d'.repeat(32),report_date:'2026-09-15',status:'downloading',can_retry:false}; await h.acceptARRDownloadTask(task,{sync:false}); h.element('#arr-download-date').value='2026-09-16'; h.handleARRDownloadDateChange(); await h.acceptARRDownloadTask({...task,status,can_retry:status==='failed'},{sync:false}); assert.equal(h.calls.length,0); assert.equal(h.element('#arr-download-button').disabled,false); assert.equal(h.element('#arr-download-button').textContent,'arr_download.start'); await h.submit(); assert.equal(h.calls.length,1); assert.equal(h.calls[0].url,'/api/arr-downloads'); const submitted=JSON.parse(h.calls[0].body); assert.equal(submitted.report_date,'2026-09-16'); assert.notEqual(submitted.request_id,task.request_id); }); } test('editing before status loads still restores the running task and retains the selected date',async()=>{ const task={request_id:'e'.repeat(32),report_date:'2026-09-15',status:'downloading',can_retry:false}; const h=harness(()=>response(200,{context_id:'production',ready:true,default_date:'2026-09-17',latest_task:task})); h.handleARRDownloadDateChange(); await h.initARRDownload(); assert.equal(h.state.arrDownloadTask.request_id,task.request_id); assert.equal(h.element('#arr-download-date').value,'2026-09-16'); assert.equal(h.element('#arr-download-button').disabled,true); assert.equal(h.calls.length,1); assert.equal(h.calls[0].method,'GET'); }); test('config is fetched first and only this instance and user may restore an intent',async()=>{ const intent={request_id:'f'.repeat(32),report_date:'2026-10-07'}; const h=harness((url)=>{ if(url==='/api/arr-downloads') { assert.equal(h.storageReads.length,0,'identity must be fetched before storage is read'); return response(200,{context_id:'production',ready:true,default_date:'2026-10-06'}); } assert.equal(url,`/api/arr-downloads/${intent.request_id}`); return response(200,{...intent,status:'downloading',can_retry:false}); }); h.storage.set(scopedKey(),JSON.stringify(intent)); h.storage.set(scopedKey('sandbox'),JSON.stringify({...intent,request_id:'a'.repeat(32)})); h.storage.set(scopedKey('production','another-user'),JSON.stringify({...intent,request_id:'b'.repeat(32)})); h.storage.set('arr:last-download:operator',JSON.stringify({...intent,report_date:'2026-09-15'})); await h.initARRDownload(); assert.deepEqual(h.storageReads,[scopedKey()]); assert.equal(h.state.arrDownloadTask.request_id,intent.request_id); assert.equal(h.element('#arr-download-date').value,intent.report_date); assert.equal(h.calls.every(call=>call.method==='GET'),true); }); test('legacy or another context cannot replace default date with an old failed task',async()=>{ const old={request_id:'a'.repeat(32),report_date:'2026-09-15',status:'failed',can_retry:true}; const h=harness(()=>response(200,{context_id:'production',ready:true,default_date:'2026-10-07',latest_task:old})); h.storage.set('arr:last-download:operator',JSON.stringify(old)); h.storage.set(scopedKey('sandbox'),JSON.stringify(old)); await h.initARRDownload(); assert.equal(h.state.arrDownloadTask,null); assert.equal(h.state.arrDownloadIntent,null); assert.equal(h.element('#arr-download-date').value,'2026-10-07'); assert.deepEqual(h.storageReads,[scopedKey()]); assert.equal(h.calls.length,1); }); test('config failure does not read any saved request before identity is known',async()=>{ const h=harness(()=>{throw new TypeError('offline');}); h.storage.set('arr:last-download:operator',JSON.stringify({request_id:'a'.repeat(32),report_date:'2026-09-15'})); await h.initARRDownload(); assert.equal(h.storageReads.length,0); assert.equal(h.state.arrDownloadIntent,null); assert.equal(h.state.arrDownloadRestored,false); }); test('an unavailable instance without an identity stays unavailable without restoring storage',async()=>{ const h=harness(()=>response(200,{ready:false,context_id:null,default_date:'2026-10-07'})); await h.initARRDownload(); assert.equal(h.storageReads.length,0); assert.equal(h.state.arrDownloadReady,false); assert.equal(h.state.arrDownloadConfigDisconnected,false); assert.equal(h.element('#arr-download-date').value,'2026-10-07'); assert.equal(h.element('#arr-download-button').disabled,true); }); test('a running task from config preselects its date and only scoped terminal intent restores later',async()=>{ const task={request_id:'a'.repeat(32),report_date:'2026-10-07',status:'processing',can_retry:false}; const h=harness(url=>response(200,url==='/api/arr-downloads' ? {context_id:'production',ready:true,default_date:'2026-10-06',latest_task:task} : {...task,status:'failed',can_retry:true})); await h.initARRDownload(); assert.equal(h.element('#arr-download-date').value,task.report_date); assert.equal(h.element('#arr-download-button').disabled,true); await h.initARRDownload(); assert.equal(h.state.arrDownloadTask.status,'failed'); assert.equal(h.state.arrDownloadIntent.request_id,task.request_id); assert.equal(h.element('#arr-download-date').value,task.report_date); }); test('changing source context clears the previous in-memory intent before restoring',async()=>{ let context='sandbox'; const h=harness(()=>response(200,{context_id:context,ready:true,default_date:'2026-10-07'})); await h.initARRDownload(); h.rememberARRIntent({request_id:'a'.repeat(32),report_date:'2026-09-15'}); context='production'; await h.initARRDownload(); assert.equal(h.state.arrDownloadIntent,null); assert.equal(h.state.arrDownloadTask,null); assert.equal(h.element('#arr-download-date').value,'2026-10-07'); assert.equal(h.calls.length,2); });