route.test.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. // app/api/auth/login/route.test.js
  2. import { describe, it, expect, vi, beforeEach } from "vitest";
  3. // 1) Mocks
  4. vi.mock("@/lib/db", () => ({
  5. getDb: vi.fn(),
  6. }));
  7. vi.mock("@/models/user", () => ({
  8. default: {
  9. findOne: vi.fn(),
  10. },
  11. }));
  12. vi.mock("@/lib/auth/session", () => ({
  13. createSession: vi.fn(),
  14. }));
  15. vi.mock("bcryptjs", () => {
  16. const compare = vi.fn();
  17. return {
  18. default: { compare },
  19. compare,
  20. };
  21. });
  22. // 2) Imports AFTER the mocks
  23. import { getDb } from "@/lib/db";
  24. import User from "@/models/user";
  25. import { createSession } from "@/lib/auth/session";
  26. import { compare as bcryptCompare } from "bcryptjs";
  27. import { POST } from "./route";
  28. function createRequestStub(body) {
  29. return {
  30. async json() {
  31. return body;
  32. },
  33. };
  34. }
  35. describe("POST /api/auth/login", () => {
  36. beforeEach(() => {
  37. vi.clearAllMocks();
  38. getDb.mockResolvedValue({}); // we only need it to "connect"
  39. });
  40. it("logs in successfully with correct credentials", async () => {
  41. const user = {
  42. _id: "507f1f77bcf86cd799439011",
  43. username: "branchuser",
  44. passwordHash: "hashed-password",
  45. role: "branch",
  46. branchId: "NL01",
  47. };
  48. User.findOne.mockReturnValue({
  49. exec: vi.fn().mockResolvedValue(user),
  50. });
  51. bcryptCompare.mockResolvedValue(true);
  52. const request = createRequestStub({
  53. username: "BranchUser", // mixed case, should be normalized
  54. password: "secret-password",
  55. });
  56. const response = await POST(request);
  57. const json = await response.json();
  58. expect(response.status).toBe(200);
  59. expect(json).toEqual({ ok: true });
  60. expect(getDb).toHaveBeenCalledTimes(1);
  61. expect(User.findOne).toHaveBeenCalledWith({ username: "branchuser" });
  62. expect(bcryptCompare).toHaveBeenCalledWith(
  63. "secret-password",
  64. "hashed-password"
  65. );
  66. expect(createSession).toHaveBeenCalledWith({
  67. userId: "507f1f77bcf86cd799439011",
  68. role: "branch",
  69. branchId: "NL01",
  70. });
  71. });
  72. it("returns 401 when user does not exist", async () => {
  73. User.findOne.mockReturnValue({
  74. exec: vi.fn().mockResolvedValue(null),
  75. });
  76. const request = createRequestStub({
  77. username: "unknownuser",
  78. password: "some-password",
  79. });
  80. const response = await POST(request);
  81. const json = await response.json();
  82. expect(response.status).toBe(401);
  83. expect(json).toEqual({ error: "Invalid credentials" });
  84. expect(createSession).not.toHaveBeenCalled();
  85. });
  86. it("returns 401 when passwordHash is missing (defensive)", async () => {
  87. User.findOne.mockReturnValue({
  88. exec: vi.fn().mockResolvedValue({
  89. _id: "507f1f77bcf86cd799439099",
  90. username: "branchuser",
  91. // passwordHash missing on purpose
  92. role: "branch",
  93. branchId: "NL01",
  94. }),
  95. });
  96. const request = createRequestStub({
  97. username: "branchuser",
  98. password: "secret-password",
  99. });
  100. const response = await POST(request);
  101. const json = await response.json();
  102. expect(response.status).toBe(401);
  103. expect(json).toEqual({ error: "Invalid credentials" });
  104. expect(bcryptCompare).not.toHaveBeenCalled();
  105. expect(createSession).not.toHaveBeenCalled();
  106. });
  107. it("returns 401 when password is incorrect", async () => {
  108. const user = {
  109. _id: "507f1f77bcf86cd799439012",
  110. username: "branchuser",
  111. passwordHash: "hashed-password",
  112. role: "branch",
  113. branchId: "NL02",
  114. };
  115. User.findOne.mockReturnValue({
  116. exec: vi.fn().mockResolvedValue(user),
  117. });
  118. bcryptCompare.mockResolvedValue(false);
  119. const request = createRequestStub({
  120. username: "branchuser",
  121. password: "wrong-password",
  122. });
  123. const response = await POST(request);
  124. const json = await response.json();
  125. expect(response.status).toBe(401);
  126. expect(json).toEqual({ error: "Invalid credentials" });
  127. expect(createSession).not.toHaveBeenCalled();
  128. });
  129. it("returns 400 when username or password is missing", async () => {
  130. const request = createRequestStub({
  131. username: "only-username",
  132. });
  133. const response = await POST(request);
  134. const json = await response.json();
  135. expect(response.status).toBe(400);
  136. expect(json).toEqual({ error: "Missing username or password" });
  137. expect(User.findOne).not.toHaveBeenCalled();
  138. expect(createSession).not.toHaveBeenCalled();
  139. });
  140. it("returns 500 when an unexpected error occurs", async () => {
  141. User.findOne.mockImplementation(() => {
  142. throw new Error("DB failure");
  143. });
  144. const request = createRequestStub({
  145. username: "branchuser",
  146. password: "secret-password",
  147. });
  148. const response = await POST(request);
  149. const json = await response.json();
  150. expect(response.status).toBe(500);
  151. expect(json).toEqual({ error: "Internal server error" });
  152. expect(createSession).not.toHaveBeenCalled();
  153. });
  154. });