feat: add date-based OHIP data reporting alongside XML
This commit is contained in:
1 parent
5ecd571e3b
commit
ad2c59256f
259 files changed
+37326
-126
No files matched your search
@@ -0,0 +1,154 @@
|
||||
// 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 fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const vm = require('node:vm');
|
||||
const {webcrypto} = require('node:crypto');
|
||||
const source = fs.readFileSync(path.resolve(__dirname, '../../arr_web/static/app.js'), 'utf8');
|
||||
const boot = ' boot();\n})();';
|
||||
assert(source.endsWith(boot+'\n') || source.endsWith(boot));
|
||||
function harness(respond) {
|
||||
const elements=new Map(), storage=new Map(), calls=[];
|
||||
const element=selector => {
|
||||
if (!elements.has(selector)) elements.set(selector,{value:'', disabled:false, hidden:false, textContent:'',
|
||||
classList:{add(){},remove(){},toggle(){},contains(){return false;}},setAttribute(){}});
|
||||
return elements.get(selector);
|
||||
};
|
||||
const context=vm.createContext({Headers, console, Date, Intl, Uint8Array,
|
||||
document:{hidden:false,querySelector:element},
|
||||
window:{ARRI18n:{t:key=>key,text:value=>value,errorMessage:code=>code},crypto:webcrypto,
|
||||
setTimeout(){return 1;},clearTimeout(){},location:{replace(){}}},
|
||||
localStorage:{setItem:(k,v)=>storage.set(k,v),getItem:k=>storage.get(k),removeItem:k=>storage.delete(k)},
|
||||
fetch:async (url, options) => {calls.push({url,method:options.method||'GET',body:options.body});return respond(url,options,calls);},
|
||||
});
|
||||
vm.runInContext(source.replace(boot,' globalThis.subject = {state, submitARRDownload, rememberARRIntent, acceptARRDownloadTask, handleARRDownloadDateChange, initARRDownload};\n})();'),context);
|
||||
const subject=context.subject;
|
||||
Object.assign(subject.state,{arrDownloadReady:true,arrDownloadLoaded:true,arrDownloadStorageKey:'test',csrf:'fixture'});
|
||||
element('#arr-download-date').value='2026-09-16';
|
||||
return {...subject,calls,storage,element,submit:()=>subject.submitARRDownload({preventDefault(){}})};
|
||||
}
|
||||
const response=(status,data,code) => ({status,ok:status<400,json:async()=>code?{ok:false,error:{code}}:{ok:true,data}});
|
||||
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,{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');
|
||||
});
|
||||
Reference in new issue
Block a user