-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #919 from COS301-SE-2024/test/backend/increasing-c…
…overagede Test/backend/increasing coveragede
- Loading branch information
Showing
3 changed files
with
333 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
import { Request, Response, NextFunction } from "express"; | ||
import * as giveawayService from "../../src/services/giveaway.service"; | ||
import { getParticipantCount, addParticipant } from "../../src/controllers/giveaway.controller"; | ||
|
||
// Mock dependencies | ||
jest.mock("../../src/services/giveaway.service"); | ||
jest.mock("../../src/config/redis.config", () => ({ | ||
cacheResponse: jest.fn(), | ||
DEFAULT_CACHE_DURATION: 600 | ||
})); | ||
|
||
describe("Giveaway Controller", () => { | ||
let mockRequest: Partial<Request>; | ||
let mockResponse: Partial<Response>; | ||
let nextFunction: NextFunction = jest.fn(); | ||
|
||
beforeEach(() => { | ||
mockRequest = {}; | ||
mockResponse = { | ||
status: jest.fn().mockReturnThis(), | ||
json: jest.fn() | ||
}; | ||
}); | ||
|
||
describe("getParticipantCount", () => { | ||
it("should return participant count and cache the response", async () => { | ||
const mockCount = { count: 100 }; | ||
(giveawayService.getParticipantCount as jest.Mock).mockResolvedValue(mockCount); | ||
|
||
await getParticipantCount(mockRequest as Request, mockResponse as Response, nextFunction); | ||
|
||
expect(giveawayService.getParticipantCount).toHaveBeenCalled(); | ||
expect(mockResponse.status).toHaveBeenCalledWith(200); | ||
expect(mockResponse.json).toHaveBeenCalledWith(mockCount); | ||
}); | ||
|
||
it("should handle errors by calling next function", async () => { | ||
const error = new Error("Test Error"); | ||
(giveawayService.getParticipantCount as jest.Mock).mockRejectedValue(error); | ||
|
||
await getParticipantCount(mockRequest as Request, mockResponse as Response, nextFunction); | ||
|
||
expect(nextFunction).toHaveBeenCalledWith(error); | ||
}); | ||
}); | ||
|
||
describe("addParticipant", () => { | ||
it("should return 400 if any required field is missing", async () => { | ||
mockRequest = { body: { ticketNumber: "12345", name: "John Doe" } }; // Missing email and phoneNumber | ||
|
||
await addParticipant(mockRequest as Request, mockResponse as Response, nextFunction); | ||
|
||
expect(mockResponse.status).toHaveBeenCalledWith(400); | ||
expect(mockResponse.json).toHaveBeenCalledWith({ | ||
Error: "Missing parameter(s): email, phoneNumber" | ||
}); | ||
}); | ||
|
||
it("should add a participant and return the response", async () => { | ||
const mockData = { ticketNumber: "12345", name: "John Doe", email: "[email protected]", phoneNumber: "123456789" }; | ||
const mockResponseData = { success: true }; | ||
mockRequest = { body: mockData }; | ||
(giveawayService.addParticipant as jest.Mock).mockResolvedValue(mockResponseData); | ||
|
||
await addParticipant(mockRequest as Request, mockResponse as Response, nextFunction); | ||
|
||
expect(giveawayService.addParticipant).toHaveBeenCalledWith(mockData); | ||
expect(mockResponse.status).toHaveBeenCalledWith(200); | ||
expect(mockResponse.json).toHaveBeenCalledWith(mockResponseData); | ||
}); | ||
|
||
it("should handle errors by calling next function", async () => { | ||
const error = new Error("Test Error"); | ||
const mockData = { ticketNumber: "12345", name: "John Doe", email: "[email protected]", phoneNumber: "123456789" }; | ||
mockRequest = { body: mockData }; | ||
(giveawayService.addParticipant as jest.Mock).mockRejectedValue(error); | ||
|
||
await addParticipant(mockRequest as Request, mockResponse as Response, nextFunction); | ||
|
||
expect(nextFunction).toHaveBeenCalledWith(error); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
import { GIVEAWAY_TABLE, TICKETS_TABLE } from "../../src/config/dynamodb.config"; | ||
import { addJobToReadQueue, addJobToWriteQueue } from "../../src/services/jobs.service"; | ||
import * as giveawayService from "../../src/services/giveaway.service"; | ||
import { CustomError } from "../../src/errors/CustomError"; | ||
import { deleteCacheKey } from "../../src/config/redis.config"; | ||
|
||
jest.mock("../../src/services/jobs.service"); | ||
jest.mock("../../src/config/redis.config"); | ||
|
||
describe("Giveaway Service", () => { | ||
afterEach(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
describe("getParticipantCount", () => { | ||
it("should return the count of participants", async () => { | ||
const mockResponse = { | ||
Count: 5, | ||
}; | ||
|
||
(addJobToReadQueue as jest.Mock).mockResolvedValue({ finished: jest.fn().mockResolvedValue(mockResponse) }); | ||
|
||
const result = await giveawayService.getParticipantCount(); | ||
|
||
expect(result).toEqual({ count: 5 }); | ||
expect(addJobToReadQueue).toHaveBeenCalledWith(expect.objectContaining({ | ||
params: { | ||
TableName: GIVEAWAY_TABLE, | ||
Select: "COUNT" | ||
} | ||
})); | ||
expect(addJobToReadQueue).toHaveBeenCalledTimes(1); | ||
}); | ||
|
||
it("should return a count of 0 if no participants are found", async () => { | ||
const mockResponse = { | ||
Count: null, | ||
}; | ||
|
||
(addJobToReadQueue as jest.Mock).mockResolvedValue({ finished: jest.fn().mockResolvedValue(mockResponse) }); | ||
|
||
const result = await giveawayService.getParticipantCount(); | ||
|
||
expect(result).toEqual({ count: 0 }); | ||
expect(addJobToReadQueue).toHaveBeenCalledWith(expect.objectContaining({ | ||
params: { | ||
TableName: GIVEAWAY_TABLE, | ||
Select: "COUNT" | ||
} | ||
})); | ||
expect(addJobToReadQueue).toHaveBeenCalledTimes(1); | ||
}); | ||
}); | ||
|
||
describe("addParticipant", () => { | ||
|
||
it("should throw an error if the ticket number is invalid", async () => { | ||
const mockFormData = { | ||
ticketNumber: "INVALID_TICKET", | ||
}; | ||
|
||
const mockTicketResponse = { | ||
Items: [], | ||
}; | ||
|
||
(addJobToReadQueue as jest.Mock).mockResolvedValueOnce({ finished: jest.fn().mockResolvedValue(mockTicketResponse) }); | ||
|
||
await expect(giveawayService.addParticipant(mockFormData)).rejects.toThrow(CustomError); | ||
expect(addJobToReadQueue).toHaveBeenCalledWith(expect.objectContaining({ | ||
params: { | ||
TableName: TICKETS_TABLE, | ||
IndexName: "ticketnumber-index", | ||
KeyConditionExpression: "ticketnumber = :ticketnumber", | ||
ExpressionAttributeValues: { ":ticketnumber": mockFormData.ticketNumber } | ||
} | ||
})); | ||
expect(addJobToReadQueue).toHaveBeenCalledTimes(1); | ||
}); | ||
|
||
it("should throw an error if no entry id is generated", async () => { | ||
const mockFormData = { | ||
email: "[email protected]", | ||
name: "Test User", | ||
phoneNumber: "123456789", | ||
ticketNumber: "TICKET123", | ||
}; | ||
|
||
const mockTicketResponse = { | ||
Items: [{ ticketnumber: "TICKET123" }], | ||
}; | ||
|
||
const mockParticipantCountResponse = { | ||
Count: 1, | ||
}; | ||
|
||
(addJobToReadQueue as jest.Mock) | ||
.mockResolvedValueOnce({ finished: jest.fn().mockResolvedValue(mockTicketResponse) }) | ||
.mockResolvedValueOnce({ finished: jest.fn().mockResolvedValue(mockParticipantCountResponse) }); | ||
|
||
jest.spyOn(Math, 'random').mockReturnValue(0); | ||
|
||
await expect(giveawayService.addParticipant(mockFormData)).rejects.toThrow(CustomError); | ||
|
||
expect(addJobToReadQueue).toHaveBeenCalledTimes(21); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
import { Request, Response, NextFunction } from "express"; | ||
import { getJobStatus, removeCacheKey, removeAllCache } from "../../src/controllers/jobs.controller"; | ||
import * as jobsService from "../../src/services/jobs.service"; | ||
import { deleteAllCache, deleteCacheKey } from "../../src/config/redis.config"; | ||
import { CustomError } from "../../src/errors/CustomError"; | ||
|
||
// Mock dependencies | ||
jest.mock('../../src/services/jobs.service', () => ({ | ||
getJob: jest.fn(), | ||
})); | ||
jest.mock("../../src/config/redis.config"); | ||
|
||
describe("Jobs Controller", () => { | ||
let mockRequest: Partial<Request>; | ||
let mockResponse: Partial<Response>; | ||
let nextFunction: NextFunction = jest.fn(); | ||
|
||
beforeEach(() => { | ||
mockRequest = {}; | ||
mockResponse = { | ||
status: jest.fn().mockReturnThis(), | ||
json: jest.fn() | ||
}; | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
describe("getJobStatus", () => { | ||
it("should return completed status if job is completed", async () => { | ||
const mockJob = { | ||
isCompleted: jest.fn().mockResolvedValue(true), | ||
isFailed: jest.fn(), | ||
returnvalue: { data: "job result" } | ||
}; | ||
mockRequest = { params: { id: "123", type: "type1" } }; | ||
(jobsService.getJob as jest.Mock).mockResolvedValue(mockJob); | ||
|
||
await getJobStatus(mockRequest as Request, mockResponse as Response, nextFunction); | ||
|
||
expect(jobsService.getJob).toHaveBeenCalledWith("123", "type1"); | ||
expect(mockJob.isCompleted).toHaveBeenCalled(); | ||
expect(mockResponse.status).toHaveBeenCalledWith(200); | ||
expect(mockResponse.json).toHaveBeenCalledWith({ status: "completed", result: mockJob.returnvalue }); | ||
}); | ||
|
||
it("should return job in progress if job is not completed", async () => { | ||
const mockJob = { | ||
isCompleted: jest.fn().mockResolvedValue(false), | ||
isFailed: jest.fn().mockResolvedValue(false) | ||
}; | ||
mockRequest = { params: { id: "123", type: "type1" } }; | ||
(jobsService.getJob as jest.Mock).mockResolvedValue(mockJob); | ||
|
||
await getJobStatus(mockRequest as Request, mockResponse as Response, nextFunction); | ||
|
||
expect(jobsService.getJob).toHaveBeenCalledWith("123", "type1"); | ||
expect(mockJob.isCompleted).toHaveBeenCalled(); | ||
expect(mockResponse.status).toHaveBeenCalledWith(200); | ||
expect(mockResponse.json).toHaveBeenCalledWith({ status: "job in progress" }); | ||
}); | ||
|
||
it("should return 500 if job has failed", async () => { | ||
const mockJob = { | ||
isCompleted: jest.fn().mockResolvedValue(false), | ||
isFailed: jest.fn().mockResolvedValue(true), | ||
failedReason: "Job error" | ||
}; | ||
mockRequest = { params: { id: "123", type: "type1" } }; | ||
(jobsService.getJob as jest.Mock).mockResolvedValue(mockJob); | ||
|
||
await getJobStatus(mockRequest as Request, mockResponse as Response, nextFunction); | ||
|
||
expect(nextFunction).toHaveBeenCalledWith(new CustomError("Job error", 500)); | ||
}); | ||
|
||
it("should return 404 if job is not found", async () => { | ||
mockRequest = { params: { id: "123", type: "type1" } }; | ||
(jobsService.getJob as jest.Mock).mockResolvedValue(null); | ||
|
||
await getJobStatus(mockRequest as Request, mockResponse as Response, nextFunction); | ||
|
||
expect(nextFunction).toHaveBeenCalledWith(new CustomError("Job not found", 404)); | ||
}); | ||
|
||
it("should handle errors by calling next function", async () => { | ||
const error = new Error("Test error"); | ||
mockRequest = { params: { id: "123", type: "type1" } }; | ||
(jobsService.getJob as jest.Mock).mockRejectedValue(error); | ||
|
||
await getJobStatus(mockRequest as Request, mockResponse as Response, nextFunction); | ||
|
||
expect(nextFunction).toHaveBeenCalledWith(error); | ||
}); | ||
}); | ||
|
||
describe("removeCacheKey", () => { | ||
it("should return 400 if cache key is missing", async () => { | ||
mockRequest = { params: {} }; | ||
|
||
await removeCacheKey(mockRequest as Request, mockResponse as Response, nextFunction); | ||
|
||
expect(nextFunction).toHaveBeenCalledWith(new CustomError("Missing parameter: key", 400)); | ||
}); | ||
|
||
it("should clear cache for the given key", async () => { | ||
mockRequest = { params: { key: "cache-key" } }; | ||
|
||
await removeCacheKey(mockRequest as Request, mockResponse as Response, nextFunction); | ||
|
||
expect(deleteCacheKey).toHaveBeenCalledWith("cache-key"); | ||
expect(mockResponse.status).toHaveBeenCalledWith(200); | ||
expect(mockResponse.json).toHaveBeenCalledWith({ status: "cache cleared for key cache-key" }); | ||
}); | ||
|
||
it("should handle errors by calling next function", async () => { | ||
const error = new Error("Test error"); | ||
mockRequest = { params: { key: "cache-key" } }; | ||
(deleteCacheKey as jest.Mock).mockRejectedValue(error); | ||
|
||
await removeCacheKey(mockRequest as Request, mockResponse as Response, nextFunction); | ||
|
||
expect(nextFunction).toHaveBeenCalledWith(error); | ||
}); | ||
}); | ||
|
||
describe("removeAllCache", () => { | ||
it("should clear all cache", async () => { | ||
await removeAllCache(mockRequest as Request, mockResponse as Response, nextFunction); | ||
|
||
expect(deleteAllCache).toHaveBeenCalled(); | ||
expect(mockResponse.status).toHaveBeenCalledWith(200); | ||
expect(mockResponse.json).toHaveBeenCalledWith({ status: "all cache cleared" }); | ||
}); | ||
|
||
it("should handle errors by calling next function", async () => { | ||
const error = new Error("Test error"); | ||
(deleteAllCache as jest.Mock).mockRejectedValue(error); | ||
|
||
await removeAllCache(mockRequest as Request, mockResponse as Response, nextFunction); | ||
|
||
expect(nextFunction).toHaveBeenCalledWith(error); | ||
}); | ||
}); | ||
}); |