Skip to content

Commit e09e4bb

Browse files
adminl-scherer
authored andcommitted
feat: 1084 agrement etape un
1 parent 7206857 commit e09e4bb

21 files changed

Lines changed: 1342 additions & 3 deletions

File tree

.talismanrc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,10 @@ fileignoreconfig:
229229
checksum: 8513c95ff253eb7ef038d5cdd5aff7d09b478c31409ea5644cd0081afbf6428e
230230
- filename: packages/frontend-usagers/src/components/address/search-address.vue
231231
checksum: e22412a54064dea24f7e200e9887726a4ef110f13570feea628c70c4fe2886a9
232+
- filename: packages/frontend-usagers/src/components/agrement/personne-morale.vue
233+
checksum: 76de358518d21d7cf5b522e31979b676147585540098a1419976d0120edaac93
234+
- filename: packages/frontend-usagers/src/components/agrement/representants.vue
235+
checksum: e1944c26baf528554fbfb052068ccbdf9ac9b158b5152760fae9b2bc0858b4e9
232236
- filename: packages/frontend-usagers/src/components/customInput.vue
233237
checksum: ff3d0adca5a28cd4e4c25ed981250d5b8e253f8554ad4ec7280a2b05900faf33
234238
- filename: packages/frontend-usagers/src/components/demande-sejour/table.vue
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import type { Request, Response } from "express";
2+
3+
import getByOrganismeIdController from "../../controllers/agrement"; // ton contrôleur
4+
import getByOrganismeIdService from "../../services/agrements/Agrements";
5+
import AppError from "../../utils/error";
6+
7+
// On mocke les dépendances
8+
jest.mock("../../services/agrements/Agrements");
9+
jest.mock("../../utils/logger", () => () => ({
10+
d: jest.fn(),
11+
i: jest.fn(),
12+
w: jest.fn(),
13+
}));
14+
15+
describe("🧪 Controller: agrement.getByOrganismeId", () => {
16+
const mockReq = {} as Request;
17+
const mockRes = {} as Response;
18+
const mockNext = jest.fn();
19+
20+
beforeEach(() => {
21+
jest.clearAllMocks();
22+
mockRes.json = jest.fn().mockReturnValue(mockRes);
23+
});
24+
25+
it("Renvoie le résultat du service avec un ID valide", async () => {
26+
(getByOrganismeIdService as jest.Mock).mockResolvedValue({
27+
agrement: { id: 1, organismeId: 42 },
28+
});
29+
30+
mockReq.params = { id: "42" };
31+
32+
await getByOrganismeIdController.getByOrganismeId(
33+
mockReq as any,
34+
mockRes,
35+
mockNext,
36+
);
37+
38+
expect(getByOrganismeIdService).toHaveBeenCalledWith({ organismeId: 42 });
39+
expect(mockRes.json).toHaveBeenCalledWith({
40+
agrement: { id: 1, organismeId: 42 },
41+
});
42+
expect(mockNext).not.toHaveBeenCalled();
43+
});
44+
45+
it("Appelle next avec une AppError si l'id est invalide", async () => {
46+
mockReq.params = { id: "abc" };
47+
48+
await getByOrganismeIdController.getByOrganismeId(
49+
mockReq as any,
50+
mockRes,
51+
mockNext,
52+
);
53+
54+
expect(getByOrganismeIdService).not.toHaveBeenCalled();
55+
expect(mockNext).toHaveBeenCalledTimes(1);
56+
57+
const error = mockNext.mock.calls[0][0];
58+
expect(error).toBeInstanceOf(AppError);
59+
expect(error.message).toBe("Paramètre incorrect");
60+
expect(error.statusCode).toBe(400);
61+
});
62+
63+
it("Appelle next si le service lève une erreur", async () => {
64+
(getByOrganismeIdService as jest.Mock).mockRejectedValue(
65+
new Error("DB fail"),
66+
);
67+
mockReq.params = { id: "10" };
68+
69+
await getByOrganismeIdController.getByOrganismeId(
70+
mockReq as any,
71+
mockRes,
72+
mockNext,
73+
);
74+
75+
expect(getByOrganismeIdService).toHaveBeenCalledWith({ organismeId: 10 });
76+
expect(mockNext).toHaveBeenCalledTimes(1);
77+
78+
const error = mockNext.mock.calls[0][0];
79+
expect(error).toBeInstanceOf(Error);
80+
expect(error.message).toBe("DB fail");
81+
});
82+
});
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import type { NextFunction, Response } from "express";
2+
3+
import getByOrganismeId from "../../services/agrements/Agrements";
4+
import type { UserRequest } from "../../types/request";
5+
import AppError from "../../utils/error";
6+
import logger from "../../utils/logger";
7+
8+
const log = logger(module.filename);
9+
10+
export default async function get(
11+
req: UserRequest,
12+
res: Response,
13+
next: NextFunction,
14+
) {
15+
log.i("IN");
16+
const organismeId = Number(req.params.id);
17+
18+
if (Number.isNaN(organismeId)) {
19+
log.w("missing or invalid parameter");
20+
return next(
21+
new AppError("Paramètre incorrect", {
22+
statusCode: 400,
23+
}),
24+
);
25+
}
26+
27+
try {
28+
const { agrement } = await getByOrganismeId({
29+
organismeId,
30+
});
31+
log.d(agrement);
32+
res.json({ agrement });
33+
} catch (error) {
34+
log.w("DONE with error");
35+
next(error);
36+
}
37+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import Logger from "../../utils/logger";
2+
import { getPool } from "../../utils/pgpool";
3+
4+
const log = Logger(module.filename);
5+
6+
const pool = getPool();
7+
8+
export const AgrementsRepository = {
9+
getByOrganismeId: async ({ organismeId }: { organismeId: number }) => {
10+
log.i("getByOrganismeId - IN");
11+
12+
const query = () => `
13+
SELECT
14+
*
15+
FROM
16+
front.agrements agr
17+
WHERE
18+
agr.organisme_id = $1
19+
`;
20+
const response = await pool.query(query(), [organismeId]);
21+
log.i("getAdminStats - DONE");
22+
23+
return {
24+
agrement: response.rows[0],
25+
};
26+
},
27+
};
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import express from "express";
2+
import request from "supertest";
3+
4+
import agrementController from "../../../controllers/agrement";
5+
import checkJWT from "../../../middlewares/checkJWT";
6+
import checkPermissionAgrement from "../../../middlewares/checkPermissionAgrement";
7+
import router from "../../agrement";
8+
9+
// 🧱 On mocke les middlewares et le contrôleur
10+
jest.mock("../../../middlewares/checkJWT", () =>
11+
jest.fn((req, res, next) => next()),
12+
);
13+
jest.mock("../../../middlewares/checkPermissionAgrement", () =>
14+
jest.fn((req, res, next) => next()),
15+
);
16+
jest.mock("../../../controllers/agrement", () => ({
17+
getByOrganismeId: jest.fn((req, res) => res.status(200).json({ ok: true })),
18+
post: jest.fn((req, res) => res.status(201).json({ created: true })),
19+
}));
20+
21+
const app = express();
22+
app.use(express.json());
23+
app.use("/agrement", router);
24+
25+
describe("🧪 Routes /agrement", () => {
26+
beforeEach(() => {
27+
jest.clearAllMocks();
28+
});
29+
30+
it("GET /agrement/organisme/:id appelle les middlewares et le contrôleur", async () => {
31+
const res = await request(app).get("/agrement/organisme/123");
32+
33+
expect(checkJWT).toHaveBeenCalled();
34+
expect(checkPermissionAgrement).toHaveBeenCalled();
35+
expect(agrementController.getByOrganismeId).toHaveBeenCalled();
36+
expect(res.status).toBe(200);
37+
expect(res.body).toEqual({ ok: true });
38+
});
39+
40+
it("POST /agrement appelle les middlewares et le contrôleur post", async () => {
41+
const res = await request(app).post("/agrement").send({ organismeId: 42 });
42+
43+
expect(checkJWT).toHaveBeenCalled();
44+
expect(checkPermissionAgrement).toHaveBeenCalled();
45+
expect(agrementController.post).toHaveBeenCalled();
46+
expect(res.status).toBe(201);
47+
expect(res.body).toEqual({ created: true });
48+
});
49+
});
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import express from "express";
2+
3+
import agrementController from "../controllers/agrement";
4+
import checkJWT from "../middlewares/checkJWT";
5+
import checkPermissionAgrement from "../middlewares/checkPermissionAgrement";
6+
7+
const router = express.Router();
8+
9+
router.get(
10+
"/organisme/:id",
11+
checkJWT,
12+
checkPermissionAgrement,
13+
agrementController.getByOrganismeId,
14+
);
15+
16+
router.post("/", checkJWT, checkPermissionAgrement, agrementController.post);
17+
18+
export default router;

packages/backend/src/routes/index.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ module.exports.organisme = require("./organisme");
99
module.exports.siret = require("./siret");
1010
module.exports.sejour = require("./sejour");
1111
module.exports.hebergement = require("./hebergement").default;
12-
1312
module.exports.message = require("./message");
1413
module.exports.territoire = require("./territoire");
1514

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import type { AgrementsDto } from "../../dto/AgrementsDto";
2+
import { AgrementsRepository } from "../../repositories/usagers/Agrements";
3+
import getByOrganismeId from "../agrements/Agrements";
4+
5+
// On mocke le module repository
6+
jest.mock("../../repositories/usagers/Agrements", () => ({
7+
AgrementsRepository: {
8+
getByOrganismeId: jest.fn(),
9+
},
10+
}));
11+
12+
describe("Service: getByOrganismeId", () => {
13+
const mockAgrement: AgrementsDto = {
14+
dateDebut: "2025-01-01",
15+
dateFin: "2025-12-31",
16+
id: 123,
17+
organismeId: 1,
18+
statut: "VALIDE",
19+
} as unknown as AgrementsDto;
20+
21+
beforeEach(() => {
22+
jest.clearAllMocks();
23+
});
24+
25+
it("devrait appeler AgrementsRepository.getByOrganismeId avec le bon id", async () => {
26+
(AgrementsRepository.getByOrganismeId as jest.Mock).mockResolvedValue({
27+
agrement: mockAgrement,
28+
});
29+
30+
const organismeId = 42;
31+
await getByOrganismeId({ organismeId });
32+
33+
expect(AgrementsRepository.getByOrganismeId).toHaveBeenCalledTimes(1);
34+
expect(AgrementsRepository.getByOrganismeId).toHaveBeenCalledWith({
35+
organismeId,
36+
});
37+
});
38+
39+
it("devrait retourner un objet contenant l'agrement", async () => {
40+
(AgrementsRepository.getByOrganismeId as jest.Mock).mockResolvedValue({
41+
agrement: mockAgrement,
42+
});
43+
44+
const result = await getByOrganismeId({ organismeId: 99 });
45+
46+
expect(result).toEqual({ agrement: mockAgrement });
47+
});
48+
49+
it("devrait propager une erreur si le repository échoue", async () => {
50+
(AgrementsRepository.getByOrganismeId as jest.Mock).mockRejectedValue(
51+
new Error("Erreur DB"),
52+
);
53+
54+
await expect(getByOrganismeId({ organismeId: 10 })).rejects.toThrow(
55+
"Erreur DB",
56+
);
57+
});
58+
});
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { AgrementsDto } from "../../dto/AgrementsDto";
2+
import { AgrementsRepository } from "../../repositories/usagers/Agrements";
3+
4+
export default async function getByOrganismeId({
5+
organismeId,
6+
}: {
7+
organismeId: number;
8+
}) {
9+
const { agrement }: { agrement: AgrementsDto } =
10+
await AgrementsRepository.getByOrganismeId({
11+
organismeId,
12+
});
13+
14+
return { agrement };
15+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<template>
2+
<DsfrSideMenu heading-title="">
3+
<DsfrSideMenuList
4+
id="agrement-menu-list"
5+
:menu-items="sommaireOptionsToDisplay"
6+
@select="onSelect"
7+
/>
8+
</DsfrSideMenu>
9+
</template>
10+
11+
<script setup>
12+
const props = defineProps({
13+
activeId: { type: String, default: "coordonnees", required: false },
14+
});
15+
16+
const emit = defineEmits(["select"]);
17+
18+
const menus = [
19+
{
20+
id: "coordonnees",
21+
text: "Coordonnées à vérifier",
22+
to: { hash: "#coordonnees" },
23+
},
24+
{ id: "dossier", text: "Dossier de candidature", to: { hash: "#dossier" } },
25+
{
26+
id: "bilan",
27+
text: "Bilan des 4 années précédentes",
28+
to: { hash: "#bilan" },
29+
},
30+
{
31+
id: "projets",
32+
text: "Projets de séjours envisagés pour les 12 prochains mois",
33+
to: { hash: "#projets" },
34+
},
35+
{ id: "synthese", text: "Synthèse", to: { hash: "#synthese" } },
36+
];
37+
38+
const sommaireOptionsToDisplay = computed(() =>
39+
menus.map((menu) => ({
40+
...menu,
41+
active: menu.id === props.activeId,
42+
})),
43+
);
44+
45+
function onSelect(idx) {
46+
emit("select", menus[idx].id);
47+
}
48+
</script>

0 commit comments

Comments
 (0)