139 lines
5.6 KiB
JavaScript
139 lines
5.6 KiB
JavaScript
const { expect, test } = require("@playwright/test");
|
|
|
|
async function authenticate(page) {
|
|
await page.goto("/");
|
|
await page.getByLabel("账号名").fill("stage4admin");
|
|
await page.getByLabel("密码").fill("Stage4-pass-123!");
|
|
await page.getByRole("button", { name: "登录", exact: true }).click();
|
|
await expect(page.locator(".sidebar, .field-error")).toBeVisible();
|
|
if (!(await page.locator(".sidebar").isVisible())) {
|
|
await page.getByRole("tab", { name: "注册" }).click();
|
|
await page.getByRole("button", { name: "注册并登录" }).click();
|
|
await expect(page.locator(".sidebar")).toBeVisible();
|
|
}
|
|
}
|
|
|
|
test("administrator can audit jobs, backfill and govern market events", async ({ page }) => {
|
|
const calls = [];
|
|
const job = {
|
|
id: 7,
|
|
kind: "market.refresh",
|
|
run_key: "2026-07-30:final:1",
|
|
requested_date: "2026-07-30",
|
|
trigger: "after-close",
|
|
status: "failed",
|
|
attempt: 1,
|
|
started_at: "2026-07-30T15:10:00+08:00",
|
|
finished_at: "2026-07-30T15:10:02+08:00",
|
|
duration_ms: 2000,
|
|
coverage: null,
|
|
source_set: [],
|
|
output_version: "",
|
|
payload: {},
|
|
error_code: "ProviderError",
|
|
error_message: "上游暂不可用,保留最后成功快照",
|
|
};
|
|
await page.route("**/api/admin/system/credentials", (route) => route.fulfill({
|
|
contentType: "application/json",
|
|
body: JSON.stringify([
|
|
{ name: "tushare_token", configured: true, updated_at: "2026-07-30T09:00:00+08:00" },
|
|
{ name: "ifind_refresh_token", configured: true, updated_at: "2026-07-30T09:00:00+08:00" },
|
|
{ name: "ifind_access_token", configured: true, updated_at: "2026-07-30T09:00:00+08:00" },
|
|
]),
|
|
}));
|
|
await page.route("**/api/admin/operations/jobs", (route) => route.fulfill({
|
|
contentType: "application/json", body: JSON.stringify([job]),
|
|
}));
|
|
await page.route("**/api/admin/operations/backfill", async (route) => {
|
|
calls.push({ kind: "backfill", payload: route.request().postDataJSON() });
|
|
await route.fulfill({ contentType: "application/json", body: JSON.stringify({ completed: 2 }) });
|
|
});
|
|
await page.route("**/api/admin/operations/events/supplement?*", async (route) => {
|
|
calls.push({ kind: "supplement" });
|
|
await route.fulfill({ contentType: "application/json", body: JSON.stringify({ matched: 3 }) });
|
|
});
|
|
await page.route("**/api/admin/operations/events/*/*/history", (route) => route.fulfill({
|
|
contentType: "application/json",
|
|
body: JSON.stringify([{
|
|
id: 2,
|
|
created_at: "2026-07-30T15:20:00+08:00",
|
|
source: "admin",
|
|
event_type: "limit_up",
|
|
reason: "人工核验原因",
|
|
created_by_name: "stage4admin",
|
|
}]),
|
|
}));
|
|
await page.route("**/api/admin/operations/events/*/*", async (route) => {
|
|
calls.push({ kind: "revision", payload: route.request().postDataJSON() });
|
|
await route.fulfill({ contentType: "application/json", body: JSON.stringify({ id: 2 }) });
|
|
});
|
|
|
|
await authenticate(page);
|
|
await page.getByRole("button", { name: "系统管理" }).click();
|
|
await expect(page.getByRole("heading", { name: "后台任务状态" })).toBeVisible();
|
|
await expect(page.getByText("上游暂不可用,保留最后成功快照")).toBeVisible();
|
|
|
|
const backfill = page.locator(".operation-form");
|
|
await backfill.getByLabel("开始日期").fill("2026-07-29");
|
|
await backfill.getByLabel("结束日期").fill("2026-07-30");
|
|
await backfill.getByRole("button", { name: "开始回补" }).click();
|
|
await expect(page.getByRole("status")).toContainText("历史数据回补已完成");
|
|
|
|
const event = page.locator(".event-form");
|
|
await event.getByLabel("数据日期").fill("2026-07-30");
|
|
await event.getByLabel("股票代码").fill("000001.SZ");
|
|
await event.getByLabel("原因").fill("人工核验原因");
|
|
await event.getByRole("button", { name: "保存修订" }).click();
|
|
await expect(page.getByText("人工核验原因", { exact: true })).toBeVisible();
|
|
await event.getByRole("button", { name: "自动补充" }).click();
|
|
await expect(page.getByRole("status")).toContainText("事件原因补充完成");
|
|
|
|
expect(calls).toEqual([
|
|
{ kind: "backfill", payload: { start_date: "2026-07-29", end_date: "2026-07-30" } },
|
|
{
|
|
kind: "revision",
|
|
payload: {
|
|
event_type: "limit_up",
|
|
reason: "人工核验原因",
|
|
first_time: "",
|
|
last_time: "",
|
|
open_times: null,
|
|
},
|
|
},
|
|
{ kind: "supplement" },
|
|
]);
|
|
});
|
|
|
|
test("administrator tests a selected model without exposing its key", async ({ page }) => {
|
|
await page.route("**/api/admin/models", (route) => route.fulfill({
|
|
contentType: "application/json",
|
|
body: JSON.stringify([{
|
|
id: 1,
|
|
display_name: "主模型",
|
|
base_url: "https://model.example.com/v1",
|
|
model_identifier: "reasoning-model",
|
|
has_api_key: true,
|
|
is_primary: true,
|
|
is_fallback: false,
|
|
updated_at: "2026-07-30T10:00:00+08:00",
|
|
}]),
|
|
}));
|
|
await page.route("**/api/admin/models/1/test", (route) => route.fulfill({
|
|
contentType: "application/json",
|
|
body: JSON.stringify({
|
|
connected: true,
|
|
message: "连接成功",
|
|
duration_ms: 286,
|
|
request_id: "audit-request",
|
|
}),
|
|
}));
|
|
|
|
await authenticate(page);
|
|
await page.getByRole("button", { name: "系统管理" }).click();
|
|
await page.getByRole("button", { name: "模型池" }).click();
|
|
await page.getByRole("button", { name: "主模型 reasoning-model 主模型" }).click();
|
|
await page.getByRole("button", { name: "测试连接" }).click();
|
|
await expect(page.getByText("连接成功 · 286 ms", { exact: true })).toBeVisible();
|
|
await expect(page.locator("body")).not.toContainText(/secret|api[_ -]?key/i);
|
|
});
|