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 { 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("Preferences", () => { const app = createApp(); beforeEach(async () => { await resetDatabase(); }); after(async () => { await prisma.$disconnect(); }); describe("GET /preferences", () => { it("rejects requests without a session cookie with 401 NOT_AUTHENTICATED", async () => { const res = await request(app).get("/preferences"); expect(res.status).to.equal(401); expect(res.body.code).to.equal(ErrorCode.NOT_AUTHENTICATED); }); it("defaults to SYSTEM when the profile has no preferences row yet", async () => { const agent = request.agent(app); await agent.post("/auth/signup").send(buildSignupPayload()); const res = await agent.get("/preferences"); expect(res.status).to.equal(200); expect(res.body).to.deep.equal({ theme: "SYSTEM" }); }); }); describe("PATCH /preferences", () => { it("rejects requests without a session cookie with 401 NOT_AUTHENTICATED", async () => { const res = await request(app).patch("/preferences").send({ theme: "DARK" }); expect(res.status).to.equal(401); expect(res.body.code).to.equal(ErrorCode.NOT_AUTHENTICATED); }); it("rejects an unknown theme with 400 VALIDATION_ERROR", async () => { const agent = request.agent(app); await agent.post("/auth/signup").send(buildSignupPayload()); const res = await agent.patch("/preferences").send({ theme: "PURPLE" }); expect(res.status).to.equal(400); expect(res.body.code).to.equal(ErrorCode.VALIDATION_ERROR); }); it("creates the preferences row on first write, and reuses it on later reads/writes", async () => { const agent = request.agent(app); await agent.post("/auth/signup").send(buildSignupPayload()); const patchRes = await agent.patch("/preferences").send({ theme: "DARK" }); expect(patchRes.status).to.equal(200); expect(patchRes.body).to.deep.equal({ theme: "DARK" }); const getRes = await agent.get("/preferences"); expect(getRes.body).to.deep.equal({ theme: "DARK" }); const secondPatchRes = await agent.patch("/preferences").send({ theme: "LIGHT" }); expect(secondPatchRes.body).to.deep.equal({ theme: "LIGHT" }); const secondGetRes = await agent.get("/preferences"); expect(secondGetRes.body).to.deep.equal({ theme: "LIGHT" }); }); it("scopes preferences to the caller's own profile", async () => { const aliceAgent = request.agent(app); await aliceAgent.post("/auth/signup").send(buildSignupPayload()); await aliceAgent.patch("/preferences").send({ theme: "DARK" }); const bobAgent = request.agent(app); await bobAgent.post("/auth/signup").send(buildSignupPayload()); const res = await bobAgent.get("/preferences"); expect(res.body).to.deep.equal({ theme: "SYSTEM" }); }); }); });