mirror of
https://github.com/nicotsx/zerobyte.git
synced 2026-05-08 07:43:35 -04:00
e2e: test backup & restore functions (#341)
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -28,3 +28,6 @@ node_modules/
|
||||
/blob-report/
|
||||
/playwright/.cache/
|
||||
/playwright/.auth/
|
||||
|
||||
playwright/.auth
|
||||
playwright/temp
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 });
|
||||
});
|
||||
73
e2e/0002-backup-restore.spec.ts
Normal file
73
e2e/0002-backup-restore.spec.ts
Normal 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" });
|
||||
});
|
||||
@@ -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 });
|
||||
|
||||
|
||||
@@ -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"],
|
||||
},
|
||||
|
||||
// {
|
||||
|
||||
Reference in New Issue
Block a user