test(admin): wait for the restore response, not a timer (#359) (#363)

The legacy restore specs "restore validation and cancellation preserve
live data and allow a valid retry" and "backup and restore recover from
interrupted network requests without losing data" ended with a 5 s
toBeHidden on the restore modal. The modal closes only after the server
answers, so that was a 5 s deadline on a disk-bound restore, which WebKit
CI runners sometimes exceed.

Both specs, and the downloaded-backup spec that already waited on the
response, now share confirmRestoreAndWait: it waits for the
POST /prompt_manager/restore response, asserts a 200 (showing the body on
failure) and the success body, then checks the modal closes.

TESTING.md explains how to reproduce disk-bound timeouts with
--device-write-iops in the pinned image.
This commit is contained in:
Vito
2026-10-26 21:52:47 -07:00
committed by GitHub
parent 80c6ec5340
commit fd949b870c
2 changed files with 27 additions and 10 deletions
+12
View File
@@ -86,6 +86,18 @@ timer that "usually" covers a frame is a race on a loaded runner. Library
virtualization specs settle with `libraryObservation.settled()` (two consecutive
frames with no Library frame queued). To look for such races, repeat a spec under
load (`--workers 8 --repeat-each 10`, and in the image with `--cpus=1` or `2`).
CPU limits do not reproduce disk-bound timeouts (backup, restore). For those,
throttle writes in the image: with rootful Docker on a host with cgroup v2, add
`--device-write-iops /dev/<disk>:20` to the `docker run`, where `<disk>` is the
whole disk (not a partition) holding Docker's data root, because the test server
keeps its database in the container's `/tmp`. On a plain partition,
`lsblk -no pkname "$(df --output=source "$(docker info -f '{{.DockerRootDir}}')" | tail -1)"`
prints it; on LVM or LUKS, `lsblk -s` walks down to the disk. A spec that waits
on a timer then fails at its 5 s expect. A spec that waits on the response can
still fail at the 30 s test timeout when several workers share the throttled
disk; that means the disk is too slow for the test budget, not that the spec is
wrong.
On unsupported Linux
distributions, browser system dependencies may need manual installation. For a
quick browser run, use `test:workflows -- --project chromium`.
+15 -10
View File
@@ -257,6 +257,18 @@ async function chooseRestore(page, file) {
await page.locator('#restoreFileInput').setInputFiles(file);
}
// Confirms the chosen restore and waits for the server's answer, not for a timer: a restore is disk-bound and can take
// longer than an expect timeout on a slow runner (#359). The modal closes only after that answer.
async function confirmRestoreAndWait(page) {
const restored = page.waitForResponse(response => response.url().endsWith('/prompt_manager/restore'));
await page.locator('#confirmRestore').click();
const response = await restored;
const body = await response.text();
expect(response.status(), body).toBe(200);
expect(JSON.parse(body)).toMatchObject({ success: true, prompt_count: 2, backup_created: expect.any(String) });
await expect(page.locator('#restoreModal')).toBeHidden();
}
function confirmNextDialog(page, accept, message) {
page.once('dialog', async dialog => {
expect(dialog.type()).toBe('confirm');
@@ -287,12 +299,7 @@ test('downloaded backup restores deleted prompts, tags, and image links through
expect(await promptSnapshot(request)).not.toEqual(before);
await chooseRestore(page, backup);
confirmNextDialog(page, true, backup.name);
const restored = page.waitForResponse(response => response.url().endsWith('/prompt_manager/restore'));
await page.locator('#confirmRestore').click();
const response = await restored;
expect(response.ok()).toBe(true);
expect(await response.json()).toMatchObject({ success: true, prompt_count: 2, backup_created: expect.any(String) });
await expect(page.locator('#restoreModal')).toBeHidden();
await confirmRestoreAndWait(page);
await expect(page.locator('.prompt-text')).toHaveCount(2);
await page.reload();
await expect(page.locator('.prompt-text').filter({ hasText: 'Cat portrait' })).toBeVisible();
@@ -335,8 +342,7 @@ test('restore validation and cancellation preserve live data and allow a valid r
expect(await promptSnapshot(request)).toEqual(before);
await page.locator('#restoreFileInput').setInputFiles(backup);
confirmNextDialog(page, true, backup.name);
await page.locator('#confirmRestore').click();
await expect(page.locator('#restoreModal')).toBeHidden();
await confirmRestoreAndWait(page);
expect(await promptSnapshot(request)).toEqual(before);
});
@@ -356,8 +362,7 @@ test('backup and restore recover from interrupted network requests without losin
await expect(page.locator('#restoreModal')).toBeVisible();
expect(await promptSnapshot(request)).toEqual(before);
confirmNextDialog(page, true, backup.name);
await page.locator('#confirmRestore').click();
await expect(page.locator('#restoreModal')).toBeHidden();
await confirmRestoreAndWait(page);
expect(await promptSnapshot(request)).toEqual(before);
});