172 lines
5.6 KiB
TypeScript
172 lines
5.6 KiB
TypeScript
import { expect, test } from "@playwright/test";
|
|
import { apiPathPattern, mockApi, primeMockSession } from "./support/network.js";
|
|
|
|
const cronTasks = [
|
|
{
|
|
id: "economic.transfer_queue",
|
|
name: "Process e-conomic transfer queue",
|
|
description: "Processes pending e-conomic transfer queue jobs.",
|
|
module: "economic",
|
|
schedule: { type: "interval", seconds: 30 },
|
|
default_schedule: { type: "interval", seconds: 30 },
|
|
enabled: true,
|
|
default_enabled: true,
|
|
estimated_duration_ms: 2400,
|
|
next_run_at: "2026-07-09 12:01:00",
|
|
last_status: "succeeded",
|
|
due: true,
|
|
},
|
|
{
|
|
id: "system.sync_logs",
|
|
name: "Sync logs",
|
|
description: "Flushes application logs.",
|
|
module: "system",
|
|
schedule: { type: "interval", seconds: 300 },
|
|
default_schedule: { type: "interval", seconds: 300 },
|
|
enabled: false,
|
|
default_enabled: true,
|
|
estimated_duration_ms: 900,
|
|
next_run_at: "2026-07-09 12:05:00",
|
|
last_status: "failed",
|
|
last_error: "Redis unavailable",
|
|
due: false,
|
|
},
|
|
];
|
|
|
|
function cronListPayload(tasks = cronTasks) {
|
|
return {
|
|
success: true,
|
|
data: {
|
|
tasks,
|
|
summary: {
|
|
total: tasks.length,
|
|
enabled: tasks.filter((task) => task.enabled).length,
|
|
due: tasks.filter((task) => task.enabled && task.due).length,
|
|
},
|
|
},
|
|
meta: {},
|
|
includes: {},
|
|
};
|
|
}
|
|
|
|
test.describe("Superuser cron operations", () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await mockApi(page, {
|
|
authenticated: true,
|
|
permissions: ["superuser", "superuser_cron_view", "SUPERUSER_RUN_CRON", "superuser_cron_manage"],
|
|
});
|
|
await primeMockSession(page, { token: "superuser-cron-token", bootPath: null });
|
|
});
|
|
|
|
test("superusers can inspect, run, toggle, and reschedule cron tasks", async ({ page }) => {
|
|
const patchPayloads: Array<Record<string, unknown>> = [];
|
|
const runPayloads: Array<Record<string, unknown>> = [];
|
|
let currentTasks = cronTasks.map((task) => ({ ...task, schedule: { ...task.schedule } }));
|
|
|
|
await page.route(apiPathPattern("/superuser/cron"), async (route) => {
|
|
if (route.request().method() !== "GET") {
|
|
await route.fallback();
|
|
return;
|
|
}
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify(cronListPayload(currentTasks)),
|
|
});
|
|
});
|
|
|
|
await page.route(apiPathPattern("/superuser/cron/runs"), async (route) => {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({
|
|
success: true,
|
|
data: {
|
|
runs: [
|
|
{
|
|
id: 41,
|
|
task_id: "economic.transfer_queue",
|
|
source: "automatic",
|
|
status: "succeeded",
|
|
started_at: "2026-07-09 12:00:00",
|
|
duration_ms: 2100,
|
|
},
|
|
],
|
|
},
|
|
meta: {},
|
|
includes: {},
|
|
}),
|
|
});
|
|
});
|
|
|
|
await page.route(apiPathPattern("/superuser/cron/run"), async (route) => {
|
|
runPayloads.push(route.request().postDataJSON() as Record<string, unknown>);
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({
|
|
success: true,
|
|
data: {
|
|
id: 42,
|
|
task_id: "economic.transfer_queue",
|
|
source: "manual",
|
|
status: "succeeded",
|
|
started_at: "2026-07-09 12:02:00",
|
|
duration_ms: 1800,
|
|
},
|
|
meta: {},
|
|
includes: {},
|
|
}),
|
|
});
|
|
});
|
|
|
|
await page.route(apiPathPattern("/superuser/cron/config"), async (route) => {
|
|
const payload = route.request().postDataJSON() as Record<string, unknown>;
|
|
patchPayloads.push(payload);
|
|
currentTasks = currentTasks.map((task) => {
|
|
if (task.id !== payload.task_id) {
|
|
return task;
|
|
}
|
|
return {
|
|
...task,
|
|
...(typeof payload.enabled === "boolean" ? { enabled: payload.enabled } : {}),
|
|
...(payload.schedule ? { schedule: payload.schedule as { type: string; seconds: number } } : {}),
|
|
};
|
|
});
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify(cronListPayload(currentTasks)),
|
|
});
|
|
});
|
|
|
|
await page.goto("/superuser/system/cron", { waitUntil: "domcontentloaded" });
|
|
|
|
await expect(page.getByTestId("superuser-cron-page")).toBeVisible();
|
|
await expect(page.getByTestId("cron-task-row-economic.transfer_queue")).toContainText(
|
|
"Process e-conomic transfer queue"
|
|
);
|
|
await expect(page.getByTestId("cron-total-tasks")).toContainText("2");
|
|
|
|
await Promise.all([
|
|
page.waitForResponse(
|
|
(response) => response.url().includes("/superuser/cron/run") && response.request().method() === "POST"
|
|
),
|
|
page.getByTestId("cron-task-run-economic.transfer_queue").click(),
|
|
]);
|
|
|
|
expect(runPayloads).toEqual([{ task_id: "economic.transfer_queue", force: false }]);
|
|
await expect(page.getByTestId("cron-run-history")).toContainText("manual");
|
|
|
|
await page.getByTestId("cron-task-toggle-system.sync_logs").click();
|
|
expect(patchPayloads.at(-1)).toMatchObject({ task_id: "system.sync_logs", enabled: true });
|
|
|
|
await page.getByTestId("cron-task-interval-economic.transfer_queue").fill("120");
|
|
await page.getByTestId("cron-task-save-economic.transfer_queue").click();
|
|
expect(patchPayloads.at(-1)).toMatchObject({
|
|
task_id: "economic.transfer_queue",
|
|
schedule: { type: "interval", seconds: 120 },
|
|
});
|
|
});
|
|
});
|