test(ai): settings page never exposes the API key

Three tests cover the storage and access boundary:

- A client manager cannot list the settings collection at all (rules are
  superuser-only — verified by the rejection, not by silent skip).
- The settings page HTML returned to the owner does not contain the key
  value, even when the key is freshly set to a unique sentinel.
- A client manager gets 404 on /admin/settings (admin layout gates on
  role, not on session presence).

helpers.ts gained ownerEmail / ownerPassword — needed by the settings test
and reusable for any owner-only integration test going forward.
This commit is contained in:
Dave 2026-09-16 15:59:49 +02:00
parent 70cb355762
commit 2401dbd68f
2 changed files with 50 additions and 0 deletions

View file

@ -0,0 +1,48 @@
import { describe, it, expect, beforeAll } from "vitest";
import PocketBase from "pocketbase";
import { APP_URL, DEMO, adminPb, requireServices, signIn, userPb } from "./helpers";
let admin: PocketBase;
beforeAll(async () => {
await requireServices();
admin = await adminPb();
});
describe("the AI key never leaves the server", () => {
it("is not readable by a client manager", async () => {
const pb = await userPb(DEMO.managerEmail, DEMO.managerPassword);
// Every rule on `settings` is null — superuser only.
await expect(pb.collection("settings").getFullList()).rejects.toThrow();
});
it("does not appear in the settings page HTML", async () => {
const marker = `sk-test-should-not-leak-${Date.now()}`;
const existing = await admin
.collection("settings")
.getFirstListItem("id != ''")
.catch(() => null);
const record = existing
? await admin.collection("settings").update(existing.id, { ai_api_key: marker })
: await admin.collection("settings").create({ ai_api_key: marker });
const cookie = await signIn(DEMO.ownerEmail, DEMO.ownerPassword);
const response = await fetch(`${APP_URL}/admin/settings`, { headers: { cookie } });
const html = await response.text();
expect(response.status).toBe(200);
expect(html).not.toContain(marker);
await admin.collection("settings").update(record.id, { ai_api_key: "" });
});
});
describe("the admin settings page is owner-only", () => {
it("returns 404 for a client manager", async () => {
const cookie = await signIn(DEMO.managerEmail, DEMO.managerPassword);
const response = await fetch(`${APP_URL}/admin/settings`, { headers: { cookie } });
expect(response.status).toBe(404);
});
});

View file

@ -8,6 +8,8 @@ export const DEMO = {
projectSlug: "wfdash-webapp", projectSlug: "wfdash-webapp",
managerEmail: "manager@wf.example.com", managerEmail: "manager@wf.example.com",
managerPassword: "manager-password-123", managerPassword: "manager-password-123",
ownerEmail: "owner@example.com",
ownerPassword: "owner-password-123",
}; };
/** /**