batchCooking/apps/api/test/profile.test.ts
Nicolas f00485f341 fix(ci): corrige les specs Cypress cassées par le refactor uid+i18n, applique biome
- onboarding.cy.ts / preferences.cy.ts / recipes.cy.ts mockaient encore
  GET /reference/diets|allergies avec l'ancienne forme {id, name}. Depuis
  les deux derniers commits l'API renvoie {id, key} (uid anglais) et le
  composant résout le libellé via i18n (t(`catalog.diets.${key}`)) — avec
  key manquant, ça affichait littéralement "catalog.diets.undefined" au
  lieu de "Végétarien"/"Omnivore"/etc., faisant échouer cy.select()/
  cy.contains() dans ces 3 specs. Corrigé pour mocker {key: "vegetarian"},
  {key: "peanuts"}, etc.
- recipes.cy.ts : le test "shows a not-found message" utilisait le
  mauvais code d'erreur (4041 au lieu de ErrorCode.RECIPE_NOT_FOUND =
  4045), donc RecipeDetailPanel tombait dans son état d'erreur générique
  au lieu du message "Cette recette n'existe pas." — bug dans mon propre
  test, sans rapport avec le refactor.
- pnpm lint (biome) : les fichiers touchés par le refactor précédent
  avaient quelques soucis de formatage/tri d'imports (des sed multi-
  fichiers, pas d'édition via l'outil habituel) — corrigés par
  `biome check --write`.

Vérifié : ces 3 specs + recipe-form.cy.ts passent maintenant dans le job
CI GitHub Actions (Linux, Cypress s'y exécute réellement — contrairement
à cet environnement Windows sandboxé, voir les commits précédents) ; 102
tests Mocha + 32 scénarios Cucumber toujours au vert en local.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-18 20:49:25 +02:00

199 lines
7.8 KiB
TypeScript

import { ErrorCode, type SignupInput } from "@batch-cooking/shared";
import { faker } from "@faker-js/faker";
import { expect } from "chai";
import request from "supertest";
import { createApp } from "../src/app.js";
import { getEnglishKey } from "../src/db/catalog-en-keys.js";
import { prisma } from "../src/db/prisma.js";
import { resetDatabase } from "../test-support/reset-db.js";
function buildSignupPayload(): SignupInput {
const firstName = faker.person.firstName();
const lastName = faker.person.lastName();
return {
firstName,
lastName,
email: faker.internet.email({ firstName, lastName }).toLowerCase(),
password: faker.internet.password({ length: 16 }),
};
}
describe("Profile", () => {
const app = createApp();
beforeEach(async () => {
await resetDatabase();
});
after(async () => {
await prisma.$disconnect();
});
describe("PATCH /profile/diet", () => {
it("rejects requests without a session cookie with 401 NOT_AUTHENTICATED", async () => {
const res = await request(app).patch("/profile/diet").send({ dietId: 1 });
expect(res.status).to.equal(401);
expect(res.body.code).to.equal(ErrorCode.NOT_AUTHENTICATED);
});
it("sets the profile's regime to a valid, seeded diet", async () => {
const agent = request.agent(app);
await agent.post("/auth/signup").send(buildSignupPayload());
const diet = await prisma.diet.findFirstOrThrow({
where: { key: getEnglishKey("Végétarien") },
});
const res = await agent.patch("/profile/diet").send({ dietId: diet.id });
expect(res.status).to.equal(200);
expect(res.body.dietId).to.equal(diet.id);
});
it("clears the regime when dietId is null", async () => {
const agent = request.agent(app);
await agent.post("/auth/signup").send(buildSignupPayload());
const diet = await prisma.diet.findFirstOrThrow({ where: { key: getEnglishKey("Végan") } });
await agent.patch("/profile/diet").send({ dietId: diet.id });
const res = await agent.patch("/profile/diet").send({ dietId: null });
expect(res.status).to.equal(200);
expect(res.body.dietId).to.equal(null);
});
it("rejects an unknown dietId with 404 DIET_NOT_FOUND", async () => {
const agent = request.agent(app);
await agent.post("/auth/signup").send(buildSignupPayload());
const res = await agent.patch("/profile/diet").send({ dietId: 999_999 });
expect(res.status).to.equal(404);
expect(res.body.code).to.equal(ErrorCode.DIET_NOT_FOUND);
});
});
describe("GET /profile/allergies + PATCH /profile/allergies", () => {
it("rejects requests without a session cookie with 401 NOT_AUTHENTICATED", async () => {
const getRes = await request(app).get("/profile/allergies");
const patchRes = await request(app).patch("/profile/allergies").send({ allergyIds: [] });
expect(getRes.status).to.equal(401);
expect(patchRes.status).to.equal(401);
});
it("starts empty, then reflects a saved selection", async () => {
const agent = request.agent(app);
await agent.post("/auth/signup").send(buildSignupPayload());
const allergies = await prisma.allergy.findMany({ include: { category: true } });
const peanuts = allergies.find((a) => a.category.key === getEnglishKey("Arachides"));
const gluten = allergies.find((a) => a.category.key === getEnglishKey("Gluten"));
if (!peanuts || !gluten) throw new Error("expected seeded allergens missing");
const initial = await agent.get("/profile/allergies");
expect(initial.body).to.deep.equal([]);
const patchRes = await agent
.patch("/profile/allergies")
.send({ allergyIds: [peanuts.id, gluten.id] });
expect(patchRes.status).to.equal(200);
expect(patchRes.body.sort()).to.deep.equal([peanuts.id, gluten.id].sort());
const refetch = await agent.get("/profile/allergies");
expect(refetch.body.sort()).to.deep.equal([peanuts.id, gluten.id].sort());
});
it("replaces (not merges) the previous selection", async () => {
const agent = request.agent(app);
await agent.post("/auth/signup").send(buildSignupPayload());
const allergies = await prisma.allergy.findMany({ include: { category: true } });
const peanuts = allergies.find((a) => a.category.key === getEnglishKey("Arachides"));
const gluten = allergies.find((a) => a.category.key === getEnglishKey("Gluten"));
if (!peanuts || !gluten) throw new Error("expected seeded allergens missing");
await agent.patch("/profile/allergies").send({ allergyIds: [peanuts.id] });
await agent.patch("/profile/allergies").send({ allergyIds: [gluten.id] });
const res = await agent.get("/profile/allergies");
expect(res.body).to.deep.equal([gluten.id]);
});
it("rejects an unknown allergyId with 404 ALLERGY_NOT_FOUND", async () => {
const agent = request.agent(app);
await agent.post("/auth/signup").send(buildSignupPayload());
const res = await agent.patch("/profile/allergies").send({ allergyIds: [999_999] });
expect(res.status).to.equal(404);
expect(res.body.code).to.equal(ErrorCode.ALLERGY_NOT_FOUND);
});
});
describe("GET /profile/disliked-ingredients + PATCH /profile/disliked-ingredients", () => {
it("rejects requests without a session cookie with 401 NOT_AUTHENTICATED", async () => {
const getRes = await request(app).get("/profile/disliked-ingredients");
const patchRes = await request(app)
.patch("/profile/disliked-ingredients")
.send({ dislikedIngredientIds: [] });
expect(getRes.status).to.equal(401);
expect(patchRes.status).to.equal(401);
});
it("starts empty, then reflects a saved selection", async () => {
const agent = request.agent(app);
await agent.post("/auth/signup").send(buildSignupPayload());
const tomate = await prisma.ingredient.findFirstOrThrow({
where: { key: getEnglishKey("Tomate") },
});
const oignon = await prisma.ingredient.findFirstOrThrow({
where: { key: getEnglishKey("Oignon") },
});
const initial = await agent.get("/profile/disliked-ingredients");
expect(initial.body).to.deep.equal([]);
const patchRes = await agent
.patch("/profile/disliked-ingredients")
.send({ dislikedIngredientIds: [tomate.id, oignon.id] });
expect(patchRes.status).to.equal(200);
expect(patchRes.body.sort()).to.deep.equal([tomate.id, oignon.id].sort());
const refetch = await agent.get("/profile/disliked-ingredients");
expect(refetch.body.sort()).to.deep.equal([tomate.id, oignon.id].sort());
});
it("replaces (not merges) the previous selection", async () => {
const agent = request.agent(app);
await agent.post("/auth/signup").send(buildSignupPayload());
const tomate = await prisma.ingredient.findFirstOrThrow({
where: { key: getEnglishKey("Tomate") },
});
const oignon = await prisma.ingredient.findFirstOrThrow({
where: { key: getEnglishKey("Oignon") },
});
await agent
.patch("/profile/disliked-ingredients")
.send({ dislikedIngredientIds: [tomate.id] });
await agent
.patch("/profile/disliked-ingredients")
.send({ dislikedIngredientIds: [oignon.id] });
const res = await agent.get("/profile/disliked-ingredients");
expect(res.body).to.deep.equal([oignon.id]);
});
it("rejects an unknown ingredientId with 404 INGREDIENT_NOT_FOUND", async () => {
const agent = request.agent(app);
await agent.post("/auth/signup").send(buildSignupPayload());
const res = await agent
.patch("/profile/disliked-ingredients")
.send({ dislikedIngredientIds: [999_999] });
expect(res.status).to.equal(404);
expect(res.body.code).to.equal(ErrorCode.INGREDIENT_NOT_FOUND);
});
});
});