Skip to content

Commit e7270c0

Browse files
authored
test: add comprehensive tests for nudge application functionality (#2543)
* test: add comprehensive tests for nudge application functionality * chore: add logger utility to discordService and logService for improved logging * test: enhance nudge application tests to cover pending status validation * refactor: remove duplicate logger import and unused config in discordService * nit: remove unused logger import * refactor: update nudge application logic and messages - Changed the success message for nudging an application to "Nudge sent successfully". - Updated error messages for nudging to be more user-friendly. - Refactored the nudgeApplication function to streamline logic and improve readability. - Adjusted integration and unit tests to reflect the updated messages and logic. * refactor: nudge model try and catch block
1 parent 508ab61 commit e7270c0

File tree

4 files changed

+389
-45
lines changed

4 files changed

+389
-45
lines changed

models/applications.ts

Lines changed: 36 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -138,59 +138,54 @@ const updateApplication = async (dataToUpdate: object, applicationId: string) =>
138138
};
139139

140140
const nudgeApplication = async ({ applicationId, userId }: { applicationId: string; userId: string }) => {
141-
try {
142-
const currentTime = Date.now();
143-
const twentyFourHoursInMilliseconds = convertDaysToMilliseconds(1);
141+
const currentTime = Date.now();
142+
const twentyFourHoursInMilliseconds = convertDaysToMilliseconds(1);
144143

145-
const result = await firestore.runTransaction(async (transaction) => {
146-
const applicationRef = ApplicationsModel.doc(applicationId);
147-
const applicationDoc = await transaction.get(applicationRef);
144+
const result = await firestore.runTransaction(async (transaction) => {
145+
const applicationRef = ApplicationsModel.doc(applicationId);
146+
const applicationDoc = await transaction.get(applicationRef);
148147

149-
if (!applicationDoc.exists) {
150-
return { status: NUDGE_APPLICATION_STATUS.notFound };
151-
}
148+
if (!applicationDoc.exists) {
149+
return { status: NUDGE_APPLICATION_STATUS.notFound };
150+
}
152151

153-
const application = applicationDoc.data();
152+
const application = applicationDoc.data();
154153

155-
if (application.userId !== userId) {
156-
return { status: NUDGE_APPLICATION_STATUS.unauthorized };
157-
}
154+
if (application.userId !== userId) {
155+
return { status: NUDGE_APPLICATION_STATUS.unauthorized };
156+
}
158157

159-
if (application.status !== APPLICATION_STATUS_TYPES.PENDING) {
160-
return { status: NUDGE_APPLICATION_STATUS.notPending };
161-
}
158+
if (application.status !== APPLICATION_STATUS_TYPES.PENDING) {
159+
return { status: NUDGE_APPLICATION_STATUS.notPending };
160+
}
162161

163-
const lastNudgeAt = application.lastNudgeAt;
164-
if (lastNudgeAt) {
165-
const lastNudgeTimestamp = new Date(lastNudgeAt).getTime();
166-
const timeDifference = currentTime - lastNudgeTimestamp;
162+
const lastNudgeAt = application.lastNudgeAt;
163+
if (lastNudgeAt) {
164+
const lastNudgeTimestamp = new Date(lastNudgeAt).getTime();
165+
const timeDifference = currentTime - lastNudgeTimestamp;
167166

168-
if (timeDifference <= twentyFourHoursInMilliseconds) {
169-
return { status: NUDGE_APPLICATION_STATUS.tooSoon };
170-
}
167+
if (timeDifference <= twentyFourHoursInMilliseconds) {
168+
return { status: NUDGE_APPLICATION_STATUS.tooSoon };
171169
}
170+
}
172171

173-
const currentNudgeCount = application.nudgeCount || 0;
174-
const updatedNudgeCount = currentNudgeCount + 1;
175-
const newLastNudgeAt = new Date(currentTime).toISOString();
176-
177-
transaction.update(applicationRef, {
178-
nudgeCount: updatedNudgeCount,
179-
lastNudgeAt: newLastNudgeAt,
180-
});
172+
const currentNudgeCount = application.nudgeCount || 0;
173+
const updatedNudgeCount = currentNudgeCount + 1;
174+
const newLastNudgeAt = new Date(currentTime).toISOString();
181175

182-
return {
183-
status: NUDGE_APPLICATION_STATUS.success,
184-
nudgeCount: updatedNudgeCount,
185-
lastNudgeAt: newLastNudgeAt,
186-
};
176+
transaction.update(applicationRef, {
177+
nudgeCount: updatedNudgeCount,
178+
lastNudgeAt: newLastNudgeAt,
187179
});
188180

189-
return result;
190-
} catch (err) {
191-
logger.error("Error while nudging application", err);
192-
throw err;
193-
}
181+
return {
182+
status: NUDGE_APPLICATION_STATUS.success,
183+
nudgeCount: updatedNudgeCount,
184+
lastNudgeAt: newLastNudgeAt,
185+
};
186+
});
187+
188+
return result;
194189
};
195190

196191
module.exports = {

services/logService.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@ interface LogBody {
1919
* @param meta { LogMeta }: Meta data of the log
2020
* @param body { LogBody }: Body of the log
2121
*/
22-
export const addLog = async (type: string, meta: LogMeta, body: LogBody): Promise<FirebaseFirestore.DocumentReference<FirebaseFirestore.DocumentData>> => {
22+
export const addLog = async (
23+
type: string,
24+
meta: LogMeta,
25+
body: LogBody
26+
): Promise<FirebaseFirestore.DocumentReference<FirebaseFirestore.DocumentData>> => {
2327
try {
2428
const log = {
2529
type,
@@ -32,4 +36,4 @@ export const addLog = async (type: string, meta: LogMeta, body: LogBody): Promis
3236
logger.error("Error in adding log", err);
3337
throw new Error(INTERNAL_SERVER_ERROR);
3438
}
35-
};
39+
};

test/integration/application.test.ts

Lines changed: 149 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,17 @@ import chai from "chai";
22
import chaiHttp from "chai-http";
33
const { expect } = chai;
44
import config from "config";
5+
import sinon from "sinon";
56
const app = require("../../server");
67
const addUser = require("../utils/addUser");
78
const cleanDb = require("../utils/cleanDb");
89
const authService = require("../../services/authService");
910
const userData = require("../fixtures/user/user")();
1011
const applicationModel = require("../../models/applications");
11-
const { requestRoleData } = require("../fixtures/discordactions/discordactions");
1212

1313
const applicationsData = require("../fixtures/applications/applications")();
1414
const cookieName = config.get("userToken.cookieName");
15-
const { getUserApplicationObject } = require("../../utils/application");
15+
const { APPLICATION_ERROR_MESSAGES, API_RESPONSE_MESSAGES } = require("../../constants/application");
1616

1717
const appOwner = userData[3];
1818
const superUser = userData[4];
@@ -65,6 +65,7 @@ describe("Application", function () {
6565

6666
after(async function () {
6767
await cleanDb();
68+
sinon.restore();
6869
});
6970

7071
describe("GET /applications", function () {
@@ -488,4 +489,150 @@ describe("Application", function () {
488489
});
489490
});
490491
});
492+
493+
describe("PATCH /applications/:applicationId/nudge", function () {
494+
let nudgeApplicationId: string;
495+
496+
beforeEach(async function () {
497+
const applicationData = { ...applicationsData[0], userId };
498+
nudgeApplicationId = await applicationModel.addApplication(applicationData);
499+
});
500+
501+
afterEach(async function () {
502+
sinon.restore();
503+
});
504+
505+
it("should successfully nudge a pending application when user owns it and no previous nudge exists", function (done) {
506+
chai
507+
.request(app)
508+
.patch(`/applications/${nudgeApplicationId}/nudge`)
509+
.set("cookie", `${cookieName}=${jwt}`)
510+
.end(function (err, res) {
511+
if (err) return done(err);
512+
513+
expect(res).to.have.status(200);
514+
expect(res.body.message).to.be.equal(API_RESPONSE_MESSAGES.NUDGE_SUCCESS);
515+
expect(res.body.nudgeCount).to.be.equal(1);
516+
expect(res.body.lastNudgeAt).to.be.a("string");
517+
done();
518+
});
519+
});
520+
521+
it("should successfully nudge an application when 24 hours have passed since last nudge", function (done) {
522+
chai
523+
.request(app)
524+
.patch(`/applications/${nudgeApplicationId}/nudge`)
525+
.set("cookie", `${cookieName}=${jwt}`)
526+
.end(function (err, res) {
527+
if (err) return done(err);
528+
529+
expect(res).to.have.status(200);
530+
expect(res.body.nudgeCount).to.be.equal(1);
531+
532+
const twentyFiveHoursAgo = new Date(Date.now() - 25 * 60 * 60 * 1000).toISOString();
533+
applicationModel.updateApplication({ lastNudgeAt: twentyFiveHoursAgo }, nudgeApplicationId).then(() => {
534+
chai
535+
.request(app)
536+
.patch(`/applications/${nudgeApplicationId}/nudge`)
537+
.set("cookie", `${cookieName}=${jwt}`)
538+
.end(function (err, res) {
539+
if (err) return done(err);
540+
541+
expect(res).to.have.status(200);
542+
expect(res.body.message).to.be.equal(API_RESPONSE_MESSAGES.NUDGE_SUCCESS);
543+
expect(res.body.nudgeCount).to.be.equal(2);
544+
expect(res.body.lastNudgeAt).to.be.a("string");
545+
done();
546+
});
547+
});
548+
});
549+
});
550+
551+
it("should return 404 if the application doesn't exist", function (done) {
552+
chai
553+
.request(app)
554+
.patch(`/applications/non-existent-id/nudge`)
555+
.set("cookie", `${cookieName}=${jwt}`)
556+
.end(function (err, res) {
557+
if (err) return done(err);
558+
559+
expect(res).to.have.status(404);
560+
expect(res.body.error).to.be.equal("Not Found");
561+
expect(res.body.message).to.be.equal("Application not found");
562+
done();
563+
});
564+
});
565+
566+
it("should return 401 if user is not authenticated", function (done) {
567+
chai
568+
.request(app)
569+
.patch(`/applications/${nudgeApplicationId}/nudge`)
570+
.end(function (err, res) {
571+
if (err) return done(err);
572+
573+
expect(res).to.have.status(401);
574+
expect(res.body.error).to.be.equal("Unauthorized");
575+
expect(res.body.message).to.be.equal("Unauthenticated User");
576+
done();
577+
});
578+
});
579+
580+
it("should return 401 if user does not own the application", function (done) {
581+
chai
582+
.request(app)
583+
.patch(`/applications/${nudgeApplicationId}/nudge`)
584+
.set("cookie", `${cookieName}=${secondUserJwt}`)
585+
.end(function (err, res) {
586+
if (err) return done(err);
587+
588+
expect(res).to.have.status(401);
589+
expect(res.body.error).to.be.equal("Unauthorized");
590+
expect(res.body.message).to.be.equal("You are not authorized to nudge this application");
591+
done();
592+
});
593+
});
594+
595+
it("should return 429 when trying to nudge within 24 hours", function (done) {
596+
chai
597+
.request(app)
598+
.patch(`/applications/${nudgeApplicationId}/nudge`)
599+
.set("cookie", `${cookieName}=${jwt}`)
600+
.end(function (err, res) {
601+
if (err) return done(err);
602+
603+
expect(res).to.have.status(200);
604+
605+
chai
606+
.request(app)
607+
.patch(`/applications/${nudgeApplicationId}/nudge`)
608+
.set("cookie", `${cookieName}=${jwt}`)
609+
.end(function (err, res) {
610+
if (err) return done(err);
611+
612+
expect(res).to.have.status(429);
613+
expect(res.body.error).to.be.equal("Too Many Requests");
614+
expect(res.body.message).to.be.equal(APPLICATION_ERROR_MESSAGES.NUDGE_TOO_SOON);
615+
done();
616+
});
617+
});
618+
});
619+
620+
it("should return 400 when trying to nudge an application that is not in pending status", function (done) {
621+
const nonPendingApplicationData = { ...applicationsData[1], userId };
622+
applicationModel.addApplication(nonPendingApplicationData).then((nonPendingApplicationId: string) => {
623+
chai
624+
.request(app)
625+
.patch(`/applications/${nonPendingApplicationId}/nudge`)
626+
.set("cookie", `${cookieName}=${jwt}`)
627+
.end(function (err, res) {
628+
if (err) return done(err);
629+
630+
expect(res).to.have.status(400);
631+
expect(res.body.error).to.be.equal("Bad Request");
632+
expect(res.body.message).to.be.equal(APPLICATION_ERROR_MESSAGES.NUDGE_ONLY_PENDING_ALLOWED);
633+
done();
634+
});
635+
});
636+
});
637+
});
491638
});

0 commit comments

Comments
 (0)