generated from RealDevSquad/website-template
-
Notifications
You must be signed in to change notification settings - Fork 280
test: add comprehensive tests for nudge application functionality #2543
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
iamitprakash
merged 13 commits into
anuj/nudge-functionality
from
anuj/application-nudge-test
Jan 17, 2026
Merged
Changes from 2 commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
45593f4
test: add comprehensive tests for nudge application functionality
AnujChhikara 0f606dc
chore: add logger utility to discordService and logService for improv…
AnujChhikara b291593
Merge branch 'anuj/nudge-functionality' into anuj/application-nudge-test
AnujChhikara e432894
test: enhance nudge application tests to cover pending status validation
AnujChhikara 5c90a58
Merge branch 'anuj/nudge-functionality' into anuj/application-nudge-test
AnujChhikara 4c54b7a
Merge branch 'anuj/nudge-functionality' into anuj/application-nudge-test
AnujChhikara b0d2d53
refactor: remove duplicate logger import and unused config in discord…
AnujChhikara b71c49a
nit: remove unused logger import
AnujChhikara 66cb1d5
Merge branch 'anuj/nudge-functionality' into anuj/application-nudge-test
AnujChhikara c895e3c
refactor: update nudge application logic and messages
AnujChhikara ac7c3b1
Merge branch 'anuj/nudge-functionality' into anuj/application-nudge-test
AnujChhikara dfa6ec9
Merge branch 'anuj/nudge-functionality' into anuj/application-nudge-test
AnujChhikara efc137e
refactor: nudge model try and catch block
AnujChhikara File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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
This file contains hidden or 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
This file contains hidden or 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
This file contains hidden or 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
This file contains hidden or 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,210 @@ | ||
| import { expect } from "chai"; | ||
| import sinon from "sinon"; | ||
| import { CustomRequest, CustomResponse } from "../../../types/global"; | ||
| const applicationsController = require("../../../controllers/applications"); | ||
| const ApplicationModel = require("../../../models/applications"); | ||
| const { API_RESPONSE_MESSAGES, APPLICATION_ERROR_MESSAGES } = require("../../../constants/application"); | ||
| const { convertDaysToMilliseconds } = require("../../../utils/time"); | ||
|
|
||
| describe("nudgeApplication", () => { | ||
| let req: Partial<CustomRequest>; | ||
| let res: Partial<CustomResponse> & { | ||
| json: sinon.SinonSpy; | ||
| boom: { | ||
| notFound: sinon.SinonSpy; | ||
| unauthorized: sinon.SinonSpy; | ||
| tooManyRequests: sinon.SinonSpy; | ||
| badImplementation: sinon.SinonSpy; | ||
| }; | ||
| }; | ||
| let jsonSpy: sinon.SinonSpy; | ||
| let boomNotFound: sinon.SinonSpy; | ||
| let boomUnauthorized: sinon.SinonSpy; | ||
| let boomTooManyRequests: sinon.SinonSpy; | ||
| let boomBadImplementation: sinon.SinonSpy; | ||
|
|
||
| const mockApplicationId = "test-application-id-123"; | ||
| const mockUserId = "test-user-id-456"; | ||
| const mockOtherUserId = "other-user-id-789"; | ||
|
|
||
| beforeEach(() => { | ||
| jsonSpy = sinon.spy(); | ||
| boomNotFound = sinon.spy(); | ||
| boomUnauthorized = sinon.spy(); | ||
| boomTooManyRequests = sinon.spy(); | ||
| boomBadImplementation = sinon.spy(); | ||
|
|
||
| req = { | ||
| params: { | ||
| applicationId: mockApplicationId, | ||
| }, | ||
| userData: { | ||
| id: mockUserId, | ||
| username: "testuser", | ||
| }, | ||
| }; | ||
|
|
||
| res = { | ||
| json: jsonSpy, | ||
| boom: { | ||
| notFound: boomNotFound, | ||
| unauthorized: boomUnauthorized, | ||
| tooManyRequests: boomTooManyRequests, | ||
| badImplementation: boomBadImplementation, | ||
| }, | ||
| }; | ||
| }); | ||
|
|
||
| afterEach(() => { | ||
| sinon.restore(); | ||
| }); | ||
|
|
||
| describe("Success cases", () => { | ||
| it("should successfully nudge an application when no previous nudge exists", async () => { | ||
| const mockApplication = { | ||
| id: mockApplicationId, | ||
| userId: mockUserId, | ||
| notFound: false, | ||
| lastNudgeAt: null, | ||
| nudgeCount: 0, | ||
| }; | ||
|
|
||
| const getApplicationByIdStub = sinon.stub(ApplicationModel, "getApplicationById").resolves(mockApplication); | ||
| const updateApplicationStub = sinon.stub(ApplicationModel, "updateApplication").resolves(); | ||
|
|
||
| await applicationsController.nudgeApplication(req as CustomRequest, res as CustomResponse); | ||
|
|
||
| expect(getApplicationByIdStub.calledOnce).to.be.true; | ||
| expect(getApplicationByIdStub.calledWith(mockApplicationId)).to.be.true; | ||
| expect(updateApplicationStub.calledOnce).to.be.true; | ||
|
|
||
| const updateData = updateApplicationStub.firstCall.args[0]; | ||
| expect(updateData.nudgeCount).to.equal(1); | ||
| expect(updateData.lastNudgeAt).to.be.a("string"); | ||
| expect(new Date(updateData.lastNudgeAt).getTime()).to.be.closeTo(Date.now(), 1000); | ||
|
|
||
| expect(jsonSpy.calledOnce).to.be.true; | ||
| expect(jsonSpy.firstCall.args[0].message).to.equal(API_RESPONSE_MESSAGES.NUDGE_SUCCESS); | ||
| expect(jsonSpy.firstCall.args[0].nudgeCount).to.equal(1); | ||
| expect(jsonSpy.firstCall.args[0].lastNudgeAt).to.be.a("string"); | ||
| }); | ||
|
|
||
| it("should successfully nudge an application when 24 hours have passed since last nudge", async () => { | ||
| const twentyFourHoursAgo = new Date(Date.now() - convertDaysToMilliseconds(1) - 1000).toISOString(); | ||
| const mockApplication = { | ||
| id: mockApplicationId, | ||
| userId: mockUserId, | ||
| notFound: false, | ||
| lastNudgeAt: twentyFourHoursAgo, | ||
| nudgeCount: 2, | ||
| }; | ||
|
|
||
| const getApplicationByIdStub = sinon.stub(ApplicationModel, "getApplicationById").resolves(mockApplication); | ||
| const updateApplicationStub = sinon.stub(ApplicationModel, "updateApplication").resolves(); | ||
|
|
||
| await applicationsController.nudgeApplication(req as CustomRequest, res as CustomResponse); | ||
|
|
||
| expect(getApplicationByIdStub.calledOnce).to.be.true; | ||
| expect(getApplicationByIdStub.calledWith(mockApplicationId)).to.be.true; | ||
| expect(updateApplicationStub.calledOnce).to.be.true; | ||
|
|
||
| const updateData = updateApplicationStub.firstCall.args[0]; | ||
| expect(updateData.nudgeCount).to.equal(3); | ||
| expect(updateData.lastNudgeAt).to.be.a("string"); | ||
|
|
||
| expect(jsonSpy.calledOnce).to.be.true; | ||
| expect(jsonSpy.firstCall.args[0].message).to.equal(API_RESPONSE_MESSAGES.NUDGE_SUCCESS); | ||
| expect(jsonSpy.firstCall.args[0].nudgeCount).to.equal(3); | ||
| }); | ||
|
|
||
| it("should increment nudgeCount correctly when nudgeCount is undefined", async () => { | ||
| const mockApplication = { | ||
| id: mockApplicationId, | ||
| userId: mockUserId, | ||
| notFound: false, | ||
| lastNudgeAt: null, | ||
| nudgeCount: undefined, | ||
| }; | ||
|
|
||
| sinon.stub(ApplicationModel, "getApplicationById").resolves(mockApplication); | ||
| const updateApplicationStub = sinon.stub(ApplicationModel, "updateApplication").resolves(); | ||
|
|
||
| await applicationsController.nudgeApplication(req as CustomRequest, res as CustomResponse); | ||
|
|
||
| const updateData = updateApplicationStub.firstCall.args[0]; | ||
| expect(updateData.nudgeCount).to.equal(1); | ||
| }); | ||
| }); | ||
|
|
||
| describe("Error cases", () => { | ||
| it("should return 404 when application is not found", async () => { | ||
| const mockApplication = { | ||
| notFound: true, | ||
| }; | ||
|
|
||
| sinon.stub(ApplicationModel, "getApplicationById").resolves(mockApplication); | ||
|
|
||
| await applicationsController.nudgeApplication(req as CustomRequest, res as CustomResponse); | ||
|
|
||
| expect(boomNotFound.calledOnce).to.be.true; | ||
| expect(boomNotFound.firstCall.args[0]).to.equal("Application not found"); | ||
| expect(jsonSpy.notCalled).to.be.true; | ||
| }); | ||
|
|
||
| it("should return 401 when user is not authorized (not the owner)", async () => { | ||
| const mockApplication = { | ||
| id: mockApplicationId, | ||
| userId: mockOtherUserId, | ||
| notFound: false, | ||
| lastNudgeAt: null, | ||
| nudgeCount: 0, | ||
| }; | ||
|
|
||
| sinon.stub(ApplicationModel, "getApplicationById").resolves(mockApplication); | ||
|
|
||
| await applicationsController.nudgeApplication(req as CustomRequest, res as CustomResponse); | ||
|
|
||
| expect(boomUnauthorized.calledOnce).to.be.true; | ||
| expect(boomUnauthorized.firstCall.args[0]).to.equal("You are not authorized to nudge this application"); | ||
| expect(jsonSpy.notCalled).to.be.true; | ||
| }); | ||
|
|
||
| it("should return 429 when trying to nudge within 24 hours", async () => { | ||
| const oneHourAgo = new Date(Date.now() - convertDaysToMilliseconds(1) / 24).toISOString(); | ||
| const mockApplication = { | ||
| id: mockApplicationId, | ||
| userId: mockUserId, | ||
| notFound: false, | ||
| lastNudgeAt: oneHourAgo, | ||
| nudgeCount: 1, | ||
| }; | ||
|
|
||
| sinon.stub(ApplicationModel, "getApplicationById").resolves(mockApplication); | ||
|
|
||
| await applicationsController.nudgeApplication(req as CustomRequest, res as CustomResponse); | ||
|
|
||
| expect(boomTooManyRequests.calledOnce).to.be.true; | ||
| expect(boomTooManyRequests.firstCall.args[0]).to.equal(APPLICATION_ERROR_MESSAGES.NUDGE_TOO_SOON); | ||
| expect(jsonSpy.notCalled).to.be.true; | ||
| }); | ||
|
|
||
| it("should return 429 when trying to nudge exactly at 24 hours", async () => { | ||
| const exactlyTwentyFourHoursAgo = new Date(Date.now() - convertDaysToMilliseconds(1)).toISOString(); | ||
| const mockApplication = { | ||
| id: mockApplicationId, | ||
| userId: mockUserId, | ||
| notFound: false, | ||
| lastNudgeAt: exactlyTwentyFourHoursAgo, | ||
| nudgeCount: 1, | ||
| }; | ||
|
|
||
| sinon.stub(ApplicationModel, "getApplicationById").resolves(mockApplication); | ||
|
|
||
| await applicationsController.nudgeApplication(req as CustomRequest, res as CustomResponse); | ||
|
|
||
| expect(boomTooManyRequests.calledOnce).to.be.true; | ||
| expect(boomTooManyRequests.firstCall.args[0]).to.equal(APPLICATION_ERROR_MESSAGES.NUDGE_TOO_SOON); | ||
| expect(jsonSpy.notCalled).to.be.true; | ||
| }); | ||
| }); | ||
| }); |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.