changement

This commit is contained in:
Emma781227
2026-09-14 22:30:40 +02:00
parent 8a6355cde1
commit 2201446ee6
54 changed files with 2495 additions and 139 deletions

View File

@@ -0,0 +1,166 @@
const request = require("supertest");
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");
const app = require("../src/app");
const prisma = require("../src/config/prisma");
const mailer = require("../src/config/mailer");
const { passwordRuleError } = require("../src/validations/password.validation");
jest.mock("../src/config/mailer", () => ({ sendMail: jest.fn() }));
const PREFIX = "onboarding-";
const ADMIN_EMAIL = PREFIX + "admin@test.local";
const EMPLOYEE_EMAIL = PREFIX + "employe@test.local";
const ctx = {};
const tk = (u) => jwt.sign({ id: u.id, role: u.role }, process.env.JWT_SECRET, { expiresIn: "1h" });
// Récupère le mot de passe provisoire dans l'email envoyé à l'employé.
function tempPasswordFromMail() {
const html = mailer.sendMail.mock.calls[0][0].html;
return html.match(/Mot de passe provisoire : <strong[^>]*>([^<]+)<\/strong>/)[1];
}
async function cleanup() {
await prisma.user.deleteMany({ where: { email: { startsWith: PREFIX } } });
}
beforeAll(async () => {
await cleanup();
const h = await bcrypt.hash("Password123", 10);
ctx.admin = await prisma.user.create({
data: { firstname: "Alice", lastname: "Admin", email: ADMIN_EMAIL, password: h, role: "ADMIN", isEmailVerified: true },
});
ctx.tAdmin = tk(ctx.admin);
});
afterAll(async () => {
await cleanup();
await prisma.$disconnect();
});
describe("Creation d un employe avec mot de passe provisoire", () => {
test("un echec d envoi de l email annule la creation du compte", async () => {
mailer.sendMail.mockRejectedValueOnce(new Error("SMTP indisponible"));
jest.spyOn(console, "error").mockImplementation(() => {});
const res = await request(app)
.post("/api/admin/employees")
.set("Authorization", `Bearer ${ctx.tAdmin}`)
.send({ firstname: "Marc", lastname: "Durand", email: EMPLOYEE_EMAIL });
console.error.mockRestore();
expect(res.status).toBe(502);
expect(await prisma.user.findUnique({ where: { email: EMPLOYEE_EMAIL } })).toBeNull();
});
test("l admin cree l employe sans mot de passe, un mot de passe provisoire est envoye", async () => {
mailer.sendMail.mockClear();
mailer.sendMail.mockResolvedValue(true);
const res = await request(app)
.post("/api/admin/employees")
.set("Authorization", `Bearer ${ctx.tAdmin}`)
// Un mot de passe choisi par l'admin est ignoré.
.send({ firstname: "Marc", lastname: "Durand", email: EMPLOYEE_EMAIL, password: "ChoixAdmin123" });
expect(res.status).toBe(201);
expect(res.body.employee.role).toBe("EMPLOYEE");
// Le mot de passe ne transite jamais par la réponse faite à l'admin.
expect(JSON.stringify(res.body)).not.toMatch(/password/i);
expect(mailer.sendMail).toHaveBeenCalledTimes(1);
expect(mailer.sendMail.mock.calls[0][0].to).toBe(EMPLOYEE_EMAIL);
ctx.tempPassword = tempPasswordFromMail();
expect(ctx.tempPassword).toHaveLength(12);
expect(ctx.tempPassword).not.toBe("ChoixAdmin123");
// Le provisoire respecte lui aussi la règle commune.
expect(passwordRuleError(ctx.tempPassword)).toBeNull();
const enBase = await prisma.user.findUnique({ where: { email: EMPLOYEE_EMAIL } });
expect(enBase.mustChangePassword).toBe(true);
expect(await bcrypt.compare("ChoixAdmin123", enBase.password)).toBe(false);
});
test("la connexion avec le mot de passe provisoire signale le changement obligatoire", async () => {
const res = await request(app).post("/api/auth/login").send({ email: EMPLOYEE_EMAIL, password: ctx.tempPassword });
expect(res.status).toBe(200);
expect(res.body.user.mustChangePassword).toBe(true);
ctx.tEmployee = res.body.token;
});
test("tant que le mot de passe n est pas change, l API est bloquee sauf /me", async () => {
const bloque = await request(app).get("/api/employee/ticket/INCONNU").set("Authorization", `Bearer ${ctx.tEmployee}`);
expect(bloque.status).toBe(403);
expect(bloque.body.code).toBe("PASSWORD_CHANGE_REQUIRED");
const changement = await request(app)
.patch("/api/auth/change-password")
.set("Authorization", `Bearer ${ctx.tEmployee}`)
.send({ currentPassword: ctx.tempPassword, newPassword: "Contournement1" });
expect(changement.status).toBe(403);
const me = await request(app).get("/api/auth/me").set("Authorization", `Bearer ${ctx.tEmployee}`);
expect(me.status).toBe(200);
expect(me.body.user.mustChangePassword).toBe(true);
});
test("le nouveau mot de passe doit etre confirme et different du provisoire", async () => {
const auth = { Authorization: `Bearer ${ctx.tEmployee}` };
const different = await request(app)
.post("/api/auth/initial-password")
.set(auth)
.send({ password: "NouveauMdp123!", confirmPassword: "AutreMdp456!" });
expect(different.status).toBe(400);
expect(different.body.message).toMatch(/ne correspondent pas/);
const court = await request(app)
.post("/api/auth/initial-password")
.set(auth)
.send({ password: "court", confirmPassword: "court" });
expect(court.status).toBe(400);
const sansSpecial = await request(app)
.post("/api/auth/initial-password")
.set(auth)
.send({ password: "NouveauMdp123", confirmPassword: "NouveauMdp123" });
expect(sansSpecial.status).toBe(400);
expect(sansSpecial.body.message).toMatch(/caractère spécial/);
const identique = await request(app)
.post("/api/auth/initial-password")
.set(auth)
.send({ password: ctx.tempPassword, confirmPassword: ctx.tempPassword });
expect(identique.status).toBe(400);
expect(identique.body.message).toMatch(/provisoire/);
});
test("apres le changement, l acces est retabli et seul le nouveau mot de passe fonctionne", async () => {
const auth = { Authorization: `Bearer ${ctx.tEmployee}` };
const ok = await request(app)
.post("/api/auth/initial-password")
.set(auth)
.send({ password: "NouveauMdp123!", confirmPassword: "NouveauMdp123!" });
expect(ok.status).toBe(200);
// Le ticket n'existe pas : un 404 prouve que la requête a franchi le filtre.
const acces = await request(app).get("/api/employee/ticket/INCONNU").set(auth);
expect(acces.status).not.toBe(403);
const encore = await request(app)
.post("/api/auth/initial-password")
.set(auth)
.send({ password: "EncoreUnAutre1!", confirmPassword: "EncoreUnAutre1!" });
expect(encore.status).toBe(400);
const ancien = await request(app).post("/api/auth/login").send({ email: EMPLOYEE_EMAIL, password: ctx.tempPassword });
expect(ancien.status).toBe(401);
const nouveau = await request(app).post("/api/auth/login").send({ email: EMPLOYEE_EMAIL, password: "NouveauMdp123!" });
expect(nouveau.status).toBe(200);
expect(nouveau.body.user.mustChangePassword).toBe(false);
});
});