315 lines
9.7 KiB
JavaScript
315 lines
9.7 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";
|
|
import { cleanSuggestions } from "../shared/workspace.js";
|
|
test("workspace groups own coffee history and surfaces tasting, inventory and experiment actions", async () => {
|
|
const { app, agent, db } = await setup();
|
|
const { csrf } = await signup(agent, "[email protected]");
|
|
const other = request.agent(app);
|
|
const { csrf: otherCsrf } = await signup(other, "[email protected]");
|
|
await other
|
|
.post("/api/plans")
|
|
.set("x-csrf-token", otherCsrf)
|
|
.send({ plan: { ...blankPlan(), fields: { 0.1: "Private coffee" } } });
|
|
const p = blankPlan();
|
|
p.fields["0.1"] = "Burundi";
|
|
p.experiment = { expected: "More sweetness" };
|
|
const saved = (
|
|
await agent.post("/api/plans").set("x-csrf-token", csrf).send({ plan: p })
|
|
).body.plan;
|
|
const owner = (
|
|
await db.query("SELECT user_id FROM roast_plans WHERE id=$1", [saved.id])
|
|
).rows[0].user_id;
|
|
await db.query(
|
|
"INSERT INTO actual_roasts(user_id,roast_plan_id,filename,original_content,parsed,after,created_at) VALUES($1,$2,'batch.alog','{}',$3,$4,'2020-01-01')",
|
|
[
|
|
owner,
|
|
saved.id,
|
|
{ roast: { title: "Fruit batch" }, derived: { developmentS: 80 } },
|
|
{ favorite: "yes", sweetness: "4" },
|
|
],
|
|
);
|
|
const result = await agent.get("/api/workspace");
|
|
assert.equal(result.status, 200);
|
|
assert.equal(result.body.coffees.length, 1);
|
|
assert.equal(result.body.coffees[0].roasts[0].after.favorite, "yes");
|
|
assert.ok(result.body.actions.some((a) => a.kind === "taste"));
|
|
assert.ok(result.body.actions.some((a) => a.kind === "experiment"));
|
|
assert.equal((await request(app).get("/api/workspace")).status, 401);
|
|
for (const route of ["/home"]) {
|
|
assert.equal((await agent.get(route)).status, 200);
|
|
assert.equal((await request(app).get(route)).status, 302);
|
|
}
|
|
assert.equal((await request(app).get("/workspace.html")).status, 302);
|
|
});
|
|
test("revision history is owned, restores planning inputs and retains current consumption and chat", 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 p = blankPlan();
|
|
p.fields["4.1"] = "light";
|
|
const saved = (
|
|
await agent
|
|
.post("/api/plans")
|
|
.set("x-csrf-token", csrf)
|
|
.send({ plan: p, name: "Original" })
|
|
).body.plan;
|
|
p.fields["4.1"] = "medium";
|
|
p.inventory.consumed = { weightG: 500 };
|
|
p.assistant = { messages: [{ role: "user", content: "Remember this" }] };
|
|
assert.equal(
|
|
(
|
|
await agent
|
|
.put("/api/plans/" + saved.id)
|
|
.set("x-csrf-token", csrf)
|
|
.send({ plan: p, name: "Variation" })
|
|
).status,
|
|
200,
|
|
);
|
|
const path = "/api/plans/" + saved.id;
|
|
const revisions = (await agent.get(path + "/revisions")).body.revisions;
|
|
assert.equal(revisions.length, 1);
|
|
assert.equal((await other.get(path + "/revisions")).status, 404);
|
|
assert.equal((await request(app).get(path + "/revisions")).status, 401);
|
|
assert.equal(
|
|
(await agent.post(path + "/restore").send({ revisionId: revisions[0].id }))
|
|
.status,
|
|
403,
|
|
);
|
|
assert.equal(
|
|
(
|
|
await other
|
|
.post(path + "/restore")
|
|
.set("x-csrf-token", otherCsrf)
|
|
.send({ revisionId: revisions[0].id })
|
|
).status,
|
|
404,
|
|
);
|
|
const restored = await agent
|
|
.post(path + "/restore")
|
|
.set("x-csrf-token", csrf)
|
|
.send({ revisionId: revisions[0].id });
|
|
assert.equal(restored.status, 200);
|
|
assert.equal(restored.body.plan.plan.fields["4.1"], "light");
|
|
assert.equal(restored.body.plan.plan.inventory.consumed.weightG, 500);
|
|
assert.equal(
|
|
restored.body.plan.plan.assistant.messages[0].content,
|
|
"Remember this",
|
|
);
|
|
assert.equal((await agent.get(path + "/revisions")).body.revisions.length, 2);
|
|
});
|
|
test("Bean Bro uses only matching account evidence and filters proposed changes", async () => {
|
|
let context;
|
|
const { app, agent, db } = await setup(
|
|
{},
|
|
{
|
|
runPlanChat: async (args) => {
|
|
context = args;
|
|
return {
|
|
reply: "Try this",
|
|
suggestions: [
|
|
{
|
|
fields: { 4.4: "1:25", 0.1: "Hijacked" },
|
|
evidenceRoastIds: ["foreign-id"],
|
|
},
|
|
],
|
|
model: "test:coach",
|
|
};
|
|
},
|
|
},
|
|
);
|
|
const { csrf } = await signup(agent, "[email protected]");
|
|
const other = request.agent(app);
|
|
const { csrf: otherCsrf } = await signup(other, "[email protected]");
|
|
const p = blankPlan();
|
|
p.fields["0.1"] = "Burundi";
|
|
await other
|
|
.post("/api/plans")
|
|
.set("x-csrf-token", otherCsrf)
|
|
.send({ plan: p });
|
|
const saved = (
|
|
await agent.post("/api/plans").set("x-csrf-token", csrf).send({ plan: p })
|
|
).body.plan;
|
|
const owner = (
|
|
await db.query("SELECT user_id FROM roast_plans WHERE id=$1", [saved.id])
|
|
).rows[0].user_id;
|
|
await db.query(
|
|
"INSERT INTO actual_roasts(user_id,roast_plan_id,filename,original_content,parsed,after) VALUES($1,$2,'owned.alog','{}',$3,$4)",
|
|
[
|
|
owner,
|
|
saved.id,
|
|
{ roast: { title: "Owned batch" } },
|
|
{ cupNotes: "Juicy", sweetness: "4" },
|
|
],
|
|
);
|
|
const result = await agent
|
|
.post("/api/plan-chat")
|
|
.set("x-csrf-token", csrf)
|
|
.send({
|
|
plan: p,
|
|
messages: [{ role: "user", content: "Suggest an experiment" }],
|
|
});
|
|
assert.equal(result.status, 200);
|
|
assert.equal(context.evidence.roasts.length, 1);
|
|
assert.equal(context.evidence.roasts[0].after.cupNotes, "Juicy");
|
|
assert.deepEqual(result.body.suggestions[0].fields, { 4.4: "1:25" });
|
|
assert.deepEqual(result.body.suggestions[0].evidenceRoastIds, []);
|
|
assert.deepEqual(
|
|
cleanSuggestions([
|
|
{ fields: { 4.4: "nonsense", 4.1: "invalid", 4.2: "999" } },
|
|
]),
|
|
[],
|
|
);
|
|
});
|
|
test("Bean Bro connection test is admin-only and uses the selected model", async () => {
|
|
let called = false;
|
|
const { app, agent } = await setup(
|
|
{},
|
|
{
|
|
runPlanChat: async () => {
|
|
called = true;
|
|
return { reply: "Connected", model: "test:coach" };
|
|
},
|
|
},
|
|
);
|
|
const { csrf } = await signup(agent, "[email protected]");
|
|
assert.equal(
|
|
(await agent.post("/api/admin/llm/test").set("x-csrf-token", csrf)).status,
|
|
403,
|
|
);
|
|
assert.equal(called, false);
|
|
const admin = request.agent(app);
|
|
const result = await admin.post("/api/auth/bootstrap").send({
|
|
setupToken: "a-secure-bootstrap-token",
|
|
email: "[email protected]",
|
|
password: "a very secure admin password",
|
|
});
|
|
const token = result.body.csrfToken;
|
|
assert.equal((await admin.post("/api/admin/llm/test")).status, 403);
|
|
const checked = await admin
|
|
.post("/api/admin/llm/test")
|
|
.set("x-csrf-token", token);
|
|
assert.equal(checked.status, 200);
|
|
assert.equal(checked.body.model, "test:coach");
|
|
});
|
|
test("manual roast completion validates milestones, stays private and is idempotent", async () => {
|
|
const { app, agent, db } = await setup();
|
|
const { csrf } = await signup(agent, "[email protected]");
|
|
const other = request.agent(app);
|
|
const { csrf: otherCsrf } = await signup(other, "[email protected]");
|
|
const p = blankPlan();
|
|
p.roastDay = {
|
|
startedAt: Date.now() - 600000,
|
|
events: {
|
|
charge: { timeS: 0, tempF: "345" },
|
|
fc: { timeS: 500, tempF: "360" },
|
|
drop: { timeS: 580, tempF: "375" },
|
|
},
|
|
};
|
|
const saved = (
|
|
await agent.post("/api/plans").set("x-csrf-token", csrf).send({ plan: p })
|
|
).body.plan;
|
|
const route = "/api/roasts/manual";
|
|
assert.equal(
|
|
(await request(app).post(route).send({ planId: saved.id })).status,
|
|
401,
|
|
);
|
|
assert.equal(
|
|
(await agent.post(route).send({ planId: saved.id })).status,
|
|
403,
|
|
);
|
|
assert.equal(
|
|
(
|
|
await other
|
|
.post(route)
|
|
.set("x-csrf-token", otherCsrf)
|
|
.send({ planId: saved.id })
|
|
).status,
|
|
404,
|
|
);
|
|
const result = await agent
|
|
.post(route)
|
|
.set("x-csrf-token", csrf)
|
|
.send({ planId: saved.id });
|
|
assert.equal(result.status, 201);
|
|
assert.equal(
|
|
(
|
|
await agent
|
|
.post(route)
|
|
.set("x-csrf-token", csrf)
|
|
.send({ planId: saved.id })
|
|
).body.roast.id,
|
|
result.body.roast.id,
|
|
);
|
|
const detail = (await agent.get("/api/roasts/" + result.body.roast.id)).body
|
|
.roast;
|
|
assert.equal(detail.roast.manual, true);
|
|
assert.equal(detail.parsed.derived.developmentS, 80);
|
|
assert.equal(
|
|
(await other.get("/api/roasts/" + result.body.roast.id)).status,
|
|
404,
|
|
);
|
|
const download = await agent.get(
|
|
"/api/roasts/" + result.body.roast.id + "/download",
|
|
);
|
|
assert.equal(download.body.format, "roast-planner-manual-record");
|
|
assert.equal(
|
|
(
|
|
await agent.get(
|
|
"/api/roasts/" + result.body.roast.id + "/download?variant=updated",
|
|
)
|
|
).status,
|
|
400,
|
|
);
|
|
p.roastDay.events.drop.timeS = 400;
|
|
await agent
|
|
.put("/api/plans/" + saved.id)
|
|
.set("x-csrf-token", csrf)
|
|
.send({ plan: p });
|
|
assert.equal(
|
|
(
|
|
await agent
|
|
.post(route)
|
|
.set("x-csrf-token", csrf)
|
|
.send({ planId: saved.id })
|
|
).status,
|
|
400,
|
|
);
|
|
});
|
|
test("manual roast targets remain captured when its source plan changes", async () => {
|
|
const { agent } = await setup();
|
|
const { csrf } = await signup(agent, "[email protected]");
|
|
const p = blankPlan();
|
|
p.fields["4.1"] = "light";
|
|
p.experiment = { expected: "More fruit" };
|
|
p.roastDay = {
|
|
startedAt: Date.now() - 600000,
|
|
events: {
|
|
fc: { timeS: 480, tempF: "360" },
|
|
drop: { timeS: 560, tempF: "375" },
|
|
},
|
|
};
|
|
const saved = (
|
|
await agent.post("/api/plans").set("x-csrf-token", csrf).send({ plan: p })
|
|
).body.plan;
|
|
const r = (
|
|
await agent
|
|
.post("/api/roasts/manual")
|
|
.set("x-csrf-token", csrf)
|
|
.send({ planId: saved.id })
|
|
).body.roast;
|
|
p.fields["4.1"] = "dark";
|
|
p.experiment.expected = "More body";
|
|
await agent
|
|
.put("/api/plans/" + saved.id)
|
|
.set("x-csrf-token", csrf)
|
|
.send({ plan: p });
|
|
const detail = (await agent.get("/api/roasts/" + r.id)).body.roast;
|
|
assert.equal(detail.hasPlanSnapshot, true);
|
|
assert.equal(detail.plan.fields["4.1"], "light");
|
|
assert.equal(detail.plan.experiment.expected, "More fruit");
|
|
});
|