| 1 | import assert from "node:assert/strict"; |
| 2 | import { startDesktopEventRecovery } from "../lib/desktopEventRecovery"; |
| 3 | |
| 4 | const tick = () => new Promise(resolve => setImmediate(resolve)); |
| 5 | let signal!: () => void; |
| 6 | let selected = "task-a", generation = 1; |
| 7 | const pending: Array<(value: string) => void> = []; |
| 8 | const applied: string[] = []; |
| 9 | const timers = new Map<number, () => void>(); |
| 10 | let timerID = 0; |
| 11 | const stop = startDesktopEventRecovery({ |
| 12 | subscribe: callback => { signal = callback; return () => {}; }, |
| 13 | capture: () => ({ selected, generation }), |
| 14 | isCurrent: scope => scope.selected === selected && scope.generation === generation, |
| 15 | read: () => new Promise<string>(resolve => pending.push(resolve)), |
| 16 | apply: (snapshot, scope) => applied.push(`${scope.selected}:${scope.generation}:${snapshot}`), |
| 17 | timer: callback => { const id = ++timerID; timers.set(id, callback); return id; }, |
| 18 | clearTimer: id => { timers.delete(id as number); }, |
| 19 | }); |
| 20 | signal(); signal(); |
| 21 | assert.equal(pending.length, 1, "gaps share one in-flight snapshot read"); |
| 22 | pending.shift()!("obsolete"); await tick(); |
| 23 | assert.deepEqual(applied, [], "a new recovery signal fences the previous snapshot"); |
| 24 | assert.equal(pending.length, 1); |
| 25 | selected = "task-b"; |
| 26 | pending.shift()!("old-selection"); await tick(); |
| 27 | assert.deepEqual(applied, [], "switching tasks prevents late snapshot application"); |
| 28 | assert.equal(timers.size, 1, "a superseded selection queues fresh recovery"); |
| 29 | [...timers.values()][0](); |
| 30 | generation++; |
| 31 | signal(); |
| 32 | pending.shift()!("old-service"); await tick(); |
| 33 | pending.shift()!("current"); await tick(); |
| 34 | assert.deepEqual(applied, ["task-b:2:current"]); |
| 35 | signal(); stop(); pending.shift()!("disposed"); await tick(); |
| 36 | assert.equal(applied.length, 1); |
| 37 | assert.equal(timers.size, 0); |
| 38 | console.log("desktop event recovery: coalescing, generation/session fencing, retry and disposal passed"); |
| 39 |