Merge branch 'codex/work-folders-runtime-hardening-refresh' into codex/work-folders-staging-hardening-refresh
* codex/work-folders-runtime-hardening-refresh: Continue independent cached file deletions after a failure
This commit is contained in:
commit
0ca7657ac9
|
|
@ -134,13 +134,13 @@ describe("cached file selection and retained trash", () => {
|
|||
expect(checkbox("a.txt")).not.toBeNull();
|
||||
expect(deleted).toEqual([]);
|
||||
});
|
||||
it("refreshes partial successes and leaves only failed files selected for retry", async () => {
|
||||
it.each([["a.txt", "b.txt"], ["b.txt", "a.txt"]])("refreshes partial successes when selected in order %s, %s", async (first, second) => {
|
||||
const operate = api.operation.getMockImplementation()!;
|
||||
api.operation.mockImplementation(async (target, operation) => {
|
||||
if (operation.path === "b.txt") throw new Error("Storage unavailable");
|
||||
return operate(target, operation);
|
||||
});
|
||||
await click(checkbox("a.txt")); await click(checkbox("b.txt"));
|
||||
await click(checkbox(first)); await click(checkbox(second));
|
||||
await click(button("Move 2 files to trash")!);
|
||||
expect(checkbox("a.txt")).toBeNull();
|
||||
expect(checkbox("b.txt").checked).toBe(true);
|
||||
|
|
|
|||
|
|
@ -70,11 +70,17 @@ export function WorkFolderBrowser({ owner, exampleFiles, readOnly = false, fillH
|
|||
if (action.type === "upload") for (const file of action.files) await workFoldersApi.upload(owner, file, directory ? `${directory}/${file.name}` : file.name, crypto.randomUUID());
|
||||
else if (action.type === "mkdir") { await workFoldersApi.operation(owner, { action: "mkdir", path: directory }, crypto.randomUUID()); setExpanded((before) => new Set([...before, directory])); }
|
||||
else if (action.type === "deleteSelected") {
|
||||
const failures: string[] = [];
|
||||
for (const path of action.paths) {
|
||||
await workFoldersApi.operation(owner, { action: "delete", path }, crypto.randomUUID());
|
||||
setCheckedFiles((before) => new Set([...before].filter((candidate) => candidate !== path && !candidate.startsWith(`${path}/`))));
|
||||
setSelectedPath((before) => before === path || before?.startsWith(`${path}/`) ? null : before);
|
||||
try {
|
||||
await workFoldersApi.operation(owner, { action: "delete", path }, crypto.randomUUID());
|
||||
setCheckedFiles((before) => new Set([...before].filter((candidate) => candidate !== path && !candidate.startsWith(`${path}/`))));
|
||||
setSelectedPath((before) => before === path || before?.startsWith(`${path}/`) ? null : before);
|
||||
} catch (error) {
|
||||
failures.push(`${path}: ${error instanceof Error ? error.message : "Could not move to trash"}`);
|
||||
}
|
||||
}
|
||||
if (failures.length > 0) throw new Error(failures.join("; "));
|
||||
}
|
||||
else if (action.type === "restore" || action.type === "purge") await workFoldersApi.operation(owner, { action: action.type, fileId: action.fileId }, crypto.randomUUID());
|
||||
else for (const run of (syncQuery.data ?? []).filter((run) => run.active)) await workFoldersApi.refresh(owner, run.runId);
|
||||
|
|
|
|||
Loading…
Reference in New Issue