143 lines
5.2 KiB
JavaScript
143 lines
5.2 KiB
JavaScript
const request = require("supertest");
|
|
const app = require("../src/app");
|
|
const prisma = require("../src/config/prisma");
|
|
const mailer = require("../src/config/mailer");
|
|
const { passport, googleEnabled } = require("../src/config/passport");
|
|
|
|
jest.mock("../src/config/mailer", () => ({ sendMail: jest.fn() }));
|
|
|
|
const EMAIL = "google-pwd-test@test.local";
|
|
const PROFILE = {
|
|
id: "sim-google-id-123456",
|
|
displayName: "Chloe Martin",
|
|
name: { givenName: "Chloe", familyName: "Martin" },
|
|
emails: [{ value: EMAIL }],
|
|
photos: [{ value: "https://example.test/avatar.png" }],
|
|
};
|
|
|
|
async function cleanup() {
|
|
await prisma.user.deleteMany({ where: { email: EMAIL } });
|
|
}
|
|
|
|
// Rejoue le callback de la strategie Google sans sortir sur le reseau.
|
|
function verifierProfilGoogle() {
|
|
const strategy = passport._strategy("google");
|
|
return new Promise((resolve, reject) => {
|
|
strategy._verify("access-token", "refresh-token", PROFILE, (err, user) => {
|
|
if (err) reject(err);
|
|
else resolve(user);
|
|
});
|
|
});
|
|
}
|
|
|
|
beforeAll(cleanup);
|
|
afterAll(async () => {
|
|
await cleanup();
|
|
await prisma.$disconnect();
|
|
});
|
|
|
|
const decrire = googleEnabled ? describe : describe.skip;
|
|
|
|
decrire("Inscription Google et definition du mot de passe", () => {
|
|
beforeEach(() => jest.clearAllMocks());
|
|
|
|
test("un compte Google est cree sans mot de passe et recoit un lien", async () => {
|
|
mailer.sendMail.mockResolvedValue(true);
|
|
|
|
const user = await verifierProfilGoogle();
|
|
|
|
expect(user.provider).toBe("GOOGLE");
|
|
expect(user.password).toBeNull();
|
|
expect(user.isEmailVerified).toBe(true);
|
|
|
|
expect(mailer.sendMail).toHaveBeenCalledTimes(1);
|
|
const envoi = mailer.sendMail.mock.calls[0][0];
|
|
expect(envoi.to).toBe(EMAIL);
|
|
expect(envoi.subject).toMatch(/mot de passe/i);
|
|
|
|
// Le lien doit viser le formulaire du front, pas la route POST de l API.
|
|
const frontendUrl = process.env.FRONTEND_URL || "http://localhost:5173";
|
|
expect(envoi.html).toContain(`${frontendUrl}/definir-mot-de-passe/`);
|
|
|
|
const enBase = await prisma.user.findUnique({ where: { email: EMAIL } });
|
|
expect(enBase.resetPasswordToken).toHaveLength(64);
|
|
expect(enBase.resetPasswordExpires.getTime()).toBeGreaterThan(Date.now());
|
|
ctxToken = enBase.resetPasswordToken;
|
|
});
|
|
|
|
test("le profil signale que le compte n a pas de mot de passe", async () => {
|
|
const jwt = require("jsonwebtoken");
|
|
const u = await prisma.user.findUnique({ where: { email: EMAIL } });
|
|
const token = jwt.sign({ id: u.id, role: u.role }, process.env.JWT_SECRET, { expiresIn: "1h" });
|
|
|
|
const res = await request(app).get("/api/auth/me").set("Authorization", "Bearer " + token).expect(200);
|
|
|
|
expect(res.body.user.hasPassword).toBe(false);
|
|
expect(res.body.user.provider).toBe("GOOGLE");
|
|
// Le hash ne doit jamais sortir de l API.
|
|
expect(res.body.user.password).toBeUndefined();
|
|
});
|
|
|
|
test("la connexion par mot de passe est refusee tant qu il n en existe pas", async () => {
|
|
const res = await request(app).post("/api/auth/login").send({ email: EMAIL, password: "PeuImporte123" });
|
|
expect(res.status).toBe(401);
|
|
expect(res.body.message).toMatch(/connexion Google/);
|
|
});
|
|
|
|
test("une confirmation qui ne correspond pas est refusee", async () => {
|
|
const res = await request(app)
|
|
.post("/api/auth/reset-password/" + ctxToken)
|
|
.send({ password: "MonMotDePasse1", confirmPassword: "AutreMotDePasse2" });
|
|
|
|
expect(res.status).toBe(400);
|
|
expect(res.body.message).toMatch(/ne correspondent pas/);
|
|
|
|
const u = await prisma.user.findUnique({ where: { email: EMAIL } });
|
|
expect(u.password).toBeNull();
|
|
});
|
|
|
|
test("un mot de passe trop court est refuse", async () => {
|
|
const res = await request(app)
|
|
.post("/api/auth/reset-password/" + ctxToken)
|
|
.send({ password: "court", confirmPassword: "court" });
|
|
|
|
expect(res.status).toBe(400);
|
|
expect(res.body.message).toMatch(/8 caractères/);
|
|
});
|
|
|
|
test("le mot de passe defini permet ensuite de se connecter et de le changer", async () => {
|
|
const ok = await request(app)
|
|
.post("/api/auth/reset-password/" + ctxToken)
|
|
.send({ password: "MonMotDePasse1", confirmPassword: "MonMotDePasse1" })
|
|
.expect(200);
|
|
expect(ok.body.message).toMatch(/réinitialisé/);
|
|
|
|
const login = await request(app).post("/api/auth/login").send({ email: EMAIL, password: "MonMotDePasse1" }).expect(200);
|
|
expect(login.body.token).toBeDefined();
|
|
|
|
// Le jeton a bien ete consomme.
|
|
const u = await prisma.user.findUnique({ where: { email: EMAIL } });
|
|
expect(u.resetPasswordToken).toBeNull();
|
|
|
|
const me = await request(app).get("/api/auth/me").set("Authorization", "Bearer " + login.body.token).expect(200);
|
|
expect(me.body.user.hasPassword).toBe(true);
|
|
|
|
const change = await request(app)
|
|
.patch("/api/auth/change-password")
|
|
.set("Authorization", "Bearer " + login.body.token)
|
|
.send({ currentPassword: "MonMotDePasse1", newPassword: "EncoreUnAutre2" });
|
|
expect(change.status).toBe(200);
|
|
});
|
|
|
|
test("une seconde connexion Google ne renvoie pas de nouveau lien", async () => {
|
|
mailer.sendMail.mockResolvedValue(true);
|
|
|
|
const user = await verifierProfilGoogle();
|
|
|
|
expect(user.email).toBe(EMAIL);
|
|
expect(mailer.sendMail).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
let ctxToken;
|