|
| 1 | +import request from "supertest"; |
| 2 | +import app from "../src/index"; |
| 3 | +import timetablesController from "../src/controllers/timetablesController"; |
| 4 | +import { Request, Response, NextFunction } from "express"; |
| 5 | +import { jest, describe, test, expect, beforeEach } from "@jest/globals"; |
| 6 | +import { supabase } from "../src/db/setupDb"; |
| 7 | +import { authHandler } from "../src/middleware/authHandler"; |
| 8 | +import { |
| 9 | + Json, |
| 10 | + mapValues, |
| 11 | +} from "@pinecone-database/pinecone/dist/pinecone-generated-ts-fetch/db_control"; |
| 12 | +import { stringify } from "qs"; |
| 13 | + |
| 14 | +const mockAuthHandler = (user_id: string) => { |
| 15 | + return (req: Request, res: Response, next: NextFunction) => { |
| 16 | + (req as any).user = { id: user_id }; // Inject user_id dynamically |
| 17 | + next(); |
| 18 | + |
| 19 | + console.log((req as any).user.id); |
| 20 | + }; |
| 21 | +}; |
| 22 | + |
| 23 | +// Mock authHandler globally |
| 24 | +jest.mock("../src/middleware/authHandler", () => ({ |
| 25 | + authHandler: jest.fn() as jest.MockedFunction<typeof authHandler>, |
| 26 | +})); |
| 27 | + |
| 28 | +// Mock timetables data |
| 29 | +const mockTimetables1 = [ |
| 30 | + { |
| 31 | + id: 1, |
| 32 | + name: "Timetable 1", |
| 33 | + user_id: "ab9e6877-f603-4c6a-9832-864e520e4d01", |
| 34 | + }, |
| 35 | + { |
| 36 | + id: 2, |
| 37 | + name: "Timetable 2", |
| 38 | + user_id: "ab9e6877-f603-4c6a-9832-864e520e4d01", |
| 39 | + }, |
| 40 | +]; |
| 41 | + |
| 42 | +jest.mock("../src/db/setupDb", () => ({ |
| 43 | + supabase: { |
| 44 | + from: jest.fn(() => ({ |
| 45 | + select: jest.fn().mockImplementation(() => { |
| 46 | + let mockQuery = { |
| 47 | + filters: {} as Record<string, any>, // Store multiple filters |
| 48 | + |
| 49 | + eq: jest.fn(function (this: any, column: string, value: any) { |
| 50 | + this.filters[column] = value; |
| 51 | + return this; // Ensure method chaining works |
| 52 | + }) as (column: string, value: any) => any, |
| 53 | + |
| 54 | + then: jest.fn(function ( |
| 55 | + this: any, |
| 56 | + callback: (result: { data: any[]; error: null }) => void |
| 57 | + ) { |
| 58 | + // Simulate Supabase filtering logic |
| 59 | + const { user_id, timetable_title } = this.filters; |
| 60 | + |
| 61 | + if ( |
| 62 | + user_id === "valid-user-id" && |
| 63 | + timetable_title === "Test Timetable" |
| 64 | + ) { |
| 65 | + return callback({ |
| 66 | + data: [{ id: 1, title: "Test Timetable", semester: "Fall" }], |
| 67 | + error: null, |
| 68 | + }); |
| 69 | + } |
| 70 | + return callback({ data: [], error: null }); |
| 71 | + }) as ( |
| 72 | + callback: (result: { data: any[]; error: null }) => void |
| 73 | + ) => any, |
| 74 | + }; |
| 75 | + |
| 76 | + return mockQuery; |
| 77 | + }), |
| 78 | + })), |
| 79 | + insert: jest.fn().mockImplementation((data: Json) => { |
| 80 | + return { |
| 81 | + data: [{ ...data[0], id: 1 }], |
| 82 | + error: null, |
| 83 | + }; |
| 84 | + }), |
| 85 | + }, |
| 86 | +})); |
| 87 | + |
| 88 | +/** |
| 89 | +// Mock the Supabase database calls for different user_ids |
| 90 | +jest.mock("../src/db/setupDb", () => ({ |
| 91 | + supabase: { |
| 92 | + schema: jest.fn().mockReturnThis(), |
| 93 | + from: jest.fn().mockReturnThis(), |
| 94 | + select: jest.fn().mockReturnThis(), |
| 95 | + eq: jest.fn().mockImplementation((key: unknown, value: unknown) => { |
| 96 | + const typedKey = key as string; |
| 97 | + const typedValue = value as string; |
| 98 | +
|
| 99 | + // Create an object to hold the key-value pairs for eq() checks |
| 100 | + const eqCalls: { [key: string]: string | undefined } = {}; |
| 101 | + eqCalls[typedKey] = typedValue; |
| 102 | +
|
| 103 | + // Skip the duplicate title check for testing |
| 104 | + if (eqCalls.user_id === "ab9e6877-f603-4c6a-9832-864e520e4d01") { |
| 105 | + if (eqCalls.timetable_title === "Test Timetable") { |
| 106 | + // Simulate that the title doesn't block the creation of a new timetable |
| 107 | + return { data: [], error: null }; // No duplicate found, return an empty array |
| 108 | + } |
| 109 | + return { data: mockTimetables1, error: null }; // Existing timetable found |
| 110 | + } |
| 111 | +
|
| 112 | + if (eqCalls.user_id === "1d3f02df-f926-4c1f-9f41-58ca50816a33") { |
| 113 | + return { data: null, error: null }; |
| 114 | + } |
| 115 | +
|
| 116 | + return { data: null, error: null }; // Default return if no conditions match |
| 117 | + }), |
| 118 | + insert: jest.fn().mockImplementation((data: Json) => { |
| 119 | + return { |
| 120 | + data: [{ ...data[0], id: 1 }], |
| 121 | + error: null, |
| 122 | + }; |
| 123 | + }), |
| 124 | + }, |
| 125 | +})); |
| 126 | + */ |
| 127 | + |
| 128 | +// Spy on the getTimetables method |
| 129 | +jest |
| 130 | + .spyOn(timetablesController, "getTimetables") |
| 131 | + .mockImplementation(timetablesController.getTimetables); |
| 132 | + |
| 133 | +// Spy on the createTimetable method |
| 134 | +jest |
| 135 | + .spyOn(timetablesController, "createTimetable") |
| 136 | + .mockImplementation(timetablesController.createTimetable); |
| 137 | + |
| 138 | +// Spy on the updateTimetable method |
| 139 | +jest |
| 140 | + .spyOn(timetablesController, "updateTimetable") |
| 141 | + .mockImplementation(timetablesController.updateTimetable); |
| 142 | + |
| 143 | +// Spy on the deleteTimetable method |
| 144 | +jest |
| 145 | + .spyOn(timetablesController, "deleteTimetable") |
| 146 | + .mockImplementation(timetablesController.deleteTimetable); |
| 147 | + |
| 148 | +describe("GET /api/timetables", () => { |
| 149 | + beforeEach(() => { |
| 150 | + jest.clearAllMocks(); |
| 151 | + }); |
| 152 | + |
| 153 | + test("should return timetables for user 1", async () => { |
| 154 | + // Initialize the session with user 1's ID |
| 155 | + ( |
| 156 | + authHandler as jest.MockedFunction<typeof authHandler> |
| 157 | + ).mockImplementationOnce( |
| 158 | + mockAuthHandler("ab9e6877-f603-4c6a-9832-864e520e4d01") |
| 159 | + ); |
| 160 | + |
| 161 | + const response = await request(app).get("/api/timetables"); |
| 162 | + |
| 163 | + // Check database interaction and response |
| 164 | + expect(response.statusCode).toBe(200); |
| 165 | + expect(response.body).toEqual(mockTimetables1); |
| 166 | + }); |
| 167 | + |
| 168 | + test("should return 404 if no timetables found", async () => { |
| 169 | + // Initialize the session with a non-existent user ID |
| 170 | + ( |
| 171 | + authHandler as jest.MockedFunction<typeof authHandler> |
| 172 | + ).mockImplementationOnce( |
| 173 | + mockAuthHandler("1d3f02df-f926-4c1f-9f41-58ca50816a33") |
| 174 | + ); |
| 175 | + |
| 176 | + const response = await request(app).get("/api/timetables"); |
| 177 | + |
| 178 | + console.log(response.body); |
| 179 | + // Verify the response status and error message |
| 180 | + expect(response.statusCode).toBe(404); |
| 181 | + expect(response.body).toEqual({ |
| 182 | + error: "Calendar id not found", |
| 183 | + }); |
| 184 | + }); |
| 185 | +}); |
| 186 | + |
| 187 | +describe("POST /api/timetables", () => { |
| 188 | + beforeEach(() => { |
| 189 | + jest.clearAllMocks; |
| 190 | + }); |
| 191 | + |
| 192 | + test("should create a new timetable given timetable title, timetable semester, timetable favorite", async () => { |
| 193 | + const user_id = "1d3f02df-f926-4c1f-9f41-58ca50816a33"; |
| 194 | + const newTimetable = { |
| 195 | + timetable_title: "Minh timetable", |
| 196 | + semester: "Fall 2025", |
| 197 | + favorite: false, |
| 198 | + }; |
| 199 | + |
| 200 | + ( |
| 201 | + authHandler as jest.MockedFunction<typeof authHandler> |
| 202 | + ).mockImplementationOnce(mockAuthHandler(user_id)); |
| 203 | + |
| 204 | + const response = await request(app) |
| 205 | + .post("/api/timetables") |
| 206 | + .send(newTimetable); |
| 207 | + // Check database interaction and response |
| 208 | + expect(response.statusCode).toBe(201); // Created |
| 209 | + expect(response.body).toEqual([ |
| 210 | + { |
| 211 | + id: 1, |
| 212 | + user_id, |
| 213 | + timetable_title: "Minh timetable", |
| 214 | + semester: "Fall 2025", |
| 215 | + favorite: false, |
| 216 | + }, |
| 217 | + ]); |
| 218 | + }); |
| 219 | +}); |
0 commit comments