Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
120 lines
3.7 KiB
TypeScript
120 lines
3.7 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
import type { TransferTask } from "../../domain/models";
|
|
import {
|
|
canApplyDedicatedResumeProgress,
|
|
createDedicatedResumeChildUpdateBatcher,
|
|
createDedicatedResumeProgressBatcher,
|
|
DEDICATED_RESUME_CHILD_UPDATE_BATCH_SIZE,
|
|
} from "./dedicatedResumeProgress";
|
|
|
|
test("deferred dedicated-resume progress cannot reopen a settled row", () => {
|
|
for (const status of [
|
|
"pausing",
|
|
"paused",
|
|
"completed",
|
|
"failed",
|
|
"cancelled",
|
|
"attention",
|
|
"interrupted",
|
|
] as const) {
|
|
assert.equal(canApplyDedicatedResumeProgress(status), false, status);
|
|
}
|
|
for (const status of ["pending", "queued", "transferring"] as const) {
|
|
assert.equal(canApplyDedicatedResumeProgress(status), true, status);
|
|
}
|
|
});
|
|
|
|
test("late animation-frame progress cannot revive any settled resume state", () => {
|
|
const settledStatuses = [
|
|
"pausing",
|
|
"paused",
|
|
"completed",
|
|
"failed",
|
|
"cancelled",
|
|
"attention",
|
|
"interrupted",
|
|
] as const;
|
|
|
|
for (const settledStatus of settledStatuses) {
|
|
let status: TransferTask["status"] = "transferring";
|
|
let scheduled: FrameRequestCallback | undefined;
|
|
const applied: number[] = [];
|
|
const batcher = createDedicatedResumeProgressBatcher<number>({
|
|
requestFrame: (callback) => {
|
|
scheduled = callback;
|
|
return 41;
|
|
},
|
|
cancelFrame: () => undefined,
|
|
canApply: () => canApplyDedicatedResumeProgress(status),
|
|
apply: (progress) => applied.push(progress),
|
|
});
|
|
|
|
batcher.push(7);
|
|
status = settledStatus;
|
|
scheduled?.(0);
|
|
assert.deepEqual(applied, [], settledStatus);
|
|
}
|
|
});
|
|
|
|
test("finishing a resume flushes once and rejects raced or future progress", () => {
|
|
let status: TransferTask["status"] = "transferring";
|
|
let scheduled: FrameRequestCallback | undefined;
|
|
const cancelledHandles: number[] = [];
|
|
const applied: number[] = [];
|
|
const batcher = createDedicatedResumeProgressBatcher<number>({
|
|
requestFrame: (callback) => {
|
|
scheduled = callback;
|
|
return 73;
|
|
},
|
|
cancelFrame: (handle) => cancelledHandles.push(handle),
|
|
canApply: () => canApplyDedicatedResumeProgress(status),
|
|
apply: (progress) => applied.push(progress),
|
|
});
|
|
|
|
batcher.push(11);
|
|
batcher.push(12);
|
|
batcher.finish();
|
|
status = "completed";
|
|
scheduled?.(0); // Simulate a frame already dequeued when it was cancelled.
|
|
batcher.push(13);
|
|
batcher.finish();
|
|
|
|
assert.deepEqual(applied, [12]);
|
|
assert.deepEqual(cancelledHandles, [73]);
|
|
});
|
|
|
|
test("50,000 retained child updates use a hard-bounded number of store scans", () => {
|
|
const retained = new Set(Array.from({ length: 50_000 }, (_, index) => `child-${index}`));
|
|
const batches: TransferTask[][] = [];
|
|
const batcher = createDedicatedResumeChildUpdateBatcher({
|
|
getTaskCount: () => 50_001,
|
|
hasTask: (taskId) => retained.has(taskId),
|
|
upsertTasks: (tasks) => batches.push([...tasks]),
|
|
});
|
|
|
|
for (let index = 0; index < 50_000; index += 1) {
|
|
const child = {
|
|
id: `child-${index}`,
|
|
status: "transferring",
|
|
parentTaskId: "parent",
|
|
} as TransferTask;
|
|
batcher.push(child);
|
|
batcher.push({ ...child, status: "completed" });
|
|
}
|
|
batcher.flush();
|
|
|
|
assert.ok(
|
|
batches.length <= Math.ceil(50_000 / DEDICATED_RESUME_CHILD_UPDATE_BATCH_SIZE),
|
|
`expected bounded store scans, got ${batches.length}`,
|
|
);
|
|
const finalById = new Map<string, TransferTask>();
|
|
for (const task of batches.flat()) finalById.set(task.id, task);
|
|
assert.equal(finalById.size, 50_000);
|
|
assert.ok([...finalById.values()].every((task) => task.status === "completed"));
|
|
assert.ok(
|
|
batches.flat().length <= 50_000 + batches.length,
|
|
"a batch-boundary transition may repeat at most one child per store scan",
|
|
);
|
|
});
|