// Identifiants factices posés avant le chargement de passport : la stratégie // Facebook est ainsi enregistrée même sans vraie app Meta configurée. process.env.FACEBOOK_APP_ID = "test-app-id"; process.env.FACEBOOK_APP_SECRET = "test-app-secret"; const request = require("supertest"); const app = require("../src/app"); const prisma = require("../src/config/prisma"); const mailer = require("../src/config/mailer"); const { passport } = require("../src/config/passport"); jest.mock("../src/config/mailer", () => ({ sendMail: jest.fn() })); const EMAIL = "facebook-login-test@test.local"; const EMAIL_EXISTANT = "facebook-link-test@test.local"; function profil(overrides = {}) { return { id: "sim-facebook-id-987654", displayName: "Léa Durand", name: { givenName: "Léa", familyName: "Durand" }, emails: [{ value: EMAIL }], photos: [{ value: "https://example.test/fb.png" }], ...overrides, }; } // Rejoue le callback de la strategie Facebook sans sortir sur le reseau. function verifierProfilFacebook(profile) { const strategy = passport._strategy("facebook"); return new Promise((resolve, reject) => { strategy._verify("access-token", "refresh-token", profile, (err, user, info) => { if (err) reject(err); else resolve({ user, info }); }); }); } async function cleanup() { await prisma.user.deleteMany({ where: { email: { in: [EMAIL, EMAIL_EXISTANT] } } }); } beforeAll(cleanup); afterAll(async () => { await cleanup(); await prisma.$disconnect(); }); describe("Connexion Facebook", () => { beforeEach(() => jest.clearAllMocks()); test("la route de depart redirige vers Facebook", async () => { const res = await request(app).get("/api/auth/facebook"); expect(res.status).toBe(302); const url = new URL(res.headers.location); expect(url.hostname).toBe("www.facebook.com"); expect(url.searchParams.get("client_id")).toBe("test-app-id"); expect(url.searchParams.get("scope")).toBe("email"); expect(url.searchParams.get("redirect_uri")).toMatch(/\/api\/auth\/facebook\/callback$/); }); test("un premier passage cree un compte FACEBOOK sans mot de passe", async () => { mailer.sendMail.mockResolvedValue(true); const { user } = await verifierProfilFacebook(profil()); expect(user.provider).toBe("FACEBOOK"); expect(user.providerId).toBe("sim-facebook-id-987654"); expect(user.password).toBeNull(); expect(user.isEmailVerified).toBe(true); expect(user.firstname).toBe("Léa"); expect(mailer.sendMail).toHaveBeenCalledTimes(1); expect(mailer.sendMail.mock.calls[0][0].html).toContain("créé avec Facebook"); }); test("un second passage reconnait le compte sans renvoyer d email", async () => { const { user } = await verifierProfilFacebook(profil()); expect(user.email).toBe(EMAIL); expect(mailer.sendMail).not.toHaveBeenCalled(); }); test("un compte existant avec le meme email est rattache a Facebook", async () => { await prisma.user.create({ data: { firstname: "Paul", lastname: "Martin", email: EMAIL_EXISTANT, password: "hash", isEmailVerified: false }, }); const { user } = await verifierProfilFacebook( profil({ id: "sim-facebook-id-link", emails: [{ value: EMAIL_EXISTANT }] }) ); expect(user.provider).toBe("FACEBOOK"); expect(user.isEmailVerified).toBe(true); // Le mot de passe existant est conserve. expect(user.password).toBe("hash"); }); test("un compte Facebook sans email est refuse", async () => { const { user, info } = await verifierProfilFacebook(profil({ id: "sim-sans-email", emails: undefined })); expect(user).toBe(false); expect(info.message).toMatch(/Facebook/); }); test("la connexion par mot de passe oriente vers le bouton Facebook", 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 Facebook/); }); });