Skip to content

Commit eb4042f

Browse files
committed
test(api): enhance createCandidacyContestationCaducite tests with validation and security checks
- Implemented security tests to verify that candidates can only create contestations for their own candidacies. - Refactored test cases to use constants for contestation reasons and dates for improved readability and maintainability.
1 parent fa82755 commit eb4042f

File tree

1 file changed

+96
-34
lines changed

1 file changed

+96
-34
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,50 @@
1+
/**
2+
* @jest-environment ./test/fastify-test-env.ts
3+
*/
4+
15
import { faker } from "@faker-js/faker/.";
26
import { CertificationAuthorityContestationDecision } from "@prisma/client";
7+
import { addDays, subDays } from "date-fns";
38
import { prismaClient } from "../../../../prisma/client";
9+
import { authorizationHeaderForUser } from "../../../../test/helpers/authorization-helper";
410
import { createCandidacyHelper } from "../../../../test/helpers/entities/create-candidacy-helper";
11+
import { injectGraphql } from "../../../../test/helpers/graphql-helper";
512
import { clearDatabase } from "../../../../test/jestClearDatabaseBeforeEachTestFile";
613
import { createCandidacyContestationCaducite } from "./createCandidacyContestationCaducite";
714

15+
const VALID_CONTESTATION_REASON = "Valid contestation reason";
16+
const FUTURE_DATE = addDays(new Date(), 30);
17+
const PAST_DATE = subDays(new Date(), 1);
18+
19+
const createContestationMutation = async ({
20+
keycloakId,
21+
candidacyId,
22+
contestationReason,
23+
readyForJuryEstimatedAt,
24+
}: {
25+
keycloakId: string;
26+
candidacyId: string;
27+
contestationReason: string;
28+
readyForJuryEstimatedAt: Date;
29+
}) =>
30+
await injectGraphql({
31+
fastify: (global as any).fastify,
32+
authorization: authorizationHeaderForUser({
33+
role: "candidate",
34+
keycloakId,
35+
}),
36+
payload: {
37+
requestType: "mutation",
38+
endpoint: "candidacy_contestation_caducite_create_contestation",
39+
arguments: {
40+
candidacyId,
41+
contestationReason,
42+
readyForJuryEstimatedAt,
43+
},
44+
returnFields: "{id,contestationReason}",
45+
},
46+
});
47+
848
describe("createCandidacyContestationCaducite", () => {
949
beforeEach(async () => {
1050
await clearDatabase();
@@ -13,13 +53,11 @@ describe("createCandidacyContestationCaducite", () => {
1353
describe("Input validation", () => {
1454
test("should fail when contestationReason is empty", async () => {
1555
const candidacy = await createCandidacyHelper();
16-
const futureDate = new Date();
17-
futureDate.setDate(futureDate.getDate() + 30);
1856

1957
const createContestationPromise = createCandidacyContestationCaducite({
2058
candidacyId: candidacy.id,
2159
contestationReason: "",
22-
readyForJuryEstimatedAt: futureDate,
60+
readyForJuryEstimatedAt: FUTURE_DATE,
2361
});
2462

2563
await expect(createContestationPromise).rejects.toThrow(
@@ -29,13 +67,11 @@ describe("createCandidacyContestationCaducite", () => {
2967

3068
test("should fail when readyForJuryEstimatedAt is in the past", async () => {
3169
const candidacy = await createCandidacyHelper();
32-
const pastDate = new Date();
33-
pastDate.setDate(pastDate.getDate() - 1);
3470

3571
const createContestationPromise = createCandidacyContestationCaducite({
3672
candidacyId: candidacy.id,
37-
contestationReason: "Valid reason",
38-
readyForJuryEstimatedAt: pastDate,
73+
contestationReason: VALID_CONTESTATION_REASON,
74+
readyForJuryEstimatedAt: PAST_DATE,
3975
});
4076

4177
await expect(createContestationPromise).rejects.toThrow(
@@ -46,13 +82,10 @@ describe("createCandidacyContestationCaducite", () => {
4682

4783
describe("Candidacy validation", () => {
4884
test("should fail when candidacy does not exist", async () => {
49-
const futureDate = new Date();
50-
futureDate.setDate(futureDate.getDate() + 30);
51-
5285
const createContestationPromise = createCandidacyContestationCaducite({
5386
candidacyId: faker.string.uuid(),
54-
contestationReason: "Valid reason",
55-
readyForJuryEstimatedAt: futureDate,
87+
contestationReason: VALID_CONTESTATION_REASON,
88+
readyForJuryEstimatedAt: FUTURE_DATE,
5689
});
5790

5891
await expect(createContestationPromise).rejects.toThrow(
@@ -62,22 +95,20 @@ describe("createCandidacyContestationCaducite", () => {
6295

6396
test("should fail when a contestation is pending", async () => {
6497
const candidacy = await createCandidacyHelper();
65-
const futureDate = new Date();
66-
futureDate.setDate(futureDate.getDate() + 30);
6798

6899
await prismaClient.candidacyContestationCaducite.create({
69100
data: {
70101
candidacyId: candidacy.id,
71-
contestationReason: "Initial contestation",
102+
contestationReason: VALID_CONTESTATION_REASON,
72103
certificationAuthorityContestationDecision:
73104
CertificationAuthorityContestationDecision.DECISION_PENDING,
74105
},
75106
});
76107

77108
const createContestationPromise = createCandidacyContestationCaducite({
78109
candidacyId: candidacy.id,
79-
contestationReason: "Another contestation",
80-
readyForJuryEstimatedAt: futureDate,
110+
contestationReason: VALID_CONTESTATION_REASON,
111+
readyForJuryEstimatedAt: FUTURE_DATE,
81112
});
82113

83114
await expect(createContestationPromise).rejects.toThrow(
@@ -87,22 +118,20 @@ describe("createCandidacyContestationCaducite", () => {
87118

88119
test("should fail when caducity has been confirmed", async () => {
89120
const candidacy = await createCandidacyHelper();
90-
const futureDate = new Date();
91-
futureDate.setDate(futureDate.getDate() + 30);
92121

93122
await prismaClient.candidacyContestationCaducite.create({
94123
data: {
95124
candidacyId: candidacy.id,
96-
contestationReason: "Initial contestation",
125+
contestationReason: VALID_CONTESTATION_REASON,
97126
certificationAuthorityContestationDecision:
98127
CertificationAuthorityContestationDecision.CADUCITE_CONFIRMED,
99128
},
100129
});
101130

102131
const createContestationPromise = createCandidacyContestationCaducite({
103132
candidacyId: candidacy.id,
104-
contestationReason: "Another contestation",
105-
readyForJuryEstimatedAt: futureDate,
133+
contestationReason: VALID_CONTESTATION_REASON,
134+
readyForJuryEstimatedAt: FUTURE_DATE,
106135
});
107136

108137
await expect(createContestationPromise).rejects.toThrow(
@@ -114,52 +143,85 @@ describe("createCandidacyContestationCaducite", () => {
114143
describe("Successful creation", () => {
115144
test("should successfully create a contestation and update readyForJuryEstimatedAt", async () => {
116145
const candidacy = await createCandidacyHelper();
117-
const futureDate = new Date();
118-
futureDate.setDate(futureDate.getDate() + 30);
119146

120147
const result = await createCandidacyContestationCaducite({
121148
candidacyId: candidacy.id,
122-
contestationReason: "Valid contestation reason",
123-
readyForJuryEstimatedAt: futureDate,
149+
contestationReason: VALID_CONTESTATION_REASON,
150+
readyForJuryEstimatedAt: FUTURE_DATE,
124151
});
125152

126153
expect(result).toMatchObject({
127154
candidacyId: candidacy.id,
128-
contestationReason: "Valid contestation reason",
155+
contestationReason: VALID_CONTESTATION_REASON,
129156
});
130157

131158
const updatedCandidacy = await prismaClient.candidacy.findUnique({
132159
where: { id: candidacy.id },
133160
});
134161
expect(updatedCandidacy?.readyForJuryEstimatedAt?.getTime()).toBe(
135-
futureDate.getTime(),
162+
FUTURE_DATE.getTime(),
136163
);
137164
});
138165

139166
test("should allow new contestation when previous one was invalidated", async () => {
140167
const candidacy = await createCandidacyHelper();
141-
const futureDate = new Date();
142-
futureDate.setDate(futureDate.getDate() + 30);
143168

144169
await prismaClient.candidacyContestationCaducite.create({
145170
data: {
146171
candidacyId: candidacy.id,
147-
contestationReason: "Initial contestation",
172+
contestationReason: VALID_CONTESTATION_REASON,
148173
certificationAuthorityContestationDecision:
149174
CertificationAuthorityContestationDecision.CADUCITE_INVALIDATED,
150175
},
151176
});
152177

153178
const result = await createCandidacyContestationCaducite({
154179
candidacyId: candidacy.id,
155-
contestationReason: "New valid contestation",
156-
readyForJuryEstimatedAt: futureDate,
180+
contestationReason: VALID_CONTESTATION_REASON,
181+
readyForJuryEstimatedAt: FUTURE_DATE,
157182
});
158183

159184
expect(result).toMatchObject({
160185
candidacyId: candidacy.id,
161-
contestationReason: "New valid contestation",
186+
contestationReason: VALID_CONTESTATION_REASON,
187+
});
188+
});
189+
});
190+
191+
describe("Security", () => {
192+
test("should allow candidate to create contestation for their own candidacy", async () => {
193+
const candidacy = await createCandidacyHelper();
194+
195+
const resp = await createContestationMutation({
196+
keycloakId: candidacy.candidate?.keycloakId ?? "",
197+
candidacyId: candidacy.id,
198+
contestationReason: VALID_CONTESTATION_REASON,
199+
readyForJuryEstimatedAt: FUTURE_DATE,
200+
});
201+
202+
expect(resp.statusCode).toEqual(200);
203+
expect(
204+
resp.json().data.candidacy_contestation_caducite_create_contestation,
205+
).toMatchObject({
206+
contestationReason: VALID_CONTESTATION_REASON,
207+
});
208+
});
209+
210+
test("should not allow candidate to create contestation for another candidacy", async () => {
211+
const candidacy = await createCandidacyHelper();
212+
const otherCandidacy = await createCandidacyHelper();
213+
214+
const resp = await createContestationMutation({
215+
keycloakId: candidacy.candidate?.keycloakId ?? "",
216+
candidacyId: otherCandidacy.id,
217+
contestationReason: VALID_CONTESTATION_REASON,
218+
readyForJuryEstimatedAt: FUTURE_DATE,
162219
});
220+
221+
expect(resp.statusCode).toEqual(200);
222+
expect(resp.json().errors?.[0].message).toEqual(
223+
"Vous n'êtes pas autorisé à accéder à cette candidature",
224+
);
163225
});
164226
});
165227
});

0 commit comments

Comments
 (0)