provider.test.ts 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. import { describe, expect, test, vi } from "vitest";
  2. function sqlToString(sqlObj: unknown): string {
  3. const stack = new Set<object>();
  4. const walk = (node: unknown): string => {
  5. if (node === null || node === undefined) return "";
  6. if (typeof node === "string") {
  7. return node;
  8. }
  9. if (typeof node === "number" || typeof node === "bigint" || typeof node === "boolean") {
  10. return String(node);
  11. }
  12. if (typeof node === "object") {
  13. if (stack.has(node)) return "";
  14. stack.add(node);
  15. try {
  16. const anyNode = node as any;
  17. if (Array.isArray(anyNode)) {
  18. return anyNode.map(walk).join("");
  19. }
  20. if (Object.hasOwn(anyNode, "value")) {
  21. const { value } = anyNode;
  22. if (Array.isArray(value)) {
  23. return value.map(String).join("");
  24. }
  25. if (value === null || value === undefined) return "";
  26. return String(value);
  27. }
  28. if (anyNode.queryChunks) {
  29. return walk(anyNode.queryChunks);
  30. }
  31. } finally {
  32. stack.delete(node);
  33. }
  34. }
  35. return "";
  36. };
  37. return walk(sqlObj);
  38. }
  39. describe("provider repository - updateProviderPrioritiesBatch", () => {
  40. test("returns 0 and does not execute SQL when updates is empty", async () => {
  41. vi.resetModules();
  42. const executeMock = vi.fn(async () => ({ rowCount: 0 }));
  43. vi.doMock("@/drizzle/db", () => ({
  44. db: {
  45. execute: executeMock,
  46. },
  47. }));
  48. const { updateProviderPrioritiesBatch } = await import("@/repository/provider");
  49. const result = await updateProviderPrioritiesBatch([]);
  50. expect(result).toBe(0);
  51. expect(executeMock).not.toHaveBeenCalled();
  52. });
  53. test("generates CASE batch update SQL and returns affected rows", async () => {
  54. vi.resetModules();
  55. const executeMock = vi.fn(async () => ({ rowCount: 2 }));
  56. vi.doMock("@/drizzle/db", () => ({
  57. db: {
  58. execute: executeMock,
  59. },
  60. }));
  61. const { updateProviderPrioritiesBatch } = await import("@/repository/provider");
  62. const result = await updateProviderPrioritiesBatch([
  63. { id: 1, priority: 0 },
  64. { id: 2, priority: 3 },
  65. ]);
  66. expect(result).toBe(2);
  67. expect(executeMock).toHaveBeenCalledTimes(1);
  68. const queryArg = executeMock.mock.calls[0]?.[0];
  69. const sqlText = sqlToString(queryArg).replaceAll(/\s+/g, " ").trim();
  70. expect(sqlText).toContain("UPDATE providers");
  71. expect(sqlText).toContain("SET");
  72. expect(sqlText).toContain("priority = CASE id");
  73. expect(sqlText).toContain("WHEN 1 THEN 0");
  74. expect(sqlText).toContain("WHEN 2 THEN 3");
  75. expect(sqlText).toContain("updated_at = NOW()");
  76. expect(sqlText).toContain("WHERE id IN (1, 2) AND deleted_at IS NULL");
  77. });
  78. test("deduplicates provider ids (last update wins)", async () => {
  79. vi.resetModules();
  80. const executeMock = vi.fn(async () => ({ rowCount: 1 }));
  81. vi.doMock("@/drizzle/db", () => ({
  82. db: {
  83. execute: executeMock,
  84. },
  85. }));
  86. const { updateProviderPrioritiesBatch } = await import("@/repository/provider");
  87. const result = await updateProviderPrioritiesBatch([
  88. { id: 1, priority: 0 },
  89. { id: 1, priority: 2 },
  90. ]);
  91. expect(result).toBe(1);
  92. expect(executeMock).toHaveBeenCalledTimes(1);
  93. const queryArg = executeMock.mock.calls[0]?.[0];
  94. const sqlText = sqlToString(queryArg).replaceAll(/\s+/g, " ").trim();
  95. expect(sqlText).toContain("WHEN 1 THEN 2");
  96. expect(sqlText).toContain("WHERE id IN (1) AND deleted_at IS NULL");
  97. });
  98. });