import test from "node:test"; import assert from "node:assert/strict"; import request from "supertest"; import { setup, signup, password } from "./helpers.js"; async function bootstrapAdmin(agent) { const response = await agent.post("/api/auth/bootstrap").send({ email: "snowspeeder@gmail.com", password, setupToken: "a-secure-bootstrap-token", }); assert.equal(response.status, 201); return response.body.csrfToken; } test("plans: custom name is created, listed, and defaults to blank", async () => { const { agent } = await setup(); const { csrf } = await signup(agent, "planner@example.com"); const unnamed = await agent .post("/api/plans") .set("x-csrf-token", csrf) .send({ plan: { fields: { 0.1: "House blend" } } }); assert.equal(unnamed.status, 201); assert.equal(unnamed.body.plan.name, ""); const named = await agent .post("/api/plans") .set("x-csrf-token", csrf) .send({ plan: { fields: { 0.1: "House blend" } }, name: " Winter batch " }); assert.equal(named.status, 201); assert.equal(named.body.plan.name, "Winter batch", "name is trimmed"); const list = (await agent.get("/api/plans")).body.plans; assert.equal(list.find((p) => p.id === named.body.plan.id).name, "Winter batch"); assert.equal(list.find((p) => p.id === unnamed.body.plan.id).name, ""); }); test("plans: PUT name-only preserves the plan JSONB, and PUT plan-only preserves the name", async () => { const { agent } = await setup(); const { csrf } = await signup(agent, "planner2@example.com"); const created = await agent .post("/api/plans") .set("x-csrf-token", csrf) .send({ plan: { fields: { 0.1: "Original title", "0.4": "300" } }, name: "First name" }); const planId = created.body.plan.id; // Name-only update: the plan JSONB is untouched. const renamed = await agent .put(`/api/plans/${planId}`) .set("x-csrf-token", csrf) .send({ name: "Second name" }); assert.equal(renamed.status, 200); assert.equal(renamed.body.plan.name, "Second name"); assert.equal(renamed.body.plan.plan.fields["0.1"], "Original title"); assert.equal(renamed.body.plan.plan.fields["0.4"], "300"); // Plan-only update: the name is untouched. const replanned = await agent .put(`/api/plans/${planId}`) .set("x-csrf-token", csrf) .send({ plan: { fields: { 0.1: "New title", "0.4": "350" } } }); assert.equal(replanned.status, 200); assert.equal(replanned.body.plan.name, "Second name"); assert.equal(replanned.body.plan.plan.fields["0.1"], "New title"); // Both at once. const both = await agent .put(`/api/plans/${planId}`) .set("x-csrf-token", csrf) .send({ plan: { fields: { 0.1: "Third title" } }, name: "Third name" }); assert.equal(both.body.plan.name, "Third name"); assert.equal(both.body.plan.plan.fields["0.1"], "Third title"); // Neither field present: bad request, same as the pre-existing plan-required behavior. assert.equal( (await agent.put(`/api/plans/${planId}`).set("x-csrf-token", csrf).send({})).status, 400, ); }); test("plans: ownership — a stranger cannot rename another user's plan", async () => { const { app } = await setup(); const owner = request.agent(app); const stranger = request.agent(app); const { csrf: ownerCsrf } = await signup(owner, "owner@example.com"); const { csrf: strangerCsrf } = await signup(stranger, "stranger@example.com"); const created = await owner .post("/api/plans") .set("x-csrf-token", ownerCsrf) .send({ plan: { fields: { 0.1: "Owner's plan" } } }); const planId = created.body.plan.id; const hijack = await stranger .put(`/api/plans/${planId}`) .set("x-csrf-token", strangerCsrf) .send({ name: "Hijacked" }); assert.equal(hijack.status, 404); // The plan is unaffected. const stillOwners = (await owner.get("/api/plans")).body.plans; assert.equal(stillOwners.find((p) => p.id === planId).name, ""); }); test("plans: admin plan list title falls back to worksheet field 0.1 when no custom name is set", async () => { const { app } = await setup(); const admin = request.agent(app); await bootstrapAdmin(admin); const user = request.agent(app); const { csrf: userCsrf } = await signup(user, "regular@example.com"); const named = await user .post("/api/plans") .set("x-csrf-token", userCsrf) .send({ plan: { fields: { 0.1: "Worksheet title" } }, name: "Custom title" }); const unnamed = await user .post("/api/plans") .set("x-csrf-token", userCsrf) .send({ plan: { fields: { 0.1: "Worksheet title only" } } }); const list = (await admin.get("/api/admin/plans")).body.plans; assert.equal(list.find((p) => p.id === named.body.plan.id).title, "Custom title"); assert.equal( list.find((p) => p.id === unnamed.body.plan.id).title, "Worksheet title only", ); });