notification-bindings.test.ts 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. /**
  2. * Notification Bindings Repository 集成测试
  3. *
  4. * 覆盖范围:
  5. * - upsertBindings:新增/更新/删除缺失绑定
  6. * - getBindingsByType:带 target join
  7. * - deleteWebhookTarget 后绑定应被级联删除
  8. */
  9. import { and, eq } from "drizzle-orm";
  10. import { describe, expect, test } from "vitest";
  11. import { db } from "@/drizzle/db";
  12. import { notificationTargetBindings } from "@/drizzle/schema";
  13. import { deleteWebhookTarget, createWebhookTarget } from "@/repository/webhook-targets";
  14. import { getBindingsByType, upsertBindings } from "@/repository/notification-bindings";
  15. const run = process.env.DSN ? describe : describe.skip;
  16. run("Notification Bindings Repository(集成测试)", () => {
  17. test("should upsert bindings and cascade delete on target removal", async () => {
  18. const targetA = await createWebhookTarget({
  19. name: `绑定目标A_${Date.now()}`,
  20. providerType: "custom",
  21. webhookUrl: "https://example.com/webhook",
  22. customTemplate: { text: "title={{title}}" },
  23. isEnabled: true,
  24. });
  25. const targetB = await createWebhookTarget({
  26. name: `绑定目标B_${Date.now()}`,
  27. providerType: "wechat",
  28. webhookUrl: "https://example.com/webhook2",
  29. isEnabled: true,
  30. });
  31. try {
  32. // 1) 插入两条绑定
  33. await upsertBindings("daily_leaderboard", [
  34. {
  35. targetId: targetA.id,
  36. isEnabled: true,
  37. scheduleCron: null,
  38. scheduleTimezone: "Asia/Shanghai",
  39. },
  40. { targetId: targetB.id, isEnabled: false },
  41. ]);
  42. let bindings = await getBindingsByType("daily_leaderboard");
  43. expect(bindings.length).toBeGreaterThanOrEqual(2);
  44. const bindingA = bindings.find((b) => b.targetId === targetA.id);
  45. expect(bindingA).toBeDefined();
  46. expect(bindingA?.isEnabled).toBe(true);
  47. expect(bindingA?.target.providerType).toBe("custom");
  48. // 2) 仅保留 A:应删除 B 的绑定
  49. await upsertBindings("daily_leaderboard", [{ targetId: targetA.id, isEnabled: true }]);
  50. bindings = await getBindingsByType("daily_leaderboard");
  51. expect(bindings.some((b) => b.targetId === targetB.id)).toBe(false);
  52. expect(bindings.some((b) => b.targetId === targetA.id)).toBe(true);
  53. // 3) 删除 targetA:绑定应被级联删除(表级别校验)
  54. await deleteWebhookTarget(targetA.id);
  55. const rows = await db
  56. .select()
  57. .from(notificationTargetBindings)
  58. .where(
  59. and(
  60. eq(notificationTargetBindings.notificationType, "daily_leaderboard"),
  61. eq(notificationTargetBindings.targetId, targetA.id)
  62. )
  63. );
  64. expect(rows.length).toBe(0);
  65. } finally {
  66. // targetA 可能已删除,忽略错误
  67. try {
  68. await deleteWebhookTarget(targetA.id);
  69. } catch (_e) {
  70. // ignore
  71. }
  72. await deleteWebhookTarget(targetB.id);
  73. }
  74. });
  75. });