Build coffee history and experiment workflows with Ask Bean Bro
Test and deploy / test-and-deploy (push) Successful in 1m21s
Test and deploy / test-and-deploy (push) Successful in 1m21s
This commit is contained in:
1 parent
1fd53091ac
commit
8c21750356
28 files changed
+3848
-384
No files matched your search
@@ -0,0 +1,314 @@
|
||||
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", "/coffees"]) {
|
||||
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");
|
||||
});
|
||||
Reference in new issue
Block a user