Complete verified offline assets in a resumable worker job
This commit is contained in:
@@ -45,3 +45,39 @@ test('removing group membership retains the old tab URL even if the physical fil
|
||||
const previous=manifest(),current=manifest();current.groups.core.files=['/index.html'];
|
||||
assert.ok(core.retained(current,previous).has('/a.js?v=a'));
|
||||
});
|
||||
|
||||
test('completion includes ungrouped runtime files and exposes verified missing paths', async () => {
|
||||
const c=cache(), m=manifest();m.files['/worker.js']={h:'w'};
|
||||
await core.syncAssets(m,{cache:c,fetchFn:async u=>response(u.split('=')[1])});
|
||||
const status=await core.status(m,c);
|
||||
assert.equal(status.ready,true);assert.equal(status.offlineReady,false);
|
||||
assert.deepEqual(status.missingFiles,['/worker.js']);assert.equal(status.cached,2);assert.equal(status.total,3);
|
||||
await core.complete(m,{cache:c,fetchFn:async u=>response(u.split('=')[1])});
|
||||
assert.equal((await core.status(m,c)).offlineReady,true);
|
||||
});
|
||||
|
||||
test('completion persists backoff and resumes only missing files after a worker restart',async()=>{
|
||||
const c=cache(),m=manifest();let clock=1000;const calls=[];
|
||||
await core.complete(m,{cache:c,cycles:1,now:()=>clock,baseDelay:10,fetchFn:async u=>{if(u.includes('index'))throw Error('offline');return response('a');}});
|
||||
const checkpoint=await(await c.match(core.completionKey(m))).json();
|
||||
assert.equal(checkpoint.running,false);assert.equal(checkpoint.failures,1);assert.equal(checkpoint.nextRetryAt,1010);
|
||||
await core.complete(m,{cache:c,now:()=>clock,sleep:async ms=>{assert.equal(ms,10);clock+=ms;},fetchFn:async u=>{calls.push(u);return response(u.split('=')[1]);}});
|
||||
assert.deepEqual(calls,['/index.html?v=b']);
|
||||
const status=await(await c.match(core.completionKey(m))).json();assert.equal(status.offlineReady,true);assert.equal(status.failures,0);
|
||||
await c.delete('/a.js?v=a');assert.equal((await core.status(m,c)).offlineReady,false);
|
||||
});
|
||||
|
||||
test('completion retries with exponential backoff and keeps making progress past a failed asset',async()=>{
|
||||
const c=cache(),m=manifest(),delays=[];let tries=0;
|
||||
const job=await core.complete(m,{cache:c,baseDelay:5,sleep:async ms=>delays.push(ms),fetchFn:async u=>{if(u.includes('a.js')&&++tries<=6)throw Error('drop');return response(u.split('=')[1]);}});
|
||||
assert.deepEqual(delays,[5,10]);assert.equal(job.offlineReady,true);assert.ok(await c.match('/index.html?v=b'));
|
||||
});
|
||||
|
||||
test('overlapping layout and completion jobs share a six-download ceiling',async()=>{
|
||||
const c=cache(),m=manifest();m.groups.extra={background:true,files:[]};
|
||||
for(let i=0;i<15;i++){const p='/extra'+i+'.js';m.files[p]={h:String(i)};m.groups.extra.files.push(p);}
|
||||
let active=0,peak=0;const calls=new Map();
|
||||
const fetchFn=async u=>{active++;peak=Math.max(active,peak);calls.set(u,(calls.get(u)||0)+1);await new Promise(r=>setTimeout(r,3));active--;return response(u.split('=')[1]);};
|
||||
await Promise.all([core.complete(m,{cache:c,fetchFn}),core.syncAssets(m,{cache:c,fetchFn,groups:['extra']})]);
|
||||
assert.ok(peak<=6);assert.ok([...calls.values()].every(count=>count===1));
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user