Files
roast_command_center/test/plans-table.test.js
T
snowspeeder a9358048ef
Test and deploy / test-and-deploy (push) Successful in 1m52s
Add independent roast plan cloning from the plans table
2026-09-06 16:58:41 -04:00

101 lines
4.9 KiB
JavaScript

import test from "node:test";
import assert from "node:assert/strict";
import request from "supertest";
import { setup, signup } from "./helpers.js";
import { blankPlan } from "../shared/fields.js";
test("plans table page and single-plan route require authentication and enforce ownership", async () => {
const { app, agent } = await setup();
assert.equal((await agent.get("/plans")).status, 302);
const { csrf } = await signup(agent, "[email protected]");
const other = request.agent(app);
await signup(other, "[email protected]");
const made = await agent
.post("/api/plans")
.set("x-csrf-token", csrf)
.send({ name: "Owned plan", plan: blankPlan() });
const id = made.body.plan.id;
assert.equal((await agent.get("/plans")).status, 200);
assert.equal((await agent.get("/plans.html")).status, 302);
assert.equal(
(await agent.get("/api/plans/" + id)).body.plan.name,
"Owned plan",
);
assert.equal((await other.get("/api/plans/" + id)).status, 404);
assert.equal((await request(app).get("/api/plans/" + id)).status, 401);
assert.equal((await agent.get("/api/plans/not-a-uuid")).status, 404);
});
test("summary search, filters and pagination exclude full telemetry and other accounts", async () => {
const { app, agent, db } = await setup();
await signup(agent, "[email protected]");
const other = request.agent(app);
const { csrf } = await signup(other, "[email protected]");
await other
.post("/api/plans")
.set("x-csrf-token", csrf)
.send({ name: "Hidden Ethiopia", plan: blankPlan() });
const owner = (
await db.query("SELECT id FROM users WHERE email=$1", ["[email protected]"])
).rows[0].id;
for (let i = 0; i < 62; i++) {
const p = blankPlan();
Object.assign(p.fields, {
0.1: "Coffee " + i,
1.3: i % 2 ? "Ethiopia" : "Burundi",
3.1: i % 2 ? "natural" : "washed",
4.1: i % 2 ? "light" : "medium",
});
p.reference = { curve: Array(30).fill({ t: 1, bt: 180 }) };
await db.query(
"INSERT INTO roast_plans(user_id,plan,name) VALUES($1,$2,$3)",
[owner, p, "Plan " + String(i).padStart(2, "0")],
);
}
const first = (await agent.get("/api/plans?view=summary")).body;
assert.equal(
(await agent.get("/api/plans?view=summary&sort=constructor")).status,
200,
);
assert.equal(
(await agent.get("/api/plans?view=summary&page=9007199254740991")).status,
400,
);
assert.equal(first.total, 62);
assert.equal(first.plans.length, 25);
assert.ok(!("plan" in first.plans[0]));
const second = (await agent.get("/api/plans?view=summary&page=2")).body;
assert.equal(second.plans.length, 25);
assert.equal(
first.plans.some((a) => second.plans.some((b) => a.id === b.id)),
false,
);
const filtered = (
await agent.get(
"/api/plans?view=summary&q=ethiopia&process=natural&level=light",
)
).body;
assert.equal(filtered.total, 31);
assert.ok(filtered.plans.every((p) => p.origin === "Ethiopia"));
assert.equal(
(await agent.get("/api/plans?view=summary&level=dark")).body.total,
0,
);
assert.equal((await agent.get("/api/plans?view=summary&page=0")).status, 400);
assert.equal(
(await agent.get("/api/plans?view=summary&limit=999")).status,
400,
);
assert.equal(
(await agent.get("/api/plans?view=summary&q=%25")).body.total,
0,
);
});
test('clone creates an independent owned plan without copying roast outcomes or consumption', async () => {
const {app,agent}=await setup();const {csrf}=await signup(agent,'[email protected]');const other=request.agent(app);const {csrf:otherCsrf}=await signup(other,'[email protected]');
const plan=blankPlan();plan.fields['0.1']='Burundi';plan.fields['4.1']='light';plan.temps.fc.tempC='190';plan.inventory={lotId:'lot',lotLabel:'Burundi lot',consumed:{weightG:500}};plan.afterRoast.cupNotes='Original cup';plan.planActual.fc.actualTime='8:20';
const original=(await agent.post('/api/plans').set('x-csrf-token',csrf).send({plan,name:'Fruity'})).body.plan;
const route='/api/plans/'+original.id+'/clone';assert.equal((await request(app).post(route)).status,401);assert.equal((await agent.post(route)).status,403);assert.equal((await other.post(route).set('x-csrf-token',otherCsrf)).status,404);assert.equal((await agent.post('/api/plans/not-a-uuid/clone').set('x-csrf-token',csrf)).status,404);
const response=await agent.post(route).set('x-csrf-token',csrf);assert.equal(response.status,201);const copy=response.body.plan;assert.notEqual(copy.id,original.id);assert.equal(copy.name,'Fruity (copy)');assert.deepEqual(copy.plan.fields,plan.fields);assert.deepEqual(copy.plan.temps,plan.temps);assert.equal(copy.plan.inventory.lotId,'lot');assert.equal(copy.plan.inventory.consumed,null);assert.deepEqual(copy.plan.afterRoast,blankPlan().afterRoast);assert.deepEqual(copy.plan.planActual,blankPlan().planActual);
copy.plan.fields['4.1']='medium';assert.equal((await agent.put('/api/plans/'+copy.id).set('x-csrf-token',csrf).send({plan:copy.plan})).status,200);assert.deepEqual((await agent.get('/api/plans/'+original.id)).body.plan.plan,plan);assert.equal((await other.get('/api/plans/'+copy.id)).status,404);
});