route.test.js 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. /* @vitest-environment node */
  2. import { describe, it, expect, vi, beforeEach } from "vitest";
  3. vi.mock("@/lib/auth/session", () => ({
  4. getSession: vi.fn(),
  5. }));
  6. vi.mock("@/lib/search", () => ({
  7. search: vi.fn(),
  8. }));
  9. import { getSession } from "@/lib/auth/session";
  10. import { search as searchBackend } from "@/lib/search";
  11. import { GET, dynamic } from "./route.js";
  12. describe("GET /api/search", () => {
  13. beforeEach(() => {
  14. vi.clearAllMocks();
  15. });
  16. it('exports dynamic="force-dynamic"', () => {
  17. expect(dynamic).toBe("force-dynamic");
  18. });
  19. it("returns 401 when unauthenticated", async () => {
  20. getSession.mockResolvedValue(null);
  21. const req = new Request("http://localhost/api/search?q=test&branch=NL01");
  22. const res = await GET(req);
  23. expect(res.status).toBe(401);
  24. expect(await res.json()).toEqual({
  25. error: { message: "Unauthorized", code: "AUTH_UNAUTHENTICATED" },
  26. });
  27. });
  28. it("returns 403 when branch user tries to access a different branch", async () => {
  29. getSession.mockResolvedValue({
  30. userId: "u1",
  31. role: "branch",
  32. branchId: "NL01",
  33. });
  34. const req = new Request("http://localhost/api/search?q=test&branch=NL02");
  35. const res = await GET(req);
  36. expect(res.status).toBe(403);
  37. expect(await res.json()).toEqual({
  38. error: { message: "Forbidden", code: "AUTH_FORBIDDEN_BRANCH" },
  39. });
  40. });
  41. it("returns 400 when missing both q and date range", async () => {
  42. getSession.mockResolvedValue({
  43. userId: "u2",
  44. role: "admin",
  45. branchId: null,
  46. });
  47. const req = new Request("http://localhost/api/search?branch=NL01");
  48. const res = await GET(req);
  49. expect(res.status).toBe(400);
  50. expect(await res.json()).toMatchObject({
  51. error: {
  52. code: "VALIDATION_SEARCH_MISSING_FILTER",
  53. },
  54. });
  55. });
  56. it("returns 400 for invalid date format", async () => {
  57. getSession.mockResolvedValue({
  58. userId: "u2",
  59. role: "admin",
  60. branchId: null,
  61. });
  62. const req = new Request(
  63. "http://localhost/api/search?branch=NL01&q=x&from=2025/01/01"
  64. );
  65. const res = await GET(req);
  66. expect(res.status).toBe(400);
  67. expect(await res.json()).toMatchObject({
  68. error: {
  69. code: "VALIDATION_SEARCH_DATE",
  70. },
  71. });
  72. });
  73. it("returns 200 and passes through items + nextCursor", async () => {
  74. getSession.mockResolvedValue({
  75. userId: "u2",
  76. role: "admin",
  77. branchId: null,
  78. });
  79. searchBackend.mockResolvedValue({
  80. items: [
  81. {
  82. branch: "NL20",
  83. date: "2025-12-18",
  84. year: "2025",
  85. month: "12",
  86. day: "18",
  87. filename: "x.pdf",
  88. relativePath: "NL20/2025/12/18/x.pdf",
  89. },
  90. ],
  91. nextCursor: "abc",
  92. total: 1,
  93. });
  94. const req = new Request(
  95. "http://localhost/api/search?branch=NL20&q=bridgestone&limit=100"
  96. );
  97. const res = await GET(req);
  98. expect(res.status).toBe(200);
  99. expect(await res.json()).toEqual({
  100. items: [
  101. {
  102. branch: "NL20",
  103. date: "2025-12-18",
  104. year: "2025",
  105. month: "12",
  106. day: "18",
  107. filename: "x.pdf",
  108. relativePath: "NL20/2025/12/18/x.pdf",
  109. },
  110. ],
  111. nextCursor: "abc",
  112. total: 1,
  113. });
  114. expect(searchBackend).toHaveBeenCalledTimes(1);
  115. });
  116. });