gitdataai/admin/tests/06-api-tokens-auth.spec.ts
ZhenYi a7e31d5649 feat(tests): add comprehensive Playwright integration tests for all API endpoints
- 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.
2026-04-20 22:37:05 +08:00

109 lines
3.4 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("API Token 管理 API", () => {
test("GET /api/api-tokens 返回 Token 列表", async ({ page }) => {
if (!await checkBackendAvailable()) { test.skip(); }
if (!await uiLogin(page)) { test.skip(); }
const res = await page.request.get("/api/api-tokens");
expect(res.status()).toBe(200);
const data = await res.json();
expect(Array.isArray(data.tokens)).toBe(true);
});
test("POST /api/api-tokens 创建新 Token", async ({ page }) => {
if (!await checkBackendAvailable()) { test.skip(); }
if (!await uiLogin(page)) { test.skip(); }
const tokenName = `test_token_${Date.now()}`;
const res = await page.request.post("/api/api-tokens", {
data: {
name: tokenName,
permissions: ["platform:read"],
expiresInDays: 7,
},
});
expect(res.status()).toBe(201);
const data = await res.json();
expect(data).toHaveProperty("id");
expect(data).toHaveProperty("token");
expect(data.name).toBe(tokenName);
if (data.id) {
await page.request.delete(`/api/api-tokens/${data.id}`);
}
});
test("POST /api/api-tokens Token 名称为空返回 400", async ({ page }) => {
if (!await checkBackendAvailable()) { test.skip(); }
if (!await uiLogin(page)) { test.skip(); }
const res = await page.request.post("/api/api-tokens", {
data: { name: "", permissions: [] },
});
expect(res.status()).toBe(400);
});
test("DELETE /api/api-tokens/[id] 无效 ID 返回 400", async ({ page }) => {
if (!await checkBackendAvailable()) { test.skip(); }
if (!await uiLogin(page)) { test.skip(); }
const res = await page.request.delete("/api/api-tokens/not_a_number");
expect(res.status()).toBe(400);
});
});
test.describe("认证登出 API", () => {
test("POST /api/auth/logout 登出成功", async ({ page }) => {
if (!await checkBackendAvailable()) { test.skip(); }
if (!await uiLogin(page)) { test.skip(); }
const res = await page.request.post("/api/auth/logout");
expect(res.status()).toBe(200);
const data = await res.json();
expect(data.success).toBe(true);
});
test("POST /api/auth/logout 未登录返回 401", async ({ request }) => {
const res = await request.post("/api/auth/logout");
expect(res.status()).toBe(401);
});
});
test.describe("健康检查 API", () => {
test("GET /api/health 无需认证返回 ok", async ({ page }) => {
const res = await page.request.get("/api/health");
expect(res.status()).toBe(200);
const data = await res.json();
expect(data.status).toBe("ok");
});
});