Skip to content

Commit 96d8594

Browse files
committed
test: add unit tests for createApplicationService to validate application creation logic
- Implemented tests for various scenarios including successful application creation, conflict errors, and boundary cases based on application creation dates. - Verified correct transformation of payload fields and handling of optional fields. - Ensured error handling and logging for different error scenarios.
1 parent b77fc04 commit 96d8594

File tree

1 file changed

+314
-0
lines changed

1 file changed

+314
-0
lines changed
Lines changed: 314 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,314 @@
1+
import { expect } from "chai";
2+
import sinon from "sinon";
3+
import { Conflict } from "http-errors";
4+
import * as applicationService from "../../../services/applicationService";
5+
import { applicationPayload } from "../../../types/application";
6+
const ApplicationModel = require("../../../models/applications");
7+
const logger = require("../../../utils/logger");
8+
const {
9+
APPLICATION_STATUS_TYPES,
10+
APPLICATION_ERROR_MESSAGES,
11+
APPLICATION_REVIEW_CYCLE_START_DATE,
12+
} = require("../../../constants/application");
13+
const applicationsData = require("../../fixtures/applications/applications")();
14+
15+
describe("createApplicationService", () => {
16+
const mockUserId = "test-user-id-123";
17+
const mockApplicationId = "mock-application-id-456";
18+
19+
const mockPayload: applicationPayload = {
20+
...applicationsData[6],
21+
imageUrl: "https://example.com/image.jpg",
22+
};
23+
24+
afterEach(() => {
25+
sinon.restore();
26+
});
27+
28+
describe("Date-based application creation logic", () => {
29+
it("should create application successfully when existing application was created before Jan 1, 2026", async () => {
30+
const existingApplication = {
31+
id: "existing-app-id",
32+
userId: mockUserId,
33+
createdAt: "2025-12-31T23:59:59.999Z",
34+
status: "pending",
35+
};
36+
37+
const getUserApplicationsStub = sinon
38+
.stub(ApplicationModel, "getUserApplications")
39+
.resolves([existingApplication]);
40+
41+
const addApplicationStub = sinon.stub(ApplicationModel, "addApplication").resolves(mockApplicationId);
42+
43+
const result = await applicationService.createApplicationService({
44+
userId: mockUserId,
45+
payload: mockPayload,
46+
});
47+
48+
expect(result).to.have.property("applicationId", mockApplicationId);
49+
expect(result).to.have.property("isNew", true);
50+
expect(getUserApplicationsStub.calledOnce).to.be.true;
51+
expect(addApplicationStub.calledOnce).to.be.true;
52+
53+
const applicationData = addApplicationStub.getCall(0).args[0];
54+
expect(applicationData).to.have.property("isNew", true);
55+
});
56+
57+
it("should throw Conflict error when existing application was created after Jan 1, 2026", async () => {
58+
const existingApplication = {
59+
id: "existing-app-id",
60+
userId: mockUserId,
61+
createdAt: "2026-01-01T00:00:00.001Z",
62+
status: "pending",
63+
};
64+
65+
const getUserApplicationsStub = sinon
66+
.stub(ApplicationModel, "getUserApplications")
67+
.resolves([existingApplication]);
68+
69+
const addApplicationStub = sinon.stub(ApplicationModel, "addApplication").resolves(mockApplicationId);
70+
71+
try {
72+
await applicationService.createApplicationService({
73+
userId: mockUserId,
74+
payload: mockPayload,
75+
});
76+
expect.fail("Should have thrown Conflict error");
77+
} catch (err) {
78+
expect(err).to.be.instanceOf(Conflict);
79+
expect(err.message).to.equal(APPLICATION_ERROR_MESSAGES.APPLICATION_ALREADY_REVIEWED);
80+
expect(getUserApplicationsStub.calledOnce).to.be.true;
81+
expect(addApplicationStub.called).to.be.false;
82+
}
83+
});
84+
85+
it("should create application successfully when existing application was created exactly on Jan 1, 2026 (boundary case)", async () => {
86+
const existingApplication = {
87+
id: "existing-app-id",
88+
userId: mockUserId,
89+
createdAt: APPLICATION_REVIEW_CYCLE_START_DATE.toISOString(),
90+
status: "pending",
91+
};
92+
93+
const getUserApplicationsStub = sinon
94+
.stub(ApplicationModel, "getUserApplications")
95+
.resolves([existingApplication]);
96+
97+
const addApplicationStub = sinon.stub(ApplicationModel, "addApplication").resolves(mockApplicationId);
98+
99+
const result = await applicationService.createApplicationService({
100+
userId: mockUserId,
101+
payload: mockPayload,
102+
});
103+
104+
expect(result).to.have.property("applicationId", mockApplicationId);
105+
expect(result).to.have.property("isNew", true);
106+
expect(getUserApplicationsStub.calledOnce).to.be.true;
107+
expect(addApplicationStub.calledOnce).to.be.true;
108+
});
109+
110+
it("should create application successfully when no existing application exists", async () => {
111+
const getUserApplicationsStub = sinon.stub(ApplicationModel, "getUserApplications").resolves([]);
112+
113+
const addApplicationStub = sinon.stub(ApplicationModel, "addApplication").resolves(mockApplicationId);
114+
115+
const result = await applicationService.createApplicationService({
116+
userId: mockUserId,
117+
payload: mockPayload,
118+
});
119+
120+
expect(result).to.have.property("applicationId", mockApplicationId);
121+
expect(result).to.have.property("isNew", true);
122+
expect(getUserApplicationsStub.calledOnce).to.be.true;
123+
expect(addApplicationStub.calledOnce).to.be.true;
124+
125+
const applicationData = addApplicationStub.getCall(0).args[0];
126+
expect(applicationData).to.have.property("isNew", true);
127+
});
128+
});
129+
130+
describe("isNew field verification", () => {
131+
it("should set isNew field to true in the application data saved to database", async () => {
132+
const getUserApplicationsStub = sinon.stub(ApplicationModel, "getUserApplications").resolves([]);
133+
134+
const addApplicationStub = sinon.stub(ApplicationModel, "addApplication").resolves(mockApplicationId);
135+
136+
await applicationService.createApplicationService({
137+
userId: mockUserId,
138+
payload: mockPayload,
139+
});
140+
141+
const applicationData = addApplicationStub.getCall(0).args[0];
142+
expect(applicationData.isNew).to.equal(true);
143+
expect(applicationData.score).to.equal(0);
144+
expect(applicationData.status).to.equal(APPLICATION_STATUS_TYPES.PENDING);
145+
expect(applicationData.nudgeCount).to.equal(0);
146+
});
147+
148+
it("should set isNew field to true even when existing application exists before Jan 1, 2026", async () => {
149+
const existingApplication = {
150+
id: "existing-app-id",
151+
userId: mockUserId,
152+
createdAt: "2025-06-15T10:30:00.000Z",
153+
status: "rejected",
154+
};
155+
156+
sinon.stub(ApplicationModel, "getUserApplications").resolves([existingApplication]);
157+
158+
const addApplicationStub = sinon.stub(ApplicationModel, "addApplication").resolves(mockApplicationId);
159+
160+
await applicationService.createApplicationService({
161+
userId: mockUserId,
162+
payload: mockPayload,
163+
});
164+
165+
const applicationData = addApplicationStub.getCall(0).args[0];
166+
expect(applicationData.isNew).to.equal(true);
167+
});
168+
});
169+
170+
describe("Field transformation and mapping", () => {
171+
it("should correctly transform payload fields to application structure", async () => {
172+
sinon.stub(ApplicationModel, "getUserApplications").resolves([]);
173+
174+
const addApplicationStub = sinon.stub(ApplicationModel, "addApplication").resolves(mockApplicationId);
175+
176+
await applicationService.createApplicationService({
177+
userId: mockUserId,
178+
payload: mockPayload,
179+
});
180+
181+
const applicationData = addApplicationStub.getCall(0).args[0];
182+
183+
expect(applicationData.userId).to.equal(mockUserId);
184+
expect(applicationData.biodata.firstName).to.equal(mockPayload.firstName);
185+
expect(applicationData.biodata.lastName).to.equal(mockPayload.lastName);
186+
expect(applicationData.location.city).to.equal(mockPayload.city);
187+
expect(applicationData.location.state).to.equal(mockPayload.state);
188+
expect(applicationData.location.country).to.equal(mockPayload.country);
189+
expect(applicationData.professional.institution).to.equal(mockPayload.college);
190+
expect(applicationData.professional.skills).to.equal(mockPayload.skills);
191+
expect(applicationData.intro.introduction).to.equal(mockPayload.introduction);
192+
expect(applicationData.intro.funFact).to.equal(mockPayload.funFact);
193+
expect(applicationData.intro.forFun).to.equal(mockPayload.forFun);
194+
expect(applicationData.intro.whyRds).to.equal(mockPayload.whyRds);
195+
expect(applicationData.intro.numberOfHours).to.equal(mockPayload.numberOfHours);
196+
expect(applicationData.foundFrom).to.equal(mockPayload.foundFrom);
197+
expect(applicationData.role).to.equal(mockPayload.role);
198+
expect(applicationData.imageUrl).to.equal(mockPayload.imageUrl);
199+
if (mockPayload.socialLink) {
200+
expect(applicationData.socialLink).to.deep.equal(mockPayload.socialLink);
201+
}
202+
});
203+
204+
it("should handle optional fields (imageUrl and socialLink) when not provided", async () => {
205+
const payloadWithoutOptionalFields: applicationPayload = {
206+
...mockPayload,
207+
imageUrl: undefined,
208+
socialLink: undefined,
209+
};
210+
211+
sinon.stub(ApplicationModel, "getUserApplications").resolves([]);
212+
213+
const addApplicationStub = sinon.stub(ApplicationModel, "addApplication").resolves(mockApplicationId);
214+
215+
await applicationService.createApplicationService({
216+
userId: mockUserId,
217+
payload: payloadWithoutOptionalFields,
218+
});
219+
220+
const applicationData = addApplicationStub.getCall(0).args[0];
221+
222+
expect(applicationData).to.not.have.property("imageUrl");
223+
expect(applicationData).to.not.have.property("socialLink");
224+
expect(applicationData.biodata.firstName).to.equal(mockPayload.firstName);
225+
expect(applicationData.foundFrom).to.equal(mockPayload.foundFrom);
226+
});
227+
228+
it("should include createdAt timestamp in the application data", async () => {
229+
sinon.stub(ApplicationModel, "getUserApplications").resolves([]);
230+
231+
const addApplicationStub = sinon.stub(ApplicationModel, "addApplication").resolves(mockApplicationId);
232+
233+
const beforeCreation = new Date().toISOString();
234+
235+
await applicationService.createApplicationService({
236+
userId: mockUserId,
237+
payload: mockPayload,
238+
});
239+
240+
const afterCreation = new Date().toISOString();
241+
242+
const applicationData = addApplicationStub.getCall(0).args[0];
243+
expect(applicationData.createdAt).to.exist;
244+
expect(applicationData.createdAt).to.be.a("string");
245+
expect(applicationData.createdAt >= beforeCreation).to.be.true;
246+
expect(applicationData.createdAt <= afterCreation).to.be.true;
247+
});
248+
});
249+
250+
describe("Error handling", () => {
251+
it("should propagate Conflict errors without modification", async () => {
252+
const existingApplication = {
253+
id: "existing-app-id",
254+
userId: mockUserId,
255+
createdAt: "2026-06-15T10:30:00.000Z",
256+
status: "pending",
257+
};
258+
259+
sinon.stub(ApplicationModel, "getUserApplications").resolves([existingApplication]);
260+
261+
try {
262+
await applicationService.createApplicationService({
263+
userId: mockUserId,
264+
payload: mockPayload,
265+
});
266+
expect.fail("Should have thrown Conflict error");
267+
} catch (err) {
268+
expect(err).to.be.instanceOf(Conflict);
269+
expect(err.message).to.equal(APPLICATION_ERROR_MESSAGES.APPLICATION_ALREADY_REVIEWED);
270+
}
271+
});
272+
273+
it("should log and re-throw non-Conflict errors", async () => {
274+
const testError = new Error("Database connection failed");
275+
const loggerErrorStub = sinon.stub(logger, "error");
276+
277+
sinon.stub(ApplicationModel, "getUserApplications").rejects(testError);
278+
279+
try {
280+
await applicationService.createApplicationService({
281+
userId: mockUserId,
282+
payload: mockPayload,
283+
});
284+
expect.fail("Should have thrown error");
285+
} catch (err) {
286+
expect(err).to.equal(testError);
287+
expect(loggerErrorStub.calledOnce).to.be.true;
288+
expect(loggerErrorStub.getCall(0).args[0]).to.equal("Error in createApplicationService");
289+
expect(loggerErrorStub.getCall(0).args[1]).to.equal(testError);
290+
}
291+
});
292+
293+
it("should handle errors from addApplication and log them", async () => {
294+
const testError = new Error("Failed to save application");
295+
const loggerErrorStub = sinon.stub(logger, "error");
296+
297+
sinon.stub(ApplicationModel, "getUserApplications").resolves([]);
298+
sinon.stub(ApplicationModel, "addApplication").rejects(testError);
299+
300+
try {
301+
await applicationService.createApplicationService({
302+
userId: mockUserId,
303+
payload: mockPayload,
304+
});
305+
expect.fail("Should have thrown error");
306+
} catch (err) {
307+
expect(err).to.equal(testError);
308+
expect(loggerErrorStub.calledOnce).to.be.true;
309+
expect(loggerErrorStub.getCall(0).args[0]).to.equal("Error in createApplicationService");
310+
expect(loggerErrorStub.getCall(0).args[1]).to.equal(testError);
311+
}
312+
});
313+
});
314+
});

0 commit comments

Comments
 (0)