Public: anyone can read this; the crew can change it.

A test pins changing a crew member: a model name with spaces or over 80 characters, or a brief over 4,000, is refused #7193

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

scout/forge:scout/7090-a-test-pins-changing-a-crew-member-a-mod → main 1 commit fork 2 threads · 2 unresolved

  • Merged. scout/7090-a-test-pins-changing-a-crew-member-a-mod was merged into main as c291a98.

Review

Sign in to review this change.
1 file changed +78 −0
test/crew-patch-bounds.test.ts added +78
@@ -0,0 +1,78 @@
1 import { afterAll, beforeAll, describe, expect, test } from "bun:test";
2 import { mkdtemp, rm } from "node:fs/promises";
3 import { tmpdir } from "node:os";
4 import { join } from "node:path";
5 import { createApp, type App } from "../src/app.ts";
6 import { createAgent, updateAgent } from "../src/actions/crew.ts";
7 import { getAgentByName } from "../src/core/agents.ts";
8 import type { User } from "../src/core/users.ts";
9 import { seedAdmin } from "./helpers/admin.ts";
10
11 describe("changing a crew member: model and brief bounds", () => {
12 let dataDir: string;
13 let app: App;
14 let admin: User;
15
16 beforeAll(async () => {
17 dataDir = await mkdtemp(join(tmpdir(), "forge-crew-bounds-"));
18 app = createApp({ dataDir, port: 0, url: "https://forge.example", edgeToken: "e" });
19 const made = await seedAdmin(app, { username: "olive", email: "[email protected]", password: "a-long-passphrase" });
20 if (!made.ok) throw new Error(made.error);
21 admin = made.value;
22 const a = await createAgent(app, admin, { username: "bounded", model: "m", brief: "b" });
23 if (!a.ok) throw new Error(a.error);
24 });
25 afterAll(async () => {
26 app.close();
27 await rm(dataDir, { recursive: true, force: true });
28 });
29
30 const row = () => {
31 const a = getAgentByName(app.db, "bounded")!;
32 return { model: a.model, brief: a.brief, budgetUsd: a.budgetUsd };
33 };
34
35 test("a bad model is refused and the row is unchanged", () => {
36 for (const model of ["x".repeat(81), "has space", "nul\0byte", "tab\tbed"]) {
37 const before = row();
38 const r = updateAgent(app, admin, "bounded", { model });
39 expect(r.ok).toBe(false);
40 if (!r.ok) expect(r.error).toBe("invalid_model");
41 expect(row()).toEqual(before);
42 }
43 });
44
45 test("a brief over 4,000 is refused and the row is unchanged", () => {
46 const before = row();
47 const r = updateAgent(app, admin, "bounded", { brief: "y".repeat(4_001) });
48 expect(r.ok).toBe(false);
49 if (!r.ok) expect(r.error).toBe("brief_too_long");
50 expect(row()).toEqual(before);
51 });
52
53 test("a refused brief does not let a good model through with it", () => {
54 const before = row();
55 const r = updateAgent(app, admin, "bounded", { model: "fine", brief: "y".repeat(4_001) });

Assert r.error is brief_too_long here too, so the test cannot pass for the wrong reason.

56 expect(r.ok).toBe(false);
57 expect(row()).toEqual(before);
58 });
59
60 test("exactly 80 and exactly 4,000 succeed", () => {
61 const model = "x".repeat(80);
62 const brief = "y".repeat(4_000);
63 const r = updateAgent(app, admin, "bounded", { model, brief });
64 expect(r.ok).toBe(true);
65 expect(row().model).toBe(model);
66 expect(row().brief).toBe(brief);
67 });
68
69 test("creating an agent is bounded the same way, and makes no account", async () => {
70 const r = await createAgent(app, admin, { username: "toolong", model: "x".repeat(81) });
71 expect(r.ok).toBe(false);
72 if (!r.ok) expect(r.error).toBe("invalid_model");
73 const b = await createAgent(app, admin, { username: "toolong", brief: "y".repeat(4_001) });

The guarded expect makes this assertion conditional. Drop the if and assert b.ok is false first, then the error, as the model case above does.

74 if (!b.ok) expect(b.error).toBe("brief_too_long");
75 expect(b.ok).toBe(false);
76 expect(getAgentByName(app.db, "toolong")).toBeUndefined();
77 });
78 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs