2
0

api-complete.test.ts 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. /**
  2. * 用户和 API Key 管理完整 E2E 测试
  3. *
  4. * 📋 测试范围:
  5. * - 用户 CRUD 操作
  6. * - Key CRUD 操作
  7. * - 完整业务流程
  8. *
  9. * ✅ 全部通过的自动化测试脚本
  10. *
  11. * 🔑 认证方式:Cookie (auth-token)
  12. * ⚙️ 前提:开发服务器运行在 http://localhost:13500
  13. * 🧹 清理:测试完成后自动清理数据
  14. */
  15. import { afterAll, beforeAll, describe, expect, test } from "vitest";
  16. import { loginAndGetAuthToken } from "./_helpers/auth";
  17. // ==================== 配置 ====================
  18. const API_BASE_URL = process.env.API_BASE_URL || "http://localhost:13500/api/actions";
  19. const ADMIN_KEY = process.env.TEST_ADMIN_TOKEN || process.env.ADMIN_TOKEN;
  20. const run = ADMIN_KEY ? describe : describe.skip;
  21. let authToken: string | undefined;
  22. const testData = {
  23. userIds: [] as number[],
  24. };
  25. // ==================== 辅助函数 ====================
  26. beforeAll(async () => {
  27. if (!ADMIN_KEY) return;
  28. authToken = await loginAndGetAuthToken(API_BASE_URL, ADMIN_KEY);
  29. });
  30. async function callApi(module: string, action: string, body: Record<string, unknown> = {}) {
  31. if (!authToken) {
  32. throw new Error("E2E tests require ADMIN_TOKEN/TEST_ADMIN_TOKEN (used to login)");
  33. }
  34. const response = await fetch(`${API_BASE_URL}/${module}/${action}`, {
  35. method: "POST",
  36. headers: {
  37. "Content-Type": "application/json",
  38. Authorization: `Bearer ${authToken}`,
  39. Cookie: `auth-token=${authToken}`,
  40. },
  41. body: JSON.stringify(body),
  42. });
  43. const contentType = response.headers.get("content-type");
  44. if (contentType?.includes("application/json")) {
  45. const data = await response.json();
  46. return { response, data };
  47. }
  48. const text = await response.text();
  49. return { response, data: { ok: false, error: `非JSON响应: ${text}` } };
  50. }
  51. async function expectSuccess(module: string, action: string, body: Record<string, unknown> = {}) {
  52. const { response, data } = await callApi(module, action, body);
  53. expect(response.ok).toBe(true);
  54. expect(data.ok).toBe(true);
  55. return data.data;
  56. }
  57. // ==================== 测试清理 ====================
  58. afterAll(async () => {
  59. if (!authToken) return;
  60. console.log(`\n🧹 清理 ${testData.userIds.length} 个测试用户...`);
  61. for (const userId of testData.userIds) {
  62. try {
  63. await callApi("users", "removeUser", { userId });
  64. } catch (_e) {
  65. // 忽略清理错误
  66. }
  67. }
  68. console.log("✅ 清理完成\n");
  69. });
  70. // ==================== 测试 ====================
  71. run("用户和 Key 管理 - E2E 测试", () => {
  72. let user1Id: number;
  73. let user2Id: number;
  74. test("✅ 1. 创建第一个用户", async () => {
  75. const result = await expectSuccess("users", "addUser", {
  76. name: `E2E用户1_${Date.now()}`,
  77. note: "测试用户1",
  78. rpm: 100,
  79. dailyQuota: 50,
  80. });
  81. expect(result.user).toBeDefined();
  82. expect(result.defaultKey).toBeDefined();
  83. expect(result.defaultKey.key).toMatch(/^sk-[a-f0-9]{32}$/);
  84. user1Id = result.user.id;
  85. testData.userIds.push(user1Id);
  86. console.log(` ✅ 用户1 ID: ${user1Id}`);
  87. });
  88. test("✅ 2. 创建第二个用户(带限额)", async () => {
  89. const result = await expectSuccess("users", "addUser", {
  90. name: `E2E用户2_${Date.now()}`,
  91. rpm: 200,
  92. dailyQuota: 100,
  93. limit5hUsd: 50,
  94. limitWeeklyUsd: 300,
  95. tags: ["test"],
  96. });
  97. user2Id = result.user.id;
  98. testData.userIds.push(user2Id);
  99. console.log(` ✅ 用户2 ID: ${user2Id}`);
  100. });
  101. test("✅ 3. 获取用户列表", async () => {
  102. const users = await expectSuccess("users", "getUsers");
  103. expect(Array.isArray(users)).toBe(true);
  104. expect(users.length).toBeGreaterThanOrEqual(2);
  105. const user1 = users.find((u: any) => u.id === user1Id);
  106. expect(user1).toBeDefined();
  107. });
  108. test("✅ 4. 编辑用户信息", async () => {
  109. await expectSuccess("users", "editUser", {
  110. userId: user1Id,
  111. rpm: 150,
  112. dailyQuota: 80,
  113. });
  114. const users = await expectSuccess("users", "getUsers");
  115. const user = users.find((u: any) => u.id === user1Id);
  116. expect(user.rpm).toBe(150);
  117. });
  118. test("✅ 5. 禁用和启用用户(通过 editUser)", async () => {
  119. // 禁用用户
  120. await expectSuccess("users", "editUser", {
  121. userId: user1Id,
  122. isEnabled: false,
  123. });
  124. let users = await expectSuccess("users", "getUsers");
  125. let user = users.find((u: any) => u.id === user1Id);
  126. expect(user.isEnabled).toBe(false);
  127. // 启用用户
  128. await expectSuccess("users", "editUser", {
  129. userId: user1Id,
  130. isEnabled: true,
  131. });
  132. users = await expectSuccess("users", "getUsers");
  133. user = users.find((u: any) => u.id === user1Id);
  134. expect(user.isEnabled).toBe(true);
  135. });
  136. test("✅ 6. 获取用户的 Keys", async () => {
  137. const keys = await expectSuccess("keys", "getKeys", { userId: user1Id });
  138. expect(Array.isArray(keys)).toBe(true);
  139. expect(keys.length).toBeGreaterThanOrEqual(1);
  140. // 验证 Key 格式(管理员可能看到完整 Key 或脱敏 Key)
  141. const keyValue = keys[0].key;
  142. const isFullKey = /^sk-[a-f0-9]{32}$/.test(keyValue); // 完整 Key
  143. const isMaskedKey = /^sk-\*+[a-f0-9]{8}$/.test(keyValue); // 脱敏 Key
  144. expect(isFullKey || isMaskedKey).toBe(true);
  145. });
  146. test("✅ 7. 为用户创建新 Key", async () => {
  147. const result = await expectSuccess("keys", "addKey", {
  148. userId: user1Id,
  149. name: `E2EKey_${Date.now()}`,
  150. });
  151. expect(result.generatedKey).toMatch(/^sk-[a-f0-9]{32}$/);
  152. console.log(` ✅ Key: ${result.generatedKey}`);
  153. });
  154. test("✅ 8. 创建带限额的 Key", async () => {
  155. const result = await expectSuccess("keys", "addKey", {
  156. userId: user2Id,
  157. name: `E2E限额Key_${Date.now()}`,
  158. limitDailyUsd: 5,
  159. limit5hUsd: 10,
  160. });
  161. expect(result.generatedKey).toBeDefined();
  162. });
  163. test("✅ 9. 验证 Key 数量", async () => {
  164. const keys = await expectSuccess("keys", "getKeys", { userId: user1Id });
  165. expect(keys.length).toBeGreaterThanOrEqual(2); // 默认Key + 新建的Key
  166. });
  167. test("✅ 10. 完整流程测试", async () => {
  168. // 创建用户
  169. const createResult = await expectSuccess("users", "addUser", {
  170. name: `E2E完整流程_${Date.now()}`,
  171. rpm: 60,
  172. dailyQuota: 10,
  173. });
  174. const userId = createResult.user.id;
  175. testData.userIds.push(userId);
  176. // 创建额外Key
  177. await expectSuccess("keys", "addKey", {
  178. userId,
  179. name: `流程Key1_${Date.now()}`,
  180. });
  181. await expectSuccess("keys", "addKey", {
  182. userId,
  183. name: `流程Key2_${Date.now()}`,
  184. });
  185. // 验证 Keys
  186. const keys = await expectSuccess("keys", "getKeys", { userId });
  187. expect(keys.length).toBe(3); // 1默认 + 2新建
  188. // 删除用户(自动删除所有Keys)
  189. await expectSuccess("users", "removeUser", { userId });
  190. // 验证已删除
  191. const users = await expectSuccess("users", "getUsers");
  192. const deletedUser = users.find((u: any) => u.id === userId);
  193. expect(deletedUser).toBeUndefined();
  194. console.log(` ✅ 完整流程通过`);
  195. });
  196. });