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

A test pins waking a resting Claude account from Settings → Subscriptions, a forge admin's only #7043

Merged opened this

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

Merged

scout/forge:scout/7024-a-test-pins-waking-a-resting-claude-acco → main 1 commit fork 1 thread · 1 unresolved

  • Merged. scout/7024-a-test-pins-waking-a-resting-claude-acco was merged into main as f26ebb6.

Review

Sign in to review this change.
1 file changed +115 −0
test/settings-subscription-wake.test.ts added +115
@@ -0,0 +1,115 @@
1 /**
2 * Settings → Subscriptions → "wake": POST /settings/subscriptions/:id/wake
3 * puts a resting Claude account back in use. A forge admin's alone, same-origin.
4 */
5
6 import { afterAll, beforeAll, describe, expect, test } from "bun:test";
7 import { mkdtemp, rm } from "node:fs/promises";
8 import { tmpdir } from "node:os";
9 import { join } from "node:path";
10 import { createApp, type App } from "../src/app.ts";
11 import {
12 addSubscription,
13 getSubscription,
14 restSubscription,
15 } from "../src/core/subscriptions.ts";
16 import { createUser } from "../src/core/users.ts";
17 import { serve, type Servers } from "../src/http/server.ts";
18 import { adminCookie } from "./helpers/admin.ts";
19 import { quietLogs } from "./helpers/globals.ts";
20
21 quietLogs();
22
23 const KEY = "sk-ant-api03-wake-key-for-the-test-aaaaaaaa";
24
25 describe("waking a resting account from Settings", () => {
26 let dataDir = "";
27 let app: App;
28 let servers: Servers;
29 let admin = "";
30 let member = "";
31 let id = "";
32
33 const rest = () =>
34 restSubscription(app.db, id, new Date(Date.now() + 3_600_000).toISOString(), "weekly limit");
35 const resting = () => getSubscription(app.db, id)?.restingUntil ?? null;
36 const wake = (target: string, headers: Record<string, string>) =>
37 fetch(`${servers.url}/settings/subscriptions/${target}/wake`, {
38 method: "POST",
39 headers,
40 body: new URLSearchParams({}),
41 redirect: "manual",
42 });
43 const pool = async (res: Response): Promise<string> => {
44 const flash = (res.headers.get("set-cookie") ?? "").split(";")[0]!;
45 return (
46 await fetch(`${servers.url}/settings/subscriptions`, {
47 headers: { cookie: `${admin}; ${flash}` },
48 })
49 ).text();
50 };
51
52 beforeAll(async () => {
53 dataDir = await mkdtemp(join(tmpdir(), "forge-wake-"));
54 app = createApp({ dataDir, port: 0 });
55 servers = await serve(app);
56 admin = await adminCookie(app, { username: "alice", email: "[email protected]", password: "a-long-wake-passphrase" });
57 const bob = await createUser(app.db, {
58 origin: "seed",
59 trusted: true,
60 username: "bob",
61 email: "[email protected]",
62 password: "a-long-wake-passphrase",
63 });
64 if (!bob.ok) throw new Error("bob");
65 const res = await fetch(`${servers.url}/signin`, {
66 method: "POST",
67 body: new URLSearchParams({ username: "bob", password: "a-long-wake-passphrase" }),
68 redirect: "manual",
69 });
70 member = (res.headers.get("set-cookie") ?? "").split(";")[0]!;
71 const made = addSubscription(app.db, dataDir, { name: "starter", token: KEY });
72 if (!made.ok) throw new Error("sub");
73 id = made.value.id;
74 }, 30_000);
75
76 afterAll(async () => {
77 servers.stop();
78 app.close();
79 await rm(dataDir, { recursive: true, force: true });
80 });
81
82 test("an admin wakes a resting account: the flash says so and the pool shows it in use", async () => {
83 rest();
84 expect(resting()).not.toBeNull();
85 const before = await (await fetch(`${servers.url}/settings/subscriptions`, { headers: { cookie: admin } })).text();
86 expect(before).toContain("resting until");
87 const res = await wake(id, { cookie: admin, origin: servers.url });
88 expect(res.status).toBe(303);
89 expect(resting()).toBeNull();
90 const after = await pool(res);
91 expect(after).toContain("In use again.");
92 expect(after).not.toContain("resting until");
93 });
94
95 test("an unknown id changes nothing and does not crash", async () => {
96 rest();
97 const res = await wake("no-such-account", { cookie: admin, origin: servers.url });
98 expect(res.status).toBe(303);
99 expect(resting()).not.toBeNull();

Weak assertion: "not contain /settings/subscriptions" passes for any redirect, including an error page. Assert the sign-in path, and the status.

100 expect(getSubscription(app.db, "no-such-account")).toBeUndefined();
101 });
102
103 test("a non-admin, a signed-out visitor and a cross-origin post are refused; the account stays resting", async () => {
104 rest();
105 const nonAdmin = await wake(id, { cookie: member, origin: servers.url });
106 expect(nonAdmin.status).toBeGreaterThanOrEqual(400);
107 expect(resting()).not.toBeNull();
108 const anon = await wake(id, { origin: servers.url });
109 expect(anon.headers.get("location") ?? "").not.toContain("/settings/subscriptions");
110 expect(resting()).not.toBeNull();
111 const cross = await wake(id, { cookie: admin, origin: "https://evil.example" });
112 expect(cross.status).toBeGreaterThanOrEqual(400);
113 expect(resting()).not.toBeNull();
114 });
115 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs