e2e: test backup & restore functions (#341)

This commit is contained in:
Nico
2026-01-11 14:00:29 +01:00
committed by GitHub
parent bb2bdb9724
commit 1ea2460ea7
7 changed files with 106 additions and 7 deletions

3
.gitignore vendored
View File

@@ -28,3 +28,6 @@ node_modules/
/blob-report/
/playwright/.cache/
/playwright/.auth/
playwright/.auth
playwright/temp

View File

@@ -98,10 +98,8 @@ export function RestoreForm({ snapshot, repository, snapshotId, returnPath }: Re
const { mutate: restoreSnapshot, isPending: isRestoring } = useMutation({
...restoreSnapshotMutation(),
onSuccess: (data) => {
toast.success("Restore completed", {
description: `Successfully restored ${data.filesRestored} file(s). ${data.filesSkipped} file(s) skipped.`,
});
onSuccess: () => {
toast.success("Restore completed");
void navigate(returnPath);
},
onError: (error) => {

View File

@@ -57,4 +57,5 @@ services:
- "4096:4096"
volumes:
- /etc/localtime:/etc/localtime:ro
- ./data:/var/lib/zerobyte
- ./playwright/data:/var/lib/zerobyte/data
- ./playwright/temp:/test-data

View File

@@ -1,6 +1,9 @@
import fs from "fs";
import { test, expect } from "@playwright/test";
import { resetDatabase } from "./helpers/db";
import path from "node:path";
const authFile = path.join(process.cwd(), "./playwright/.auth/user.json");
// TODO: Run these tests with different users once multi-user support is added
@@ -80,3 +83,16 @@ test("can't create another admin user after initial setup", async ({ page }) =>
await expect(page.getByText("Failed to create admin user")).toBeVisible();
});
test("can login after initial setup", async ({ page }) => {
await page.goto("/login");
await page.getByRole("textbox", { name: "Username" }).fill("test");
await page.getByRole("textbox", { name: "Password" }).fill("password");
await page.getByRole("button", { name: "Login" }).click();
await expect(page).toHaveURL("/volumes");
await expect(page.getByRole("heading", { name: "No volume" })).toBeVisible();
await page.context().storageState({ path: authFile });
});

View File

@@ -0,0 +1,73 @@
import { expect, test } from "@playwright/test";
import path from "node:path";
import fs from "node:fs";
test.beforeAll(() => {
const testDataPath = path.join(process.cwd(), "playwright", "temp");
if (fs.existsSync(testDataPath)) {
fs.rmSync(testDataPath, { recursive: true, force: true });
}
});
test("can backup & restore a file", async ({ page }) => {
await page.goto("/");
await expect(page).toHaveURL("/volumes");
// 0. Create a test file in /test-data
const testDataPath = path.join(process.cwd(), "playwright", "temp");
if (!fs.existsSync(testDataPath)) {
fs.mkdirSync(testDataPath);
}
const filePath = path.join(testDataPath, "test.json");
fs.chmodSync(testDataPath, 0o777);
fs.writeFileSync(filePath, JSON.stringify({ data: "test file" }));
// 1. Create a local volume on /test-data
await page.getByRole("button", { name: "Create Volume" }).click();
await page.getByRole("textbox", { name: "Name" }).fill("Test Volume");
await page.getByRole("button", { name: "Change" }).click();
await page.getByRole("button", { name: "test-data" }).click();
await page.getByRole("button", { name: "Create Volume" }).click();
await expect(page.getByText("Volume created successfully")).toBeVisible();
// 2. Create a local repository on the default location
await page.getByRole("link", { name: "Repositories" }).click();
await page.getByRole("button", { name: "Create repository" }).click();
await page.getByRole("textbox", { name: "Name" }).fill("Test Repo");
await page.getByRole("combobox", { name: "Backend" }).click();
await page.getByRole("option", { name: "Local" }).click();
await page.getByRole("button", { name: "Create repository" }).click();
await expect(page.getByText("Repository created successfully")).toBeVisible();
// 3. Create a backup schedule
await page.getByRole("link", { name: "Backups" }).click();
await page.getByRole("button", { name: "Create a backup job" }).click();
await page.getByRole("combobox").filter({ hasText: "Choose a volume to backup" }).click();
await page.getByRole("option", { name: "test-volume" }).click();
await page.getByRole("textbox", { name: "Backup name" }).fill("Test Backup");
await page.getByRole("combobox").filter({ hasText: "Select a repository" }).click();
await page.getByRole("option", { name: "Test Repo" }).click();
await page.getByRole("combobox").filter({ hasText: "Select frequency" }).click();
await page.getByRole("option", { name: "Daily" }).click();
await page.getByRole("textbox", { name: "Execution time" }).fill("00:00");
await page.getByRole("button", { name: "Create" }).click();
await expect(page.getByText("Backup job created successfully")).toBeVisible();
// 4. Runs that schedule once
await page.getByRole("button", { name: "Backup now" }).click();
await expect(page.getByText("Backup started successfully")).toBeVisible();
await expect(page.getByText("✓ Success")).toBeVisible({ timeout: 30000 });
// 5. Modify the json file after the backup
fs.writeFileSync(filePath, JSON.stringify({ data: "modified file" }));
// 6. Restores the file from backup
await page.getByRole("link", { name: "Restore" }).click();
await expect(page).toHaveURL(/\/restore/);
await page.getByRole("button", { name: "Restore All" }).click();
await expect(page.getByText("Restore completed")).toBeVisible({ timeout: 30000 });
// 7. Ensures that the file is back to its previous state
const restoredContent = fs.readFileSync(filePath, "utf8");
expect(JSON.parse(restoredContent)).toEqual({ data: "test file" });
});

View File

@@ -4,7 +4,7 @@ import path from "node:path";
import { DATABASE_URL } from "~/server/core/constants";
import * as schema from "~/server/db/schema";
const sqlite = createClient({ url: `file:${path.join(process.cwd(), "data", DATABASE_URL)}` });
const sqlite = createClient({ url: `file:${path.join(process.cwd(), "playwright", DATABASE_URL)}` });
export const db = drizzle({ client: sqlite, schema: schema });

View File

@@ -14,9 +14,17 @@ export default defineConfig({
trace: "on-first-retry",
},
projects: [
{
name: "setup",
testMatch: /.*\.setup\.ts/,
},
{
name: "chromium",
use: { ...devices["Desktop Chrome"] },
use: {
...devices["Desktop Chrome"],
storageState: "playwright/.auth/user.json",
},
dependencies: ["setup"],
},
// {