notification-settings.test.ts 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. /**
  2. * 通知设置(Webhook Targets / Bindings)E2E 测试
  3. *
  4. * 覆盖范围:
  5. * - 创建/更新/删除推送目标
  6. * - 绑定通知类型与目标
  7. * - 验证创建目标后自动退出 legacy 模式(useLegacyMode=false)
  8. *
  9. * 前提:
  10. * - 开发服务器运行在 http://localhost:13500
  11. * - 已配置 ADMIN_TOKEN(或 TEST_ADMIN_TOKEN)
  12. */
  13. import { afterAll, describe, expect, test } from "vitest";
  14. const API_BASE_URL = process.env.API_BASE_URL || "http://localhost:13500/api/actions";
  15. const ADMIN_TOKEN = process.env.TEST_ADMIN_TOKEN || process.env.ADMIN_TOKEN;
  16. async function callApi(module: string, action: string, body: Record<string, unknown> = {}) {
  17. const response = await fetch(`${API_BASE_URL}/${module}/${action}`, {
  18. method: "POST",
  19. headers: {
  20. "Content-Type": "application/json",
  21. Cookie: `auth-token=${ADMIN_TOKEN}`,
  22. },
  23. body: JSON.stringify(body),
  24. });
  25. const contentType = response.headers.get("content-type");
  26. if (contentType?.includes("application/json")) {
  27. const data = await response.json();
  28. return { response, data };
  29. }
  30. const text = await response.text();
  31. return { response, data: { ok: false, error: `非JSON响应: ${text}` } };
  32. }
  33. async function expectOk(module: string, action: string, body: Record<string, unknown> = {}) {
  34. const { response, data } = await callApi(module, action, body);
  35. expect(response.ok).toBe(true);
  36. expect(data.ok).toBe(true);
  37. return data.data;
  38. }
  39. const testState = {
  40. targetIds: [] as number[],
  41. };
  42. afterAll(async () => {
  43. // 尽量清理测试数据(忽略失败)
  44. for (const id of testState.targetIds) {
  45. try {
  46. await callApi("webhook-targets", "deleteWebhookTargetAction", { id });
  47. } catch (_e) {
  48. // ignore
  49. }
  50. }
  51. });
  52. const run = ADMIN_TOKEN ? describe : describe.skip;
  53. run("通知设置 - Webhook 目标与绑定(E2E)", () => {
  54. let targetId: number;
  55. test("1) 获取通知设置", async () => {
  56. const settings = await expectOk("notifications", "getNotificationSettingsAction");
  57. expect(settings).toBeDefined();
  58. expect(typeof settings.enabled).toBe("boolean");
  59. });
  60. test("2) 创建推送目标(custom)", async () => {
  61. const result = await expectOk("webhook-targets", "createWebhookTargetAction", {
  62. name: `E2E Webhook Target ${Date.now()}`,
  63. providerType: "custom",
  64. webhookUrl: "https://example.com/webhook",
  65. customTemplate: JSON.stringify({ text: "title={{title}}" }),
  66. customHeaders: { "X-Test": "1" },
  67. proxyUrl: null,
  68. proxyFallbackToDirect: false,
  69. isEnabled: true,
  70. });
  71. expect(result).toBeDefined();
  72. expect(result.id).toBeTypeOf("number");
  73. expect(result.providerType).toBe("custom");
  74. targetId = result.id;
  75. testState.targetIds.push(targetId);
  76. });
  77. test("3) 创建目标后应处于新模式(useLegacyMode=false)", async () => {
  78. const settings = await expectOk("notifications", "getNotificationSettingsAction");
  79. expect(settings.useLegacyMode).toBe(false);
  80. });
  81. test("4) 支持局部更新目标配置", async () => {
  82. const updated = await expectOk("webhook-targets", "updateWebhookTargetAction", {
  83. id: targetId,
  84. input: { isEnabled: false },
  85. });
  86. expect(updated.id).toBe(targetId);
  87. expect(updated.isEnabled).toBe(false);
  88. });
  89. test("5) 绑定 daily_leaderboard -> target", async () => {
  90. await expectOk("notification-bindings", "updateBindingsAction", {
  91. type: "daily_leaderboard",
  92. bindings: [{ targetId, isEnabled: true }],
  93. });
  94. const bindings = await expectOk("notification-bindings", "getBindingsForTypeAction", {
  95. type: "daily_leaderboard",
  96. });
  97. expect(Array.isArray(bindings)).toBe(true);
  98. expect(bindings.length).toBe(1);
  99. expect(bindings[0].targetId).toBe(targetId);
  100. expect(bindings[0].target.id).toBe(targetId);
  101. });
  102. test("6) 删除目标应使绑定不可见", async () => {
  103. await expectOk("webhook-targets", "deleteWebhookTargetAction", { id: targetId });
  104. // 从清理列表移除,避免重复删除
  105. testState.targetIds = testState.targetIds.filter((id) => id !== targetId);
  106. const bindings = await expectOk("notification-bindings", "getBindingsForTypeAction", {
  107. type: "daily_leaderboard",
  108. });
  109. expect(Array.isArray(bindings)).toBe(true);
  110. expect(bindings.length).toBe(0);
  111. });
  112. });