keys-actions.test.ts 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710
  1. /**
  2. * API Key 管理模块 API 测试
  3. *
  4. * ⚠️ 状态:待重构为集成测试
  5. * 详见:tests/DIAGNOSIS-FINAL.md
  6. *
  7. * ---
  8. *
  9. * 测试范围:
  10. * - getKeys() - 获取 Key 列表
  11. * - addKey() - 创建 Key
  12. * - editKey() - 编辑 Key
  13. * - removeKey() - 删除 Key
  14. * - getKeysWithStatistics() - 获取 Key 统计信息
  15. * - getKeyLimitUsage() - 获取 Key 限额使用情况
  16. *
  17. * 测试场景:
  18. * - CRUD 操作
  19. * - 权限控制(用户只能管理自己的 Key)
  20. * - Key 限额验证(不能超过用户限额)
  21. * - 供应商分组验证
  22. * - 错误处理
  23. */
  24. import { beforeEach, describe, expect, test } from "vitest";
  25. import { callActionsRoute } from "../test-utils";
  26. const ADMIN_TOKEN = process.env.TEST_ADMIN_TOKEN || "test-admin-token";
  27. const USER_TOKEN = "test-user-token";
  28. // 辅助函数:调用 Key 管理 API
  29. async function callKeysApi(
  30. action: string,
  31. body: Record<string, unknown> = {},
  32. authToken = ADMIN_TOKEN
  33. ) {
  34. const { response, json } = await callActionsRoute({
  35. method: "POST",
  36. pathname: `/api/actions/keys/${action}`,
  37. authToken,
  38. body,
  39. });
  40. return { response, data: json as any };
  41. }
  42. // 辅助函数:调用用户管理 API
  43. async function callUsersApi(
  44. action: string,
  45. body: Record<string, unknown> = {},
  46. authToken = ADMIN_TOKEN
  47. ) {
  48. const { response, json } = await callActionsRoute({
  49. method: "POST",
  50. pathname: `/api/actions/users/${action}`,
  51. authToken,
  52. body,
  53. });
  54. return { response, data: json as any };
  55. }
  56. // ⚠️ 跳过所有测试直到重构为集成测试
  57. describe.skip("Key 管理 - API 测试(待重构)", () => {
  58. test("未登录应返回错误", async () => {
  59. const { response, data } = await callKeysApi("getKeys", { userId: 1 }, undefined);
  60. expect(response.ok).toBe(true);
  61. expect(data.ok).toBe(false);
  62. expect(data.error).toContain("未登录");
  63. });
  64. test("管理员应该可以查看任何用户的 Key", async () => {
  65. // 创建测试用户
  66. const { data: userData } = await callUsersApi("addUser", {
  67. name: `Key测试用户_${Date.now()}`,
  68. rpm: 60,
  69. dailyQuota: 10,
  70. });
  71. const userId = userData.data?.user?.id;
  72. if (!userId) {
  73. console.log("跳过测试:无法创建测试用户");
  74. return;
  75. }
  76. const { response, data } = await callKeysApi("getKeys", { userId });
  77. expect(response.ok).toBe(true);
  78. expect(data.ok).toBe(true);
  79. expect(Array.isArray(data.data)).toBe(true);
  80. });
  81. test("普通用户不能查看其他用户的 Key", async () => {
  82. const { response, data } = await callKeysApi("getKeys", { userId: 999 }, USER_TOKEN);
  83. expect(response.ok).toBe(true);
  84. expect(data.ok).toBe(false);
  85. expect(data.error).toContain("无权限");
  86. });
  87. });
  88. describe.skip("Key 管理 - 创建 Key (addKey)", () => {
  89. let testUserId: number;
  90. beforeEach(async () => {
  91. // 创建测试用户
  92. const { data } = await callUsersApi("addUser", {
  93. name: `Key测试用户_${Date.now()}`,
  94. rpm: 60,
  95. dailyQuota: 10,
  96. limit5hUsd: 5,
  97. limitWeeklyUsd: 20,
  98. limitMonthlyUsd: 50,
  99. });
  100. testUserId = data.data?.user?.id;
  101. });
  102. test("应该成功创建 Key", async () => {
  103. if (!testUserId) {
  104. console.log("跳过测试:无法创建测试用户");
  105. return;
  106. }
  107. const { response, data } = await callKeysApi("addKey", {
  108. userId: testUserId,
  109. name: `测试Key_${Date.now()}`,
  110. });
  111. expect(response.ok).toBe(true);
  112. expect(data.ok).toBe(true);
  113. expect(data.data).toBeDefined();
  114. expect(data.data.generatedKey).toMatch(/^sk-[a-f0-9]{32}$/);
  115. expect(data.data.name).toBeDefined();
  116. });
  117. test("非管理员不能给其他用户创建 Key", async () => {
  118. if (!testUserId) {
  119. console.log("跳过测试:无法创建测试用户");
  120. return;
  121. }
  122. const { response, data } = await callKeysApi(
  123. "addKey",
  124. {
  125. userId: testUserId,
  126. name: "测试Key",
  127. },
  128. USER_TOKEN
  129. );
  130. expect(response.ok).toBe(true);
  131. expect(data.ok).toBe(false);
  132. expect(data.error).toContain("无权限");
  133. });
  134. test("缺少必需参数应返回错误", async () => {
  135. if (!testUserId) {
  136. console.log("跳过测试:无法创建测试用户");
  137. return;
  138. }
  139. const { response, data } = await callKeysApi("addKey", {
  140. userId: testUserId,
  141. // 缺少 name
  142. });
  143. expect(response.ok).toBe(true);
  144. expect(data.ok).toBe(false);
  145. expect(data.error).toBeDefined();
  146. });
  147. test("创建同名 Key 应返回错误", async () => {
  148. if (!testUserId) {
  149. console.log("跳过测试:无法创建测试用户");
  150. return;
  151. }
  152. const keyName = `重复Key_${Date.now()}`;
  153. // 创建第一个 Key
  154. await callKeysApi("addKey", {
  155. userId: testUserId,
  156. name: keyName,
  157. });
  158. // 尝试创建同名 Key
  159. const { response, data } = await callKeysApi("addKey", {
  160. userId: testUserId,
  161. name: keyName,
  162. });
  163. expect(response.ok).toBe(true);
  164. expect(data.ok).toBe(false);
  165. expect(data.error).toContain("已存在");
  166. });
  167. test("创建带过期时间的 Key", async () => {
  168. if (!testUserId) {
  169. console.log("跳过测试:无法创建测试用户");
  170. return;
  171. }
  172. const futureDate = new Date();
  173. futureDate.setDate(futureDate.getDate() + 30);
  174. const { response, data } = await callKeysApi("addKey", {
  175. userId: testUserId,
  176. name: `过期Key_${Date.now()}`,
  177. expiresAt: futureDate.toISOString().split("T")[0],
  178. });
  179. expect(response.ok).toBe(true);
  180. expect(data.ok).toBe(true);
  181. });
  182. test("创建带限额的 Key", async () => {
  183. if (!testUserId) {
  184. console.log("跳过测试:无法创建测试用户");
  185. return;
  186. }
  187. const { response, data } = await callKeysApi("addKey", {
  188. userId: testUserId,
  189. name: `限额Key_${Date.now()}`,
  190. limit5hUsd: 2,
  191. limitDailyUsd: 5,
  192. limitWeeklyUsd: 10,
  193. limitMonthlyUsd: 20,
  194. limitConcurrentSessions: 2,
  195. });
  196. expect(response.ok).toBe(true);
  197. expect(data.ok).toBe(true);
  198. });
  199. test("Key 限额超过用户限额应返回错误", async () => {
  200. if (!testUserId) {
  201. console.log("跳过测试:无法创建测试用户");
  202. return;
  203. }
  204. const { response, data } = await callKeysApi("addKey", {
  205. userId: testUserId,
  206. name: `超限Key_${Date.now()}`,
  207. limit5hUsd: 100, // 超过用户的 5 USD 限额
  208. });
  209. expect(response.ok).toBe(true);
  210. expect(data.ok).toBe(false);
  211. expect(data.error).toContain("不能超过用户限额");
  212. });
  213. test("创建带 Web UI 登录权限的 Key", async () => {
  214. if (!testUserId) {
  215. console.log("跳过测试:无法创建测试用户");
  216. return;
  217. }
  218. const { response, data } = await callKeysApi("addKey", {
  219. userId: testUserId,
  220. name: `WebUI_Key_${Date.now()}`,
  221. canLoginWebUi: true,
  222. });
  223. expect(response.ok).toBe(true);
  224. expect(data.ok).toBe(true);
  225. });
  226. test("创建带供应商分组的 Key", async () => {
  227. // 先创建带供应商分组的用户
  228. const { data: userData } = await callUsersApi("addUser", {
  229. name: `分组用户_${Date.now()}`,
  230. rpm: 60,
  231. dailyQuota: 10,
  232. providerGroup: "group1,group2",
  233. });
  234. const userId = userData.data?.user?.id;
  235. if (!userId) {
  236. console.log("跳过测试:无法创建测试用户");
  237. return;
  238. }
  239. const { response, data } = await callKeysApi("addKey", {
  240. userId,
  241. name: `分组Key_${Date.now()}`,
  242. providerGroup: "group1",
  243. });
  244. expect(response.ok).toBe(true);
  245. expect(data.ok).toBe(true);
  246. });
  247. test("Key 供应商分组超出用户分组应返回错误", async () => {
  248. if (!testUserId) {
  249. console.log("跳过测试:无法创建测试用户");
  250. return;
  251. }
  252. const { response, data } = await callKeysApi("addKey", {
  253. userId: testUserId,
  254. name: `无效分组Key_${Date.now()}`,
  255. providerGroup: "invalid-group",
  256. });
  257. expect(response.ok).toBe(true);
  258. expect(data.ok).toBe(false);
  259. expect(data.error).toBeDefined();
  260. });
  261. test("创建带缓存策略的 Key", async () => {
  262. if (!testUserId) {
  263. console.log("跳过测试:无法创建测试用户");
  264. return;
  265. }
  266. const { response, data } = await callKeysApi("addKey", {
  267. userId: testUserId,
  268. name: `缓存Key_${Date.now()}`,
  269. cacheTtlPreference: "5m",
  270. });
  271. expect(response.ok).toBe(true);
  272. expect(data.ok).toBe(true);
  273. });
  274. test("创建带滚动日限额的 Key", async () => {
  275. if (!testUserId) {
  276. console.log("跳过测试:无法创建测试用户");
  277. return;
  278. }
  279. const { response, data } = await callKeysApi("addKey", {
  280. userId: testUserId,
  281. name: `滚动限额Key_${Date.now()}`,
  282. limitDailyUsd: 5,
  283. dailyResetMode: "rolling",
  284. });
  285. expect(response.ok).toBe(true);
  286. expect(data.ok).toBe(true);
  287. });
  288. });
  289. describe.skip("Key 管理 - 编辑 Key (editKey)", () => {
  290. let testUserId: number;
  291. let testKeyId: number;
  292. beforeEach(async () => {
  293. // 创建测试用户
  294. const { data: userData } = await callUsersApi("addUser", {
  295. name: `Key测试用户_${Date.now()}`,
  296. rpm: 60,
  297. dailyQuota: 10,
  298. limit5hUsd: 10,
  299. });
  300. testUserId = userData.data?.user?.id;
  301. if (!testUserId) {
  302. return;
  303. }
  304. // 创建测试 Key
  305. const { data: _keyData } = await callKeysApi("addKey", {
  306. userId: testUserId,
  307. name: `待编辑Key_${Date.now()}`,
  308. });
  309. // 获取 Key ID
  310. const keysResponse = await callKeysApi("getKeys", { userId: testUserId });
  311. testKeyId = keysResponse.data.data?.[0]?.id;
  312. });
  313. test("应该成功编辑 Key", async () => {
  314. if (!testKeyId) {
  315. console.log("跳过测试:无法创建测试 Key");
  316. return;
  317. }
  318. const { response, data } = await callKeysApi("editKey", {
  319. keyId: testKeyId,
  320. name: "已修改的Key名称",
  321. });
  322. expect(response.ok).toBe(true);
  323. expect(data.ok).toBe(true);
  324. });
  325. test("非管理员不能编辑其他用户的 Key", async () => {
  326. if (!testKeyId) {
  327. console.log("跳过测试:无法创建测试 Key");
  328. return;
  329. }
  330. const { response, data } = await callKeysApi(
  331. "editKey",
  332. {
  333. keyId: testKeyId,
  334. name: "尝试修改",
  335. },
  336. USER_TOKEN
  337. );
  338. expect(response.ok).toBe(true);
  339. expect(data.ok).toBe(false);
  340. expect(data.error).toContain("无权限");
  341. });
  342. test("更新 Key 限额", async () => {
  343. if (!testKeyId) {
  344. console.log("跳过测试:无法创建测试 Key");
  345. return;
  346. }
  347. const { response, data } = await callKeysApi("editKey", {
  348. keyId: testKeyId,
  349. name: "测试Key",
  350. limit5hUsd: 5,
  351. limitDailyUsd: 8,
  352. });
  353. expect(response.ok).toBe(true);
  354. expect(data.ok).toBe(true);
  355. });
  356. test("更新 Key 过期时间", async () => {
  357. if (!testKeyId) {
  358. console.log("跳过测试:无法创建测试 Key");
  359. return;
  360. }
  361. const futureDate = new Date();
  362. futureDate.setDate(futureDate.getDate() + 60);
  363. const { response, data } = await callKeysApi("editKey", {
  364. keyId: testKeyId,
  365. name: "测试Key",
  366. expiresAt: futureDate.toISOString().split("T")[0],
  367. });
  368. expect(response.ok).toBe(true);
  369. expect(data.ok).toBe(true);
  370. });
  371. test("编辑不存在的 Key 应返回错误", async () => {
  372. const { response, data } = await callKeysApi("editKey", {
  373. keyId: 999999,
  374. name: "不存在的Key",
  375. });
  376. expect(response.ok).toBe(true);
  377. expect(data.ok).toBe(false);
  378. expect(data.error).toContain("密钥不存在");
  379. });
  380. });
  381. describe.skip("Key 管理 - 删除 Key (removeKey)", () => {
  382. let testUserId: number;
  383. let testKeyId: number;
  384. beforeEach(async () => {
  385. // 创建测试用户(会自动创建一个默认 Key)
  386. const { data: userData } = await callUsersApi("addUser", {
  387. name: `Key测试用户_${Date.now()}`,
  388. rpm: 60,
  389. dailyQuota: 10,
  390. });
  391. testUserId = userData.data?.user?.id;
  392. if (!testUserId) {
  393. return;
  394. }
  395. // 创建第二个 Key(确保用户有多个 Key)
  396. await callKeysApi("addKey", {
  397. userId: testUserId,
  398. name: `待删除Key_${Date.now()}`,
  399. });
  400. // 获取第二个 Key 的 ID
  401. const keysResponse = await callKeysApi("getKeys", { userId: testUserId });
  402. testKeyId = keysResponse.data.data?.[1]?.id;
  403. });
  404. test("应该成功删除 Key", async () => {
  405. if (!testKeyId) {
  406. console.log("跳过测试:无法创建测试 Key");
  407. return;
  408. }
  409. const { response, data } = await callKeysApi("removeKey", {
  410. keyId: testKeyId,
  411. });
  412. expect(response.ok).toBe(true);
  413. expect(data.ok).toBe(true);
  414. });
  415. test("非管理员不能删除其他用户的 Key", async () => {
  416. if (!testKeyId) {
  417. console.log("跳过测试:无法创建测试 Key");
  418. return;
  419. }
  420. const { response, data } = await callKeysApi(
  421. "removeKey",
  422. {
  423. keyId: testKeyId,
  424. },
  425. USER_TOKEN
  426. );
  427. expect(response.ok).toBe(true);
  428. expect(data.ok).toBe(false);
  429. expect(data.error).toContain("无权限");
  430. });
  431. test("删除用户最后一个 Key 应返回错误", async () => {
  432. if (!testUserId) {
  433. console.log("跳过测试:无法创建测试用户");
  434. return;
  435. }
  436. // 获取所有 Key
  437. const keysResponse = await callKeysApi("getKeys", { userId: testUserId });
  438. const keys = keysResponse.data.data || [];
  439. // 删除所有 Key 直到只剩一个
  440. for (let i = 0; i < keys.length - 1; i++) {
  441. await callKeysApi("removeKey", { keyId: keys[i].id });
  442. }
  443. // 尝试删除最后一个 Key
  444. const lastKeyId = keys[keys.length - 1].id;
  445. const { response, data } = await callKeysApi("removeKey", {
  446. keyId: lastKeyId,
  447. });
  448. expect(response.ok).toBe(true);
  449. expect(data.ok).toBe(false);
  450. expect(data.error).toContain("至少需要保留一个");
  451. });
  452. test("删除不存在的 Key 应返回错误", async () => {
  453. const { response, data } = await callKeysApi("removeKey", {
  454. keyId: 999999,
  455. });
  456. expect(response.ok).toBe(true);
  457. expect(data.ok).toBe(false);
  458. expect(data.error).toContain("密钥不存在");
  459. });
  460. });
  461. describe.skip("Key 管理 - 获取 Key 统计信息 (getKeysWithStatistics)", () => {
  462. let testUserId: number;
  463. beforeEach(async () => {
  464. // 创建测试用户
  465. const { data } = await callUsersApi("addUser", {
  466. name: `统计测试用户_${Date.now()}`,
  467. rpm: 60,
  468. dailyQuota: 10,
  469. });
  470. testUserId = data.data?.user?.id;
  471. });
  472. test("应该成功获取 Key 统计信息", async () => {
  473. if (!testUserId) {
  474. console.log("跳过测试:无法创建测试用户");
  475. return;
  476. }
  477. const { response, data } = await callKeysApi("getKeysWithStatistics", {
  478. userId: testUserId,
  479. });
  480. expect(response.ok).toBe(true);
  481. expect(data.ok).toBe(true);
  482. expect(Array.isArray(data.data)).toBe(true);
  483. });
  484. test("非管理员不能获取其他用户的统计", async () => {
  485. if (!testUserId) {
  486. console.log("跳过测试:无法创建测试用户");
  487. return;
  488. }
  489. const { response, data } = await callKeysApi(
  490. "getKeysWithStatistics",
  491. {
  492. userId: testUserId,
  493. },
  494. USER_TOKEN
  495. );
  496. expect(response.ok).toBe(true);
  497. expect(data.ok).toBe(false);
  498. expect(data.error).toContain("无权限");
  499. });
  500. });
  501. describe.skip("Key 管理 - 获取 Key 限额使用情况 (getKeyLimitUsage)", () => {
  502. let testUserId: number;
  503. let testKeyId: number;
  504. beforeEach(async () => {
  505. // 创建带限额的测试用户
  506. const { data: userData } = await callUsersApi("addUser", {
  507. name: `限额测试用户_${Date.now()}`,
  508. rpm: 60,
  509. dailyQuota: 10,
  510. limit5hUsd: 10,
  511. limitWeeklyUsd: 50,
  512. limitMonthlyUsd: 200,
  513. });
  514. testUserId = userData.data?.user?.id;
  515. if (!testUserId) {
  516. return;
  517. }
  518. // 获取默认 Key
  519. const keysResponse = await callKeysApi("getKeys", { userId: testUserId });
  520. testKeyId = keysResponse.data.data?.[0]?.id;
  521. });
  522. test("应该成功获取 Key 限额使用情况", async () => {
  523. if (!testKeyId) {
  524. console.log("跳过测试:无法创建测试 Key");
  525. return;
  526. }
  527. const { response, data } = await callKeysApi("getKeyLimitUsage", {
  528. keyId: testKeyId,
  529. });
  530. expect(response.ok).toBe(true);
  531. expect(data.ok).toBe(true);
  532. expect(data.data).toBeDefined();
  533. expect(data.data.cost5h).toBeDefined();
  534. expect(data.data.costDaily).toBeDefined();
  535. expect(data.data.costWeekly).toBeDefined();
  536. expect(data.data.costMonthly).toBeDefined();
  537. expect(data.data.costTotal).toBeDefined();
  538. expect(data.data.concurrentSessions).toBeDefined();
  539. });
  540. test("非管理员不能查看其他用户 Key 的限额", async () => {
  541. if (!testKeyId) {
  542. console.log("跳过测试:无法创建测试 Key");
  543. return;
  544. }
  545. const { response, data } = await callKeysApi(
  546. "getKeyLimitUsage",
  547. {
  548. keyId: testKeyId,
  549. },
  550. USER_TOKEN
  551. );
  552. expect(response.ok).toBe(true);
  553. expect(data.ok).toBe(false);
  554. expect(data.error).toContain("无权限");
  555. });
  556. test("查询不存在的 Key 应返回错误", async () => {
  557. const { response, data } = await callKeysApi("getKeyLimitUsage", {
  558. keyId: 999999,
  559. });
  560. expect(response.ok).toBe(true);
  561. expect(data.ok).toBe(false);
  562. expect(data.error).toContain("密钥不存在");
  563. });
  564. });
  565. describe.skip("Key 管理 - 响应格式验证", () => {
  566. test("所有成功响应应符合 ActionResult 格式", async () => {
  567. // 创建测试用户
  568. const { data: userData } = await callUsersApi("addUser", {
  569. name: `格式测试用户_${Date.now()}`,
  570. rpm: 60,
  571. dailyQuota: 10,
  572. });
  573. const userId = userData.data?.user?.id;
  574. if (!userId) {
  575. console.log("跳过测试:无法创建测试用户");
  576. return;
  577. }
  578. const { response, data } = await callKeysApi("getKeys", { userId });
  579. expect(response.ok).toBe(true);
  580. expect(data).toHaveProperty("ok");
  581. expect(data.ok).toBe(true);
  582. expect(data).toHaveProperty("data");
  583. });
  584. test("所有错误响应应符合 ActionResult 格式", async () => {
  585. const { response, data } = await callKeysApi("getKeys", { userId: 1 }, USER_TOKEN);
  586. expect(response.ok).toBe(true);
  587. expect(data).toHaveProperty("ok");
  588. expect(data.ok).toBe(false);
  589. expect(data).toHaveProperty("error");
  590. expect(typeof data.error).toBe("string");
  591. });
  592. });