- tests/01: add graceful login timeout handling with try/catch - tests/02: migrate from isolated request.newContext to uiLogin + page.request pattern - tests/03: workspace members, platform users CRUD, billing, alert-config - tests/04: room messages, room list, repo list/detail APIs - tests/05: project members CRUD, project detail, project billing - tests/06: API token CRUD, logout, health check - tests/07: AI provider/model/version/pricing CRUD - tests/08: admin user CRUD, role CRUD All tests use consistent checkBackendAvailable() + uiLogin() pattern with graceful degradation (test.skip) when backend is unreachable.
162 lines
5.8 KiB
TypeScript
162 lines
5.8 KiB
TypeScript
import { test, expect } from "@playwright/test";
|
|
|
|
const ADMIN_USER = process.env.ADMIN_TEST_USERNAME || "admin";
|
|
const ADMIN_PASS = process.env.ADMIN_TEST_PASSWORD || "admin123";
|
|
|
|
async function checkBackendAvailable(): Promise<boolean> {
|
|
try {
|
|
const ctrl = new AbortController();
|
|
const id = setTimeout(() => ctrl.abort(), 2000);
|
|
const res = await fetch("http://localhost:3001/api/health", { signal: ctrl.signal });
|
|
clearTimeout(id);
|
|
return res.ok;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
async function uiLogin(page: Parameters<typeof test>[0]): Promise<boolean> {
|
|
try {
|
|
await page.goto("/login");
|
|
await page.fill("input#username", ADMIN_USER);
|
|
await page.fill("input#password", ADMIN_PASS);
|
|
await page.click('button[type="submit"]');
|
|
await page.waitForURL((url) => !url.toString().includes("/login"), { timeout: 8000 });
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
test.describe("AI Provider/Model/Version CRUD API (需要 Rust 后端)", () => {
|
|
test("GET /api/admin/ai/providers 返回提供商列表(需要 Rust 后端)", async ({ page }) => {
|
|
if (!await checkBackendAvailable()) { test.skip(); }
|
|
if (!await uiLogin(page)) { test.skip(); }
|
|
|
|
const res = await page.request.get("/api/admin/ai/providers");
|
|
expect([200, 500]).toContain(res.status());
|
|
if (res.status() === 200) {
|
|
const data = await res.json();
|
|
expect(Array.isArray(data.providers)).toBe(true);
|
|
}
|
|
});
|
|
|
|
test("POST /api/admin/ai/providers 创建提供商(需要 Rust 后端)", async ({ page }) => {
|
|
if (!await checkBackendAvailable()) { test.skip(); }
|
|
if (!await uiLogin(page)) { test.skip(); }
|
|
|
|
const res = await page.request.post("/api/admin/ai/providers", {
|
|
data: { name: "Test Provider", api_type: "openai" },
|
|
});
|
|
expect([200, 201, 400, 401, 500]).toContain(res.status());
|
|
});
|
|
|
|
test("PATCH /api/admin/ai/providers?id= 缺少id返回400", async ({ page }) => {
|
|
if (!await checkBackendAvailable()) { test.skip(); }
|
|
if (!await uiLogin(page)) { test.skip(); }
|
|
|
|
const res = await page.request.patch("/api/admin/ai/providers", {
|
|
data: { name: "Updated" },
|
|
});
|
|
expect(res.status()).toBe(400);
|
|
});
|
|
|
|
test("DELETE /api/admin/ai/providers?id= 缺少id返回400", async ({ page }) => {
|
|
if (!await checkBackendAvailable()) { test.skip(); }
|
|
if (!await uiLogin(page)) { test.skip(); }
|
|
|
|
const res = await page.request.delete("/api/admin/ai/providers");
|
|
expect(res.status()).toBe(400);
|
|
});
|
|
|
|
test("GET /api/admin/ai/models 返回模型列表(需要 Rust 后端)", async ({ page }) => {
|
|
if (!await checkBackendAvailable()) { test.skip(); }
|
|
if (!await uiLogin(page)) { test.skip(); }
|
|
|
|
const res = await page.request.get("/api/admin/ai/models");
|
|
expect([200, 500]).toContain(res.status());
|
|
if (res.status() === 200) {
|
|
const data = await res.json();
|
|
expect(Array.isArray(data.models)).toBe(true);
|
|
}
|
|
});
|
|
|
|
test("POST /api/admin/ai/models 创建模型(需要 Rust 后端)", async ({ page }) => {
|
|
if (!await checkBackendAvailable()) { test.skip(); }
|
|
if (!await uiLogin(page)) { test.skip(); }
|
|
|
|
const res = await page.request.post("/api/admin/ai/models", {
|
|
data: { model_id: "test-model", provider: "openai" },
|
|
});
|
|
expect([200, 201, 400, 401, 500]).toContain(res.status());
|
|
});
|
|
|
|
test("PATCH /api/admin/ai/models?id= 缺少id返回400", async ({ page }) => {
|
|
if (!await checkBackendAvailable()) { test.skip(); }
|
|
if (!await uiLogin(page)) { test.skip(); }
|
|
|
|
const res = await page.request.patch("/api/admin/ai/models", {
|
|
data: { context_length: 8192 },
|
|
});
|
|
expect(res.status()).toBe(400);
|
|
});
|
|
|
|
test("DELETE /api/admin/ai/models?id= 缺少id返回400", async ({ page }) => {
|
|
if (!await checkBackendAvailable()) { test.skip(); }
|
|
if (!await uiLogin(page)) { test.skip(); }
|
|
|
|
const res = await page.request.delete("/api/admin/ai/models");
|
|
expect(res.status()).toBe(400);
|
|
});
|
|
|
|
test("GET /api/admin/ai/versions 返回版本列表(需要 Rust 后端)", async ({ page }) => {
|
|
if (!await checkBackendAvailable()) { test.skip(); }
|
|
if (!await uiLogin(page)) { test.skip(); }
|
|
|
|
const res = await page.request.get("/api/admin/ai/versions");
|
|
expect([200, 500]).toContain(res.status());
|
|
if (res.status() === 200) {
|
|
const data = await res.json();
|
|
expect(Array.isArray(data.versions)).toBe(true);
|
|
}
|
|
});
|
|
|
|
test("POST /api/admin/ai/versions 创建版本(需要 Rust 后端)", async ({ page }) => {
|
|
if (!await checkBackendAvailable()) { test.skip(); }
|
|
if (!await uiLogin(page)) { test.skip(); }
|
|
|
|
const res = await page.request.post("/api/admin/ai/versions", {
|
|
data: { version: "1.0.0", model_id: "test" },
|
|
});
|
|
expect([200, 201, 400, 401, 500]).toContain(res.status());
|
|
});
|
|
|
|
test("PATCH /api/admin/ai/versions?id= 缺少id返回400", async ({ page }) => {
|
|
if (!await checkBackendAvailable()) { test.skip(); }
|
|
if (!await uiLogin(page)) { test.skip(); }
|
|
|
|
const res = await page.request.patch("/api/admin/ai/versions", {
|
|
data: { is_default: true },
|
|
});
|
|
expect(res.status()).toBe(400);
|
|
});
|
|
|
|
test("DELETE /api/admin/ai/versions?id= 缺少id返回400", async ({ page }) => {
|
|
if (!await checkBackendAvailable()) { test.skip(); }
|
|
if (!await uiLogin(page)) { test.skip(); }
|
|
|
|
const res = await page.request.delete("/api/admin/ai/versions");
|
|
expect(res.status()).toBe(400);
|
|
});
|
|
|
|
test("PATCH /api/admin/ai/pricing/[id] 缺少配置返回500", async ({ page }) => {
|
|
if (!await checkBackendAvailable()) { test.skip(); }
|
|
if (!await uiLogin(page)) { test.skip(); }
|
|
|
|
const res = await page.request.patch("/api/admin/ai/pricing/test-id", {
|
|
data: { input_price: 0.001 },
|
|
});
|
|
expect([400, 401, 500]).toContain(res.status());
|
|
});
|
|
});
|