feat: make mobile queue priority self-healing (Closes #1466)
CI / lint (pull_request) Successful in 3m44s
CI / build-release (pull_request) Successful in 7s
CI / browser-journey (pull_request) Successful in 7m12s
CI / release-candidate (pull_request) Has been skipped

This commit is contained in:
timmy committed 2026-08-27 10:32:02 +00:00
1 parent c5b801460d
commit 1e8d27c18f
4 files changed
+168 -31

No files matched your search

+91 -24
View File
@@ -149,42 +149,109 @@ const fetchJson = async (url, init={{}}) => {{
]
def test_mobile_queue_priority_serializes_rapid_edits_and_publishes_latest_order():
def test_mobile_queue_priority_coalesces_rapid_edits_and_publishes_latest_order():
script = f"""
const createPriority = require({json.dumps(str(QUEUE_PRIORITY))});
const values = new Map(); const pending=[]; const payloads=[];
const fetchJson = async (_url, init) => new Promise(resolve => {{
const payload=JSON.parse(init.body); payloads.push(payload); pending.push(() => resolve({{revision:payload.revision+1,order:payload.order}}));
}});
const values = new Map(); const payloads=[]; const timers=[];
const setTimer = (callback, delay) => {{ const timer={{callback,delay,cancelled:false}}; timers.push(timer); return timer; }};
const clearTimer = timer => {{ if (timer) timer.cancelled=true; }};
const fetchJson = async (_url, init) => {{
const payload=JSON.parse(init.body); payloads.push(payload);
return {{revision:payload.revision+1,order:payload.order}};
}};
(async () => {{
let priority;
priority=createPriority({{
const priority=createPriority({{
storage:{{getItem:key=>values.get(key)||null,setItem:(key,value)=>values.set(key,value),removeItem:key=>values.delete(key)}},
getLogin:()=>'alice', fetchJson, onChange:()=>{{ void priority.sync(); }},
getLogin:()=>'alice', fetchJson, setTimeout:setTimer, clearTimeout:clearTimer, debounceMs:150,
}});
priority.move('following', -1);
priority.move('following', -1);
for (let index=0; index<4; index += 1) priority.move('following', -1);
const before=payloads.length;
const active=timers.filter(timer=>!timer.cancelled);
active[0].callback();
await new Promise(resolve => setImmediate(resolve));
const firstPending=pending.length;
pending.shift()();
await new Promise(resolve => setImmediate(resolve));
const secondPending=pending.length;
pending.shift()();
await new Promise(resolve => setImmediate(resolve));
process.stdout.write(JSON.stringify({{firstPending,secondPending,payloads,state:priority.state()}}));
process.stdout.write(JSON.stringify({{before,active:active.map(timer=>timer.delay),payloads,state:priority.state()}}));
}})().catch(error=>{{console.error(error);process.exit(1);}});
"""
result = subprocess.run(["node", "-e", script], capture_output=True, text=True)
assert result.returncode == 0, result.stderr
payload = json.loads(result.stdout)
assert payload["firstPending"] == 1
assert payload["secondPending"] == 1
assert len(payload["payloads"]) == 2
assert payload["before"] == 0
assert payload["active"] == [150]
assert len(payload["payloads"]) == 1
assert payload["payloads"][0]["revision"] == 0
assert payload["payloads"][1]["revision"] == 1
assert payload["payloads"][1]["order"] == payload["state"]["order"]
assert payload["state"]["revision"] == 2
assert payload["payloads"][0]["order"] == payload["state"]["order"]
assert payload["state"]["revision"] == 1
assert payload["state"]["status"] == "ready"
def test_mobile_queue_priority_retries_transient_failure_without_another_edit():
script = f"""
const createPriority = require({json.dumps(str(QUEUE_PRIORITY))});
const values = new Map(); const timers=[]; let calls=0;
const setTimer = (callback, delay) => {{ const timer={{callback,delay,cancelled:false}}; timers.push(timer); return timer; }};
const clearTimer = timer => {{ if (timer) timer.cancelled=true; }};
const fetchJson = async (_url, init) => {{
calls += 1;
if (calls === 1) {{ const error=new Error('temporary'); error.status=503; throw error; }}
const payload=JSON.parse(init.body);
return {{revision:payload.revision+1,order:payload.order}};
}};
(async () => {{
const priority=createPriority({{
storage:{{getItem:key=>values.get(key)||null,setItem:(key,value)=>values.set(key,value),removeItem:key=>values.delete(key)}},
getLogin:()=>'alice', fetchJson, setTimeout:setTimer, clearTimeout:clearTimer,
debounceMs:150, retryBaseMs:1000, retryMaxMs:30000,
}});
priority.move('following', -1);
timers.find(timer=>!timer.cancelled).callback();
await new Promise(resolve => setImmediate(resolve));
const afterFailure=priority.state();
const retry=timers.filter(timer=>!timer.cancelled).at(-1);
retry.callback();
await new Promise(resolve => setImmediate(resolve));
process.stdout.write(JSON.stringify({{calls,afterFailure,retryDelay:retry.delay,settled:priority.state()}}));
}})().catch(error=>{{console.error(error);process.exit(1);}});
"""
result = subprocess.run(["node", "-e", script], capture_output=True, text=True)
assert result.returncode == 0, result.stderr
payload = json.loads(result.stdout)
assert payload["afterFailure"]["pending"] is True
assert payload["afterFailure"]["status"] == "pending"
assert payload["retryDelay"] == 1000
assert payload["calls"] == 2
assert payload["settled"]["pending"] is False
assert payload["settled"]["status"] == "ready"
def test_mobile_queue_priority_refreshes_clean_device_when_foregrounded():
script = f"""
const createPriority = require({json.dumps(str(QUEUE_PRIORITY))});
const values = new Map(); const windowListeners={{}}; const documentListeners={{}};
const remoteOrder=['attention','following','today','update','agenda','authored','filed','later','draft'];
let calls=0;
const documentRef={{hidden:false,addEventListener:(name, callback)=>documentListeners[name]=callback}};
const windowRef={{addEventListener:(name, callback)=>windowListeners[name]=callback}};
const priority=createPriority({{
storage:{{getItem:key=>values.get(key)||null,setItem:(key,value)=>values.set(key,value),removeItem:key=>values.delete(key)}},
getLogin:()=>'alice', fetchJson:async () => {{ calls += 1; return {{revision:4,order:remoteOrder.slice()}}; }},
}});
(async () => {{
priority.startLifecycle({{window:windowRef,document:documentRef}});
documentListeners.visibilitychange();
await new Promise(resolve => setImmediate(resolve));
process.stdout.write(JSON.stringify({{calls,state:priority.state(),listeners:[...Object.keys(windowListeners),...Object.keys(documentListeners)]}}));
}})().catch(error=>{{console.error(error);process.exit(1);}});
"""
result = subprocess.run(["node", "-e", script], capture_output=True, text=True)
assert result.returncode == 0, result.stderr
payload = json.loads(result.stdout)
assert set(payload["listeners"]) == {"online", "visibilitychange"}
assert payload["calls"] == 1
assert payload["state"]["revision"] == 4
assert payload["state"]["order"][1] == "following"
assert payload["state"]["status"] == "ready"
@@ -535,7 +602,7 @@ async def test_mobile_queue_priority_wires_cross_device_hydration_and_explicit_c
assert 'id="use-remote-mobile-queue-priority"' in html
assert "fetchJson: fetchReviewJson" in html
assert "void mobileQueuePriority.load();" in html
assert "void mobileQueuePriority.sync();" in html
assert "mobileQueuePriority.startLifecycle({window, document});" in html
assert "keepLocalButton: qs('#keep-local-mobile-queue-priority')" in html
assert "useRemoteButton: qs('#use-remote-mobile-queue-priority')" in html
assert "error.payload = payload;" in html