diff --git a/packages/backend/src/controllers/eig/create.js b/packages/backend/src/controllers/eig/create.js index ae50becc5..01ed136f7 100644 --- a/packages/backend/src/controllers/eig/create.js +++ b/packages/backend/src/controllers/eig/create.js @@ -13,7 +13,7 @@ module.exports = async (req, res, next) => { log.i("IN", { body: req.body }); const { parametre } = req.body; - if (!parametre.declarationId) { + if (!parametre?.declarationId) { return res.status(400).send({ errors: "Le champs declarationId est obligatoire", name: "UnexpectedError", diff --git a/packages/backend/src/helpers/__tests__/eig.js b/packages/backend/src/helpers/__tests__/eig.js new file mode 100644 index 000000000..aa4e12075 --- /dev/null +++ b/packages/backend/src/helpers/__tests__/eig.js @@ -0,0 +1,70 @@ +const { isDeclarationligibleToEig } = require("../eig"); +const { statuts: dsStatuts } = require("../ds-statuts"); +const { expect } = require("@playwright/test"); + +describe("isDeclarationligibleToEig", () => { + const declaration = { + id: 26, + libelle: "declaration1", + dateDebut: "2024-09-01", + dateFin: "2024-09-07", + }; + + const allStatuts = Object.values(dsStatuts); + + const okStatuts = [ + dsStatuts.SEJOUR_EN_COURS, + dsStatuts.VALIDEE_8J, + dsStatuts.TERMINEE, + ]; + + const badStatus = allStatuts.filter((s) => !okStatuts.includes(s)); + + it.each(okStatuts)( + "should return true for statut %p and good date range", + (statut) => { + declaration.statut = statut; + jest.useFakeTimers().setSystemTime(new Date("2024-09-01")); + expect(isDeclarationligibleToEig(declaration)).toBe(true); + + jest.useFakeTimers().setSystemTime(new Date("2024-09-06")); + expect(isDeclarationligibleToEig(declaration)).toBe(true); + + jest.useFakeTimers().setSystemTime(new Date("2024-09-07")); + expect(isDeclarationligibleToEig(declaration)).toBe(true); + + jest.useFakeTimers().setSystemTime(new Date("2024-09-14")); + expect(isDeclarationligibleToEig(declaration)).toBe(true); + }, + ); + + it.each(badStatus)( + "should return false for statut %p and good date range", + (statut) => { + declaration.statut = statut; + jest.useFakeTimers().setSystemTime(new Date("2024-09-01")); + expect(isDeclarationligibleToEig(declaration)).toBe(false); + + jest.useFakeTimers().setSystemTime(new Date("2024-09-06")); + expect(isDeclarationligibleToEig(declaration)).toBe(false); + + jest.useFakeTimers().setSystemTime(new Date("2024-09-07")); + expect(isDeclarationligibleToEig(declaration)).toBe(false); + + jest.useFakeTimers().setSystemTime(new Date("2024-09-14")); + expect(isDeclarationligibleToEig(declaration)).toBe(false); + }, + ); + + it.each(allStatuts)( + "should return false for statut %p and bad date range", + (statut) => { + declaration.statut = statut; + jest.useFakeTimers().setSystemTime(new Date("2024-08-31")); + expect(isDeclarationligibleToEig(declaration)).toBe(false); + + jest.useFakeTimers().setSystemTime(new Date("2024-09-15")); + expect(isDeclarationligibleToEig(declaration)).toBe(false); + }, + ); +}); diff --git a/packages/backend/src/routes/__tests__/eig/delete.test.js b/packages/backend/src/routes/__tests__/eig/delete.test.js new file mode 100644 index 000000000..a61cd1f50 --- /dev/null +++ b/packages/backend/src/routes/__tests__/eig/delete.test.js @@ -0,0 +1,60 @@ +const checkJWT = require("../../../middlewares/checkJWT"); +const checkPermissionEIG = require("../../../middlewares/checkPermissionEIG"); +const canUpdateEig = require("../../../middlewares/can-update-or-delete-eig"); +const AppError = require("../../../utils/error"); +const request = require("supertest"); +const app = require("../../../app"); +const eigService = require("../../../services/eig"); + +jest.mock("../../../middlewares/checkJWT"); +jest.mock("../../../middlewares/checkPermissionEIG"); +jest.mock("../../../middlewares/can-update-or-delete-eig"); +jest.mock("../../../services/eig"); +jest.mock("../../../services/DemandeSejour"); +jest.mock("../../../helpers/eigMail"); +jest.mock("../../../utils/mail"); +jest.mock("../../../services/geo/Commune"); +jest.mock("../../../services/mail"); + +describe("DELETE /eig/:id", () => { + const user = { + id: 1, + }; + beforeEach(() => { + jest.clearAllMocks(); + checkJWT.mockImplementation((req, res, next) => { + req.decoded = { ...user }; + next(); + }); + checkPermissionEIG.mockImplementation((req, res, next) => { + next(); + }); + canUpdateEig.mockImplementation((req, res, next) => { + next(); + }); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it("should return an error 403 if you don't have permission for eig", async () => { + checkPermissionEIG.mockImplementation((req, res, next) => { + return next( + new AppError("Vous n'êtes pas autorisé à accéder à cet EIG", { + statusCode: 403, + }), + ); + }); + + const response = await request(app).delete("/eig/1"); + expect(response.statusCode).toBe(403); + expect(eigService.delete).not.toHaveBeenCalled(); + }); + + it("should depose an eig if everything is ok", async () => { + const response = await request(app).delete("/eig/1"); + expect(response.statusCode).toBe(200); + expect(eigService.delete).toHaveBeenCalled(); + }); +}); diff --git a/packages/backend/src/routes/__tests__/eig/get-admin-by-ds-id.test.js b/packages/backend/src/routes/__tests__/eig/get-admin-by-ds-id.test.js new file mode 100644 index 000000000..bea449f5f --- /dev/null +++ b/packages/backend/src/routes/__tests__/eig/get-admin-by-ds-id.test.js @@ -0,0 +1,57 @@ +const boCheckJWT = require("../../../middlewares/bo-check-JWT"); +const checkPermissionBODeclarationSejour = require("../../../middlewares/checkPermissionBODeclarationSejour"); +const getDepartements = require("../../../middlewares/getDepartements"); +const request = require("supertest"); +const app = require("../../../app"); +const eigService = require("../../../services/eig"); + +jest.mock("../../../middlewares/bo-check-JWT"); + +jest.mock("../../../middlewares/checkPermissionBODeclarationSejour"); +jest.mock("../../../middlewares/getDepartements"); +jest.mock("../../../services/eig"); + +describe("GET /eig/admin/ds/:declarationId", () => { + afterEach(() => { + jest.clearAllMocks(); + }); + const user = { + id: 1, + }; + beforeEach(() => { + jest.resetAllMocks(); + boCheckJWT.mockImplementation((req, res, next) => { + req.decoded = { + ...user, + roles: ["eig", "DemandeSejour_Lecture", "DemandeSejour_Ecriture"], + }; + next(); + }); + + getDepartements.mockImplementation((req, res, next) => { + next(); + }); + checkPermissionBODeclarationSejour.mockImplementation((req, res, next) => { + next(); + }); + }); + + it("shoud return a 403 if the admin don't have role eig", async () => { + boCheckJWT.mockImplementationOnce((req, res, next) => { + req.decoded = { + ...user, + roles: ["DemandeSejour_Lecture", "DemandeSejour_Ecriture"], + }; + next(); + }); + const response = await request(app).get("/eig/admin/ds/1"); + expect(response.statusCode).toBe(403); + expect(eigService.getByDsIdAdmin).not.toHaveBeenCalled(); + }); + + it("should call getByDsIdAdmin if everything is ok", async () => { + const response = await request(app).get("/eig/admin/ds/1"); + expect(response.statusCode).toBe(200); + expect(eigService.getByDsIdAdmin).toHaveBeenCalled(); + }); +}); diff --git a/packages/backend/src/routes/__tests__/eig/get-admin-by-id.test.js b/packages/backend/src/routes/__tests__/eig/get-admin-by-id.test.js new file mode 100644 index 000000000..b862a4f04 --- /dev/null +++ b/packages/backend/src/routes/__tests__/eig/get-admin-by-id.test.js @@ -0,0 +1,77 @@ +const boCheckJWT = require("../../../middlewares/bo-check-JWT"); +const checkPermissionBOEIG = require("../../../middlewares/checkPermissionBOEIG"); +const request = require("supertest"); +const app = require("../../../app"); +const eigService = require("../../../services/eig"); +const AppError = require("../../../utils/error"); +const { getEmails } = require("../../../helpers/eigMail"); + +jest.mock("../../../middlewares/bo-check-JWT"); + +jest.mock("../../../middlewares/checkPermissionBODeclarationSejour"); +jest.mock("../../../middlewares/getDepartements"); +jest.mock("../../../services/eig"); +jest.mock("../../../middlewares/checkPermissionBOEIG"); +jest.mock("../../../helpers/eigMail"); + +describe("GET /eig/admin/id", () => { + afterEach(() => { + jest.clearAllMocks(); + }); + const user = { + id: 1, + }; + beforeEach(() => { + jest.resetAllMocks(); + boCheckJWT.mockImplementation((req, res, next) => { + req.decoded = { + ...user, + roles: ["eig", "DemandeSejour_Lecture", "DemandeSejour_Ecriture"], + }; + next(); + }); + checkPermissionBOEIG.mockImplementation((req, res, next) => { + next(); + }); + getEmails.mockResolvedValue({}); + eigService.getById.mockResolvedValue({ + declarationId: 1, + }); + }); + + it("shoud return a 403 if the admin don't have role eig", async () => { + boCheckJWT.mockImplementationOnce((req, res, next) => { + req.decoded = { + ...user, + roles: ["DemandeSejour_Lecture", "DemandeSejour_Ecriture"], + }; + next(); + }); + const response = await request(app).get("/eig/admin/1"); + expect(response.statusCode).toBe(403); + expect(eigService.getById).not.toHaveBeenCalled(); + }); + + it("should return an error 403 if you don't have dclaration sejour permission for eig", async () => { + checkPermissionBOEIG.mockImplementation((req, res, next) => { + return next( + new AppError( + "Vous n'êtes pas autorisé à accéder à cette déclaration de séjour", + { + statusCode: 403, + }, + ), + ); + }); + + const response = await request(app).get("/eig/admin/1"); + expect(response.statusCode).toBe(403); + expect(eigService.getById).not.toHaveBeenCalled(); + }); + + it("should call getById if everything is ok", async () => { + const response = await request(app).get("/eig/admin/1"); + expect(response.statusCode).toBe(200); + expect(eigService.getById).toHaveBeenCalled(); + }); +}); diff --git a/packages/backend/src/routes/__tests__/eig/get-admin.test.js b/packages/backend/src/routes/__tests__/eig/get-admin.test.js new file mode 100644 index 000000000..bdb0e84ff --- /dev/null +++ b/packages/backend/src/routes/__tests__/eig/get-admin.test.js @@ -0,0 +1,47 @@ +const boCheckJWT = require("../../../middlewares/bo-check-JWT"); +const request = require("supertest"); +const app = require("../../../app"); +const eigService = require("../../../services/eig"); + +jest.mock("../../../middlewares/bo-check-JWT"); +jest.mock("../../../services/eig"); + +describe("GET /eig/admin", () => { + afterEach(() => { + jest.clearAllMocks(); + }); + const user = { + id: 1, + }; + beforeEach(() => { + jest.resetAllMocks(); + boCheckJWT.mockImplementation((req, res, next) => { + req.decoded = { + ...user, + roles: ["eig", "DemandeSejour_Lecture", "DemandeSejour_Ecriture"], + }; + next(); + }); + + + }); + + it("shoud return a 403 if the admin don't have role eig", async () => { + boCheckJWT.mockImplementationOnce((req, res, next) => { + req.decoded = { + ...user, + roles: ["DemandeSejour_Lecture", "DemandeSejour_Ecriture"], + }; + next(); + }); + const response = await request(app).get("/eig/admin"); + expect(response.statusCode).toBe(403); + expect(eigService.getAdmin).not.toHaveBeenCalled(); + }); + + it("should call getByDsIdAdmin if everything is ok", async () => { + const response = await request(app).get("/eig/admin"); + expect(response.statusCode).toBe(200); + expect(eigService.getAdmin).toHaveBeenCalled(); + }); +}); diff --git a/packages/backend/src/routes/__tests__/eig/get-by-ds-id.test.ds.js b/packages/backend/src/routes/__tests__/eig/get-by-ds-id.test.ds.js new file mode 100644 index 000000000..5e8dca2cf --- /dev/null +++ b/packages/backend/src/routes/__tests__/eig/get-by-ds-id.test.ds.js @@ -0,0 +1,52 @@ +const checkJWT = require("../../../middlewares/checkJWT"); +const checkPermissionDeclarationSejour = require("../../../middlewares/checkPermissionDeclarationSejour"); +const AppError = require("../../../utils/error"); +const request = require("supertest"); +const app = require("../../../app"); +const eigService = require("../../../services/eig"); + +jest.mock("../../../middlewares/checkJWT"); +jest.mock("../../../middlewares/checkPermissionDeclarationSejour"); +jest.mock("../../../services/eig"); + +describe("GET /eig/ds/:declarationId", () => { + afterEach(() => { + jest.clearAllMocks(); + }); + const user = { + id: 1, + }; + beforeEach(() => { + jest.resetAllMocks(); + checkJWT.mockImplementation((req, res, next) => { + req.decoded = { ...user }; + next(); + }); + checkPermissionDeclarationSejour.mockImplementation((req, res, next) => { + next(); + }); + }); + + it("should return an error 403 if you don't have dclaration sejour permission for eig", async () => { + checkPermissionDeclarationSejour.mockImplementation((req, res, next) => { + return next( + new AppError( + "Vous n'êtes pas autorisé à accéder à cette déclaration de séjour", + { + statusCode: 403, + }, + ), + ); + }); + + const response = await request(app).get("/eig/ds/1"); + expect(response.statusCode).toBe(403); + expect(eigService.getByDsId).not.toHaveBeenCalled(); + }); + + it("should call getByDsId if everything is ok", async () => { + const response = await request(app).get("/eig/ds/1"); + expect(response.statusCode).toBe(200); + expect(eigService.getByDsId).toHaveBeenCalled(); + }); +}); diff --git a/packages/backend/src/routes/__tests__/eig/get-by-id.test.js b/packages/backend/src/routes/__tests__/eig/get-by-id.test.js new file mode 100644 index 000000000..6ae7dadc6 --- /dev/null +++ b/packages/backend/src/routes/__tests__/eig/get-by-id.test.js @@ -0,0 +1,59 @@ +const checkJWT = require("../../../middlewares/checkJWT"); +const checkPermissionEIG = require("../../../middlewares/checkPermissionEIG"); +const AppError = require("../../../utils/error"); +const request = require("supertest"); +const app = require("../../../app"); +const eigService = require("../../../services/eig"); +const { getEmails } = require("../../../helpers/eigMail"); + +jest.mock("../../../middlewares/checkJWT"); +jest.mock("../../../middlewares/checkPermissionEIG"); +jest.mock("../../../services/eig"); +jest.mock("../../../helpers/eigMail"); + +describe("GET /eig/id", () => { + const user = { + id: 1, + }; + afterEach(() => { + jest.clearAllMocks(); + }); + + beforeEach(() => { + jest.clearAllMocks(); + checkJWT.mockImplementation((req, res, next) => { + req.decoded = { ...user }; + next(); + }); + checkPermissionEIG.mockImplementation((req, res, next) => { + next(); + }); + eigService.getById.mockResolvedValue({ + declarationId: 1, + }); + getEmails.mockResolvedValue({}); + }); + + it("should return an error 403 if you don't have dclaration sejour permission for eig", async () => { + checkPermissionEIG.mockImplementation((req, res, next) => { + return next( + new AppError( + "Vous n'êtes pas autorisé à accéder à cette déclaration de séjour", + { + statusCode: 403, + }, + ), + ); + }); + + const response = await request(app).get("/eig/1"); + expect(response.statusCode).toBe(403); + expect(eigService.getById).not.toHaveBeenCalled(); + }); + + it("should call getByDsId if everything is ok", async () => { + const response = await request(app).get("/eig/1"); + expect(response.statusCode).toBe(200); + expect(eigService.getById).toHaveBeenCalled(); + }); +}); diff --git a/packages/backend/src/routes/__tests__/eig/get-me.test.js b/packages/backend/src/routes/__tests__/eig/get-me.test.js new file mode 100644 index 000000000..2f265adfd --- /dev/null +++ b/packages/backend/src/routes/__tests__/eig/get-me.test.js @@ -0,0 +1,35 @@ +const checkJWT = require("../../../middlewares/checkJWT"); +const request = require("supertest"); +const app = require("../../../app"); +const eigService = require("../../../services/eig"); + +jest.mock("../../../middlewares/checkJWT"); +jest.mock("../../../services/eig"); + +describe("GET /eig/me", () => { + const user = { + id: 1, + }; + beforeEach(() => { + jest.resetAllMocks(); + checkJWT.mockImplementation((req, res, next) => { + req.decoded = { ...user }; + next(); + }); + }); + afterEach(() => { + jest.clearAllMocks(); + }); + + it("should send a 400 if query params are not ok", async () => { + const response = await request(app).get("/eig/me?sortBy=badSort"); + expect(response.statusCode).toBe(400); + expect(eigService.getByUserId).not.toHaveBeenCalled(); + }); + + it("should call getByDsId if everything is ok", async () => { + const response = await request(app).get("/eig/me"); + expect(response.statusCode).toBe(200); + expect(eigService.getByUserId).toHaveBeenCalled(); + }); +}); diff --git a/packages/backend/src/routes/__tests__/eig/post-depose.test.js b/packages/backend/src/routes/__tests__/eig/post-depose.test.js new file mode 100644 index 000000000..27943b628 --- /dev/null +++ b/packages/backend/src/routes/__tests__/eig/post-depose.test.js @@ -0,0 +1,111 @@ +const checkJWT = require("../../../middlewares/checkJWT"); +const checkPermissionEIG = require("../../../middlewares/checkPermissionEIG"); +const canUpdateEig = require("../../../middlewares/can-update-or-delete-eig"); +const AppError = require("../../../utils/error"); +const request = require("supertest"); +const app = require("../../../app"); +const eigService = require("../../../services/eig"); +const DemandeSejour = require("../../../services/DemandeSejour"); +const yup = require("yup"); +const { getEmails } = require("../../../helpers/eigMail"); +const MailUtils = require("../../../utils/mail"); + +jest.mock("../../../middlewares/checkJWT"); +jest.mock("../../../middlewares/checkPermissionEIG"); +jest.mock("../../../middlewares/can-update-or-delete-eig"); +jest.mock("../../../services/eig"); +jest.mock("../../../services/DemandeSejour"); +jest.mock("../../../helpers/eigMail"); +jest.mock("../../../utils/mail"); +jest.mock("../../../services/geo/Commune"); +jest.mock("../../../services/mail"); + +describe("POST /depose/:id", () => { + const user = { + id: 1, + }; + beforeEach(() => { + jest.clearAllMocks(); + checkJWT.mockImplementation((req, res, next) => { + req.decoded = { ...user }; + next(); + }); + checkPermissionEIG.mockImplementation((req, res, next) => { + next(); + }); + canUpdateEig.mockImplementation((req, res, next) => { + next(); + }); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it("should return an error 403 if you don't have permission for eig", async () => { + checkPermissionEIG.mockImplementation((req, res, next) => { + return next( + new AppError("Vous n'êtes pas autorisé à accéder à cet EIG", { + statusCode: 403, + }), + ); + }); + + const response = await request(app) + .post("/eig/depose/1") + .send({ parametre: { declarationId: 1 } }); + expect(response.statusCode).toBe(403); + expect(eigService.depose).not.toHaveBeenCalled(); + }); + + it("should depose an eig if everything is ok", async () => { + DemandeSejour.getOne.mockResolvedValue({ + id: 1, + dateDebut: new Date("2024-01-01"), + dateFin: new Date("2024-01-10"), + hebergement: { hebergements: [] }, + responsableSejour: { email: "a@a.com" }, + }); + eigService.getById.mockResolvedValue({ + declarationId: 1, + emailAutresDestinataires: ["autre@autre.com"], + }); + jest.spyOn(yup, "object").mockImplementation(() => ({ + validate: (parametre) => { + return parametre; + }, + })); + getEmails.mockResolvedValue({}); + + const response = await request(app) + .post("/eig/depose/1") + .send({ parametre: { declarationId: 1 } }); + expect(response.statusCode).toBe(200); + expect(eigService.depose).toHaveBeenCalled(); + expect(MailUtils.bo.eig.sendToAutre).toHaveBeenCalled(); + + getEmails.mockResolvedValue({ emailsDDETS: ["a@a.com"] }); + await request(app) + .post("/eig/depose/1") + .send({ parametre: { declarationId: 1 } }); + expect(MailUtils.bo.eig.sendToDDETS).toHaveBeenCalledTimes(1); + expect(MailUtils.bo.eig.sendToDREETS).toHaveBeenCalledTimes(0); + expect(MailUtils.bo.eig.sendToOrganisme).toHaveBeenCalledTimes(0); + + getEmails.mockResolvedValue({ emailsDREETS: ["a@a.com"] }); + await request(app) + .post("/eig/depose/1") + .send({ parametre: { declarationId: 1 } }); + expect(MailUtils.bo.eig.sendToDDETS).toHaveBeenCalledTimes(1); + expect(MailUtils.bo.eig.sendToDREETS).toHaveBeenCalledTimes(1); + expect(MailUtils.bo.eig.sendToOrganisme).toHaveBeenCalledTimes(0); + + getEmails.mockResolvedValue({ emailsOrganisateur: ["a@a.com"] }); + await request(app) + .post("/eig/depose/1") + .send({ parametre: { declarationId: 1 } }); + expect(MailUtils.bo.eig.sendToDDETS).toHaveBeenCalledTimes(1); + expect(MailUtils.bo.eig.sendToDREETS).toHaveBeenCalledTimes(1); + expect(MailUtils.bo.eig.sendToOrganisme).toHaveBeenCalledTimes(1); + }); +}); diff --git a/packages/backend/src/routes/__tests__/eig/post-mark-as-read.test.js b/packages/backend/src/routes/__tests__/eig/post-mark-as-read.test.js new file mode 100644 index 000000000..882ff9b0e --- /dev/null +++ b/packages/backend/src/routes/__tests__/eig/post-mark-as-read.test.js @@ -0,0 +1,104 @@ +const boCheckJWT = require("../../../middlewares/bo-check-JWT"); + +const request = require("supertest"); +const app = require("../../../app"); +const eigService = require("../../../services/eig"); +const { statuts } = require("../../../helpers/eig"); + +jest.mock("../../../middlewares/bo-check-JWT"); +jest.mock("../../../services/eig"); +jest.mock("../../../services/DemandeSejour"); +jest.mock("../../../utils/mail"); +jest.mock("../../../services/mail"); +jest.mock("../../../services/geo/Region"); +jest.mock("../../../services/geo/Departement"); + +describe("GET /eig/admin/:id/mark-as-read", () => { + afterEach(() => { + jest.clearAllMocks(); + }); + const user = { + id: 1, + }; + beforeEach(() => { + jest.resetAllMocks(); + boCheckJWT.mockImplementation((req, res, next) => { + req.decoded = { + ...user, + roles: ["eig", "DemandeSejour_Lecture", "DemandeSejour_Ecriture"], + }; + next(); + }); + eigService.getById.mockReturnValue({ + agrementRegionObtention: "BRE", + territoireCode: 92, + statut: statuts.ENVOYE, + }); + }); + + it("shoud return a 403 if the admin don't have role eig", async () => { + boCheckJWT.mockImplementation((req, res, next) => { + req.decoded = { + ...user, + roles: ["DemandeSejour_Lecture", "DemandeSejour_Ecriture"], + }; + next(); + }); + const response = await request(app) + .post("/eig/admin/1/mark-as-read") + .send({}); + expect(response.statusCode).toBe(403); + expect(eigService.markAsRead).not.toHaveBeenCalled(); + }); + + it("should return a 400 if eig don't have the good statut", async () => { + eigService.getById.mockReturnValue({ statut: statuts.LU }); + const response = await request(app) + .post("/eig/admin/1/mark-as-read") + .send({}); + + expect(response.statusCode).toBe(400); + expect(eigService.markAsRead).not.toHaveBeenCalled(); + }); + + it("should return a 400 The user don't have any action to do", async () => { + boCheckJWT.mockImplementation((req, res, next) => { + req.decoded = { + ...user, + roles: ["DemandeSejour_Lecture", "DemandeSejour_Ecriture", "eig"], + territoireCode: 93, + }; + next(); + }); + const responseDep = await request(app) + .post("/eig/admin/1/mark-as-read") + .send({}); + + expect(responseDep.statusCode).toBe(400); + expect(eigService.markAsRead).not.toHaveBeenCalled(); + + boCheckJWT.mockImplementation((req, res, next) => { + req.decoded = { + ...user, + roles: ["DemandeSejour_Lecture", "DemandeSejour_Ecriture", "eig"], + territoireCode: "IDF", + }; + next(); + }); + const responseREG = await request(app) + .post("/eig/admin/1/mark-as-read") + .send({}); + + expect(responseREG.statusCode).toBe(400); + expect(eigService.markAsRead).not.toHaveBeenCalled(); + }); + + it("should call markAsRead if everything is of", async () => { + const response = await request(app) + .post("/eig/admin/1/mark-as-read") + .send({}); + + expect(response.statusCode).toBe(200); + expect(eigService.markAsRead).toHaveBeenCalledWith("1", "DDETS"); + }); +}); diff --git a/packages/backend/src/routes/__tests__/eig/post.test.js b/packages/backend/src/routes/__tests__/eig/post.test.js new file mode 100644 index 000000000..23f7f2ef3 --- /dev/null +++ b/packages/backend/src/routes/__tests__/eig/post.test.js @@ -0,0 +1,95 @@ +const checkJWT = require("../../../middlewares/checkJWT"); +const checkPermissionDeclarationSejourForEig = require("../../../middlewares/checkPermissionDeclarationSejourEig"); +const DemandeSejour = require("../../../services/DemandeSejour"); +const request = require("supertest"); +const app = require("../../../app"); +const { isDeclarationligibleToEig } = require("../../../helpers/eig"); +const yup = require("yup"); +const eigService = require("../../../services/eig"); + +jest.mock("../../../middlewares/checkJWT"); +jest.mock("../../../middlewares/checkPermissionDeclarationSejourEig"); +jest.mock("../../../services/DemandeSejour"); +jest.mock("../../../helpers/eig"); +jest.mock("../../../services/eig"); + +describe("POST /eig", () => { + afterEach(() => { + jest.clearAllMocks(); + }); + const user = { + id: 1, + }; + beforeEach(() => { + jest.clearAllMocks(); + checkJWT.mockImplementation((req, res, next) => { + req.decoded = { ...user }; + next(); + }); + checkPermissionDeclarationSejourForEig.mockImplementation( + (req, res, next) => { + next(); + }, + ); + }); + + it("should return an error 404 if declaration is missing", async () => { + DemandeSejour.getOne.mockResolvedValue(null); + + const response = await request(app) + .post("/eig") + .send({ parametre: { declarationId: 1 } }); + expect(response.statusCode).toBe(404); + }); + + it("should return an error 400 if declaration is not eligible", async () => { + DemandeSejour.getOne.mockResolvedValue({ id: 1 }); + isDeclarationligibleToEig.mockReturnValue(false); + const response = await request(app) + .post("/eig") + .send({ parametre: { declarationId: 1 } }); + expect(response.statusCode).toBe(400); + }); + + it("should return a validation error if the body is not validate by yup", async () => { + DemandeSejour.getOne.mockResolvedValue({ + id: 1, + dateDebut: new Date("2024-01-01"), + dateFin: new Date("2024-01-10"), + }); + isDeclarationligibleToEig.mockReturnValue(true); + jest.spyOn(yup, "object").mockImplementation(() => ({ + validate: () => { + throw new Error(); + }, + })); + const response = await request(app) + .post("/eig") + .send({ parametre: { declarationId: 1 } }); + + expect(yup.object).toHaveBeenCalled(); + expect(response.statusCode).toBe(400); + }); + + it("should call eig create service if everything is ok", async () => { + DemandeSejour.getOne.mockResolvedValue({ + id: 1, + dateDebut: new Date("2024-01-01"), + dateFin: new Date("2024-01-10"), + }); + isDeclarationligibleToEig.mockReturnValue(true); + jest.spyOn(yup, "object").mockImplementation(() => ({ + validate: (parametre) => { + return parametre; + }, + })); + eigService.create.mockResolvedValueOnce(1); + + const response = await request(app) + .post("/eig") + .send({ parametre: { declarationId: 1 } }); + + expect(eigService.create).toHaveBeenCalled(); + expect(response.statusCode).toBe(200); + }); +}); diff --git a/packages/backend/src/routes/__tests__/eig/put-update.js b/packages/backend/src/routes/__tests__/eig/put-update.js new file mode 100644 index 000000000..689748c5e --- /dev/null +++ b/packages/backend/src/routes/__tests__/eig/put-update.js @@ -0,0 +1,171 @@ +const checkJWT = require("../../../middlewares/checkJWT"); +const checkPermissionEIG = require("../../../middlewares/checkPermissionEIG"); +const checkPermissionDeclarationSejourForEig = require("../../../middlewares/checkPermissionDeclarationSejourEig"); +const canUpdateEig = require("../../../middlewares/can-update-or-delete-eig"); +const DemandeSejour = require("../../../services/DemandeSejour"); +const request = require("supertest"); +const app = require("../../../app"); +const eigService = require("../../../services/eig"); +const AppError = require("../../../utils/error"); +const { + isDeclarationligibleToEig, + UpdateTypes, +} = require("../../../helpers/eig"); +const yup = require("yup"); + +jest.mock("../../../middlewares/checkJWT"); +jest.mock("../../../middlewares/checkPermissionEIG"); +jest.mock("../../../middlewares/checkPermissionDeclarationSejourEig"); +jest.mock("../../../middlewares/can-update-or-delete-eig"); +jest.mock("../../../services/DemandeSejour"); +jest.mock("../../../services/eig"); +jest.mock("../../../helpers/eig"); + +describe("PUT /eig", () => { + const user = { + id: 1, + }; + beforeEach(() => { + jest.clearAllMocks(); + checkJWT.mockImplementation((req, res, next) => { + req.decoded = { ...user }; + next(); + }); + checkPermissionEIG.mockImplementation((req, res, next) => { + next(); + }); + checkPermissionDeclarationSejourForEig.mockImplementation( + (req, res, next) => { + next(); + }, + ); + canUpdateEig.mockImplementation((req, res, next) => { + next(); + }); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it("should return an error 404 if declaration is missing", async () => { + DemandeSejour.getOne.mockResolvedValue(null); + + const response = await request(app) + .put("/eig/1") + .send({ parametre: { declarationId: 1 } }); + expect(response.statusCode).toBe(404); + expect(eigService.updateDS).not.toHaveBeenCalled(); + expect(eigService.updateType).not.toHaveBeenCalled(); + expect(eigService.updateRenseignementsGeneraux).not.toHaveBeenCalled(); + expect(eigService.updateEmailAutresDestinataires).not.toHaveBeenCalled(); + }); + + it("should return an error 403 if you don't have permission for eig", async () => { + checkPermissionEIG.mockImplementation((req, res, next) => { + return next( + new AppError("Vous n'êtes pas autorisé à accéder à cet EIG", { + statusCode: 403, + }), + ); + }); + + const response = await request(app) + .put("/eig/1") + .send({ parametre: { declarationId: 1 } }); + expect(response.statusCode).toBe(403); + expect(eigService.updateDS).not.toHaveBeenCalled(); + expect(eigService.updateType).not.toHaveBeenCalled(); + expect(eigService.updateRenseignementsGeneraux).not.toHaveBeenCalled(); + expect(eigService.updateEmailAutresDestinataires).not.toHaveBeenCalled(); + }); + + it("should return an error 403 if you don't have dclaration sejour permission for eig", async () => { + checkPermissionDeclarationSejourForEig.mockImplementation( + (req, res, next) => { + return next( + new AppError( + "Vous n'êtes pas autorisé à accéder à cette déclaration de séjour", + { + statusCode: 403, + }, + ), + ); + }, + ); + DemandeSejour.getOne.mockResolvedValue({ id: 1 }); + isDeclarationligibleToEig.mockReturnValue(false); + + const response = await request(app) + .put("/eig/1") + .send({ parametre: { declarationId: 1 } }); + expect(response.statusCode).toBe(403); + expect(eigService.updateDS).not.toHaveBeenCalled(); + expect(eigService.updateType).not.toHaveBeenCalled(); + expect(eigService.updateRenseignementsGeneraux).not.toHaveBeenCalled(); + expect(eigService.updateEmailAutresDestinataires).not.toHaveBeenCalled(); + }); + + it("should return an error 400 if declaration is not eligible", async () => { + DemandeSejour.getOne.mockResolvedValue({ id: 1 }); + isDeclarationligibleToEig.mockReturnValue(false); + const response = await request(app) + .put("/eig/1") + .send({ parametre: { declarationId: 1 } }); + expect(response.statusCode).toBe(400); + expect(eigService.updateDS).not.toHaveBeenCalled(); + expect(eigService.updateType).not.toHaveBeenCalled(); + expect(eigService.updateRenseignementsGeneraux).not.toHaveBeenCalled(); + expect(eigService.updateEmailAutresDestinataires).not.toHaveBeenCalled(); + }); + + it("should call eig update service if everything is ok", async () => { + DemandeSejour.getOne.mockResolvedValue({ + id: 1, + dateDebut: new Date("2024-01-01"), + dateFin: new Date("2024-01-10"), + }); + isDeclarationligibleToEig.mockReturnValue(true); + jest.spyOn(yup, "object").mockImplementation((schema) => ({ + validate: (parametre, _) => { + return parametre; + }, + })); + + const responseDS = await request(app) + .put("/eig/1") + .send({ + parametre: { declarationId: 1 }, + type: UpdateTypes.DECLARATION_SEJOUR, + }); + expect(eigService.updateDS).toHaveBeenCalled(); + expect(responseDS.statusCode).toBe(200); + + const responseType = await request(app) + .put("/eig/1") + .send({ + parametre: { declarationId: 1 }, + type: UpdateTypes.TYPE_EVENEMENT, + }); + expect(eigService.updateType).toHaveBeenCalled(); + expect(responseType.statusCode).toBe(200); + + const responseRG = await request(app) + .put("/eig/1") + .send({ + parametre: { declarationId: 1 }, + type: UpdateTypes.RENSEIGNEMENT_GENERAUX, + }); + expect(eigService.updateRenseignementsGeneraux).toHaveBeenCalled(); + expect(responseRG.statusCode).toBe(200); + + const responseMail = await request(app) + .put("/eig/1") + .send({ + parametre: { declarationId: 1 }, + type: UpdateTypes.EMAIL_AUTRES_DESTINATAIRES, + }); + expect(eigService.updateEmailAutresDestinataires).toHaveBeenCalled(); + expect(responseMail.statusCode).toBe(200); + }); +}); diff --git a/packages/backend/src/routes/eig.js b/packages/backend/src/routes/eig.js index 4de26d45b..a8e3d5b93 100644 --- a/packages/backend/src/routes/eig.js +++ b/packages/backend/src/routes/eig.js @@ -78,7 +78,6 @@ router.delete( router.post( "/admin/:id/mark-as-read", boCheckJWT, - getDepartements, boCheckRoleEig, eigController.markAsRead, ); diff --git a/packages/backend/src/schemas/__tests__/eig.js b/packages/backend/src/schemas/__tests__/eig.js new file mode 100644 index 000000000..57129a9e9 --- /dev/null +++ b/packages/backend/src/schemas/__tests__/eig.js @@ -0,0 +1,203 @@ +const yup = require("yup"); +const { + selectionSejourSchema, + eigTypesSchema, + informationsGeneralesSchema, + emailAutresDestinatairesSchema, +} = require("../eig"); +const { Types, Categorie } = require("../../helpers/eig"); +const { + fonctionOptions, +} = require("../../helpers/declaration/informations-personnel"); + +describe("YUP - eig", () => { + test("selectionSejourSchema", async () => { + const eig1 = { + declarationId: 1, + departement: "92", + date: new Date("2021-01-02"), + }; + const eig2 = { + departement: "92", + date: new Date("2021-01-02"), + }; + const eig3 = { + declarationId: 1, + date: new Date("2021-01-02"), + }; + const eig4 = { + declarationId: 1, + departement: "92", + }; + + const schema = yup.object( + selectionSejourSchema("2021-01-01", "2021-01-10"), + ); + expect(await schema.validate(eig1)).toEqual(eig1); + await expect(() => schema.validate(eig2)).rejects.toThrow( + "Ce champ est obligatoire", + ); + await expect(() => schema.validate(eig3)).rejects.toThrow( + "Ce champ est obligatoire", + ); + await expect(() => schema.validate(eig4)).rejects.toThrow( + "Ce champ est obligatoire", + ); + await expect(() => + schema.validate({ ...eig4, date: "bad date" }), + ).rejects.toThrow("La date n'est pas au format attendu"); + await expect(() => + schema.validate({ ...eig4, date: "2020-12-31" }), + ).rejects.toThrow( + "La date de l'incident doit être supérieure à la date de début de séjour", + ); + await expect(() => + schema.validate({ ...eig4, date: "2021-01-11" }), + ).rejects.toThrow( + "La date de l'incident doit être inférieure à la date de fin de séjour", + ); + }); + + test("eigTypesSchemaCRUD", async () => { + const eig1 = { + types: ["bad type"], + }; + const eig2 = { + types: [Types[Categorie.FONCTIONNEMENT_ORGANISME].AUTRE], + }; + const eig3 = { + types: [Types[Categorie.SANTE].AUTRE], + }; + const eig4 = { + types: [Types[Categorie.SECURITE].AUTRE], + }; + const eig5 = { + types: [Types[Categorie.VICTIMES].AUTRE], + }; + + const schema = yup.object(eigTypesSchema); + + await expect(() => schema.validate(eig1)).rejects.toThrow( + "La valeur insérée ne fait pas partie de la liste des possibles", + ); + + // champs "autre" pour la catégorie FONCTIONNEMENT_ORGANISME + await expect(() => schema.validate(eig2)).rejects.toThrow( + "Ce champ est obligatoire", + ); + await expect(() => + schema.validate({ ...eig2, fonctionnementAutrePrecision: "aaaa" }), + ).rejects.toThrow("Ce champ doit faire au moins 5 caractères"); + expect( + await schema.validate({ ...eig2, fonctionnementAutrePrecision: "aaaaa" }), + ).toEqual({ ...eig2, fonctionnementAutrePrecision: "aaaaa" }); + + // champs "autre" pour la catégorie SANTE + await expect(() => schema.validate(eig3)).rejects.toThrow( + "Ce champ est obligatoire", + ); + await expect(() => + schema.validate({ ...eig3, santeAutrePrecision: "aaaa" }), + ).rejects.toThrow("Ce champ doit faire au moins 5 caractères"); + expect( + await schema.validate({ ...eig3, santeAutrePrecision: "aaaaa" }), + ).toEqual({ ...eig3, santeAutrePrecision: "aaaaa" }); + + // champs "autre" pour la catégorie SECURITE + await expect(() => schema.validate(eig4)).rejects.toThrow( + "Ce champ est obligatoire", + ); + await expect(() => + schema.validate({ ...eig4, securiteAutrePrecision: "aaaa" }), + ).rejects.toThrow("Ce champ doit faire au moins 5 caractères"); + expect( + await schema.validate({ ...eig4, securiteAutrePrecision: "aaaaa" }), + ).toEqual({ ...eig4, securiteAutrePrecision: "aaaaa" }); + + // champs "autre" pour la catégorie VICTIMES + await expect(() => schema.validate(eig5)).rejects.toThrow( + "Ce champ est obligatoire", + ); + await expect(() => + schema.validate({ ...eig5, victimesAutrePrecision: "aaaa" }), + ).rejects.toThrow("Ce champ doit faire au moins 5 caractères"); + expect( + await schema.validate({ ...eig5, victimesAutrePrecision: "aaaaa" }), + ).toEqual({ ...eig5, victimesAutrePrecision: "aaaaa" }); + }); + + test("informationsGeneralesSchema", async () => { + const personnel = [ + { + nom: "nom", + prenom: "prenom", + telephone: "0666666666", + listeFonction: [fonctionOptions[0].value], + dateNaissance: new Date("1986-05-14"), + competence: "eeee", + }, + ]; + + const eig = { + deroulement: "aaaaa", + dispositionInformations: "bbbbb", + dispositionRemediation: "ccccc", + dispositionVictimes: "ddddd", + personnel, + }; + + const schema = yup.object(informationsGeneralesSchema); + expect(await schema.validate(eig)).toEqual(eig); + + await expect(() => + schema.validate({ ...eig, deroulement: "a" }), + ).rejects.toThrow("Ce champ doit faire au moins 5 caractères"); + await expect(() => + schema.validate({ ...eig, deroulement: undefined }), + ).rejects.toThrow("Ce champ est obligatoire"); + + await expect(() => + schema.validate({ ...eig, dispositionInformations: "a" }), + ).rejects.toThrow("Ce champ doit faire au moins 5 caractères"); + await expect(() => + schema.validate({ ...eig, dispositionInformations: undefined }), + ).rejects.toThrow("Ce champ est obligatoire"); + + await expect(() => + schema.validate({ ...eig, dispositionRemediation: "a" }), + ).rejects.toThrow("Ce champ doit faire au moins 5 caractères"); + await expect(() => + schema.validate({ ...eig, dispositionRemediation: undefined }), + ).rejects.toThrow("Ce champ est obligatoire"); + + await expect(() => + schema.validate({ ...eig, dispositionVictimes: "a" }), + ).rejects.toThrow("Ce champ doit faire au moins 5 caractères"); + await expect(() => + schema.validate({ ...eig, dispositionVictimes: undefined }), + ).rejects.toThrow("Ce champ est obligatoire"); + + await expect(() => + schema.validate({ ...eig, personnel: [] }), + ).rejects.toThrow("Vous devez saisir au moins 1 personnel"); + await expect(() => + schema.validate({ ...eig, personnel: undefined }), + ).rejects.toThrow("Ce champ est obligatoire"); + }); + + test("emailAutresDestinatairesSchema", async () => { + const eig1 = { + emailAutresDestinataires: ["a@a.com", "b@b.fr"], + }; + const eig2 = { + emailAutresDestinataires: ["a@"], + }; + + const schema = yup.object(emailAutresDestinatairesSchema); + + expect(await schema.validate(eig1)).toEqual(eig1); + await expect(() => schema.validate(eig2)).rejects.toThrow( + "Format de courriel invalide", + ); + }); +}); diff --git a/packages/backend/src/schemas/eig.js b/packages/backend/src/schemas/eig.js index 94cf9862e..a61c4c545 100644 --- a/packages/backend/src/schemas/eig.js +++ b/packages/backend/src/schemas/eig.js @@ -11,8 +11,8 @@ const selectionSejourSchema = (dateDebut, dateFin) => ({ declarationId: yup .number() .integer("Ce champ doit contenir un nombre entier") - .required(), - departement: yup.string().required("ce champ est obligatoire"), + .required("Ce champ est obligatoire"), + departement: yup.string().required("Ce champ est obligatoire"), date: yup .date() .typeError("La date n'est pas au format attendu") @@ -24,7 +24,7 @@ const selectionSejourSchema = (dateDebut, dateFin) => ({ dateFin, "La date de l'incident doit être inférieure à la date de fin de séjour", ) - .required(), + .required("Ce champ est obligatoire"), }); const eigTypeBase = yup @@ -44,7 +44,7 @@ const eigTypeBase = yup (type) => isTypeActive(type), ), ], - "la valeur insérée ne fait pas partie de la liste des possibles", + "La valeur insérée ne fait pas partie de la liste des possibles", ) .required(); @@ -96,7 +96,9 @@ const eigTypesSchemaCRUD = { }, otherwise: (precision) => precision.nullable().strip(), then: (precision) => - precision.min(5, "Ce champ est obligatoire").required(), + precision + .required("Ce champ est obligatoire") + .min(5, "Ce champ doit faire au moins 5 caractères"), }), santeAutrePrecision: yup .string() @@ -107,7 +109,9 @@ const eigTypesSchemaCRUD = { }, otherwise: (precision) => precision.nullable().strip(), then: (precision) => - precision.min(5, "Ce champ est obligatoire").required(), + precision + .required("Ce champ est obligatoire") + .min(5, "Ce champ doit faire au moins 5 caractères"), }), securiteAutrePrecision: yup .string() @@ -118,9 +122,15 @@ const eigTypesSchemaCRUD = { }, otherwise: (precision) => precision.nullable().strip(), then: (precision) => - precision.min(5, "Ce champ est obligatoire").required(), + precision + .required("Ce champ est obligatoire") + .min(5, "Ce champ doit faire au moins 5 caractères"), }), - types: yup.array().of(eigTypeBase).min(1).required(), + types: yup + .array() + .of(eigTypeBase) + .min(1) + .required("Ce champ est obligatoire"), victimesAutrePrecision: yup .string() .nullable() @@ -130,24 +140,29 @@ const eigTypesSchemaCRUD = { }, otherwise: (precision) => precision.nullable().strip(), then: (precision) => - precision.min(5, "Ce champ est obligatoire").required(), + precision + .required("Ce champ est obligatoire") + .min(5, "Ce champ doit faire au moins 5 caractères"), }), }; const informationsGeneralesSchema = { - deroulement: yup.string().min(5, "Ce champ est obligatoire").required(), + deroulement: yup + .string() + .min(5, "Ce champ doit faire au moins 5 caractères") + .required("Ce champ est obligatoire"), dispositionInformations: yup .string() - .min(5, "Ce champ est obligatoire") - .required(), + .min(5, "Ce champ doit faire au moins 5 caractères") + .required("Ce champ est obligatoire"), dispositionRemediation: yup .string() - .min(5, "Ce champ est obligatoire") - .required(), + .min(5, "Ce champ doit faire au moins 5 caractères") + .required("Ce champ est obligatoire"), dispositionVictimes: yup .string() - .min(5, "Ce champ est obligatoire") - .required(), + .min(5, "Ce champ doit faire au moins 5 caractères") + .required("Ce champ est obligatoire"), personnel: yup .array() .of( @@ -165,7 +180,7 @@ const informationsGeneralesSchema = { ), ) .min(1, "Vous devez saisir au moins 1 personnel") - .required(), + .required("Ce champ est obligatoire"), }; const emailAutresDestinatairesSchema = { @@ -185,6 +200,7 @@ module.exports.selectionSejourSchema = selectionSejourSchema; module.exports.eigTypesSchema = eigTypesSchemaCRUD; module.exports.informationsGeneralesSchema = informationsGeneralesSchema; module.exports.syntheseSchema = syntheseSchema; +module.exports.emailAutresDestinatairesSchema = emailAutresDestinatairesSchema; module.exports.updateSchemaAdapteur = (type, dateRange) => { switch (type) {