feat(server): auth status, attachments list, user preferences

GET auth/status ({ hasUsers }), GET attachments?entity=&entityId= (owner
scoped, 404 for foreign targets) and GET/PATCH me/preferences (ratingScale,
defaultVisibility in users.preferences).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Lucas WintherandClaude Opus 5.5 committed 2026-10-30 21:57:08 +01:00
1 parent d547355e5c
commit d204775235
5 files changed
+130 -2

No files matched your search

+43
View File
@@ -286,6 +286,49 @@ forEachDialect("api", (getDb) => {
expect((await call(app, "GET", `attachments/${att.id}`, { cookie: ana })).status).toBe(404);
});
test("auth/status says whether any user exists, without auth", async () => {
expect(await (await call(app, "GET", "auth/status")).json()).toEqual({ hasUsers: false });
await signUp(app, "ana");
expect(await (await call(app, "GET", "auth/status")).json()).toEqual({ hasUsers: true });
});
test("attachments list: owner-scoped per entity; a foreign or unknown entity is 404", async () => {
const ana = await signUp(app, "ana");
const add = (await (await call(app, "POST", "entries/quick", { cookie: ana, body: { title: "Dune" } })).json()) as { id: string };
const q = `attachments?entity=entry&entityId=${add.id}`;
expect(await (await call(app, "GET", q, { cookie: ana })).json()).toEqual([]);
const form = new FormData();
form.set("file", new File([new Uint8Array([0x89, 0x50])], "shot.png", { type: "image/png" }));
form.set("entity", "entry");
form.set("entityId", add.id);
const up = (await (await call(app, "POST", "attachments", { cookie: ana, form })).json()) as { id: string };
const list = (await (await call(app, "GET", q, { cookie: ana })).json()) as { id: string; mime: string; filename: string; links: { id: string }[] }[];
expect(list.map((a) => [a.id, a.mime, a.filename])).toEqual([[up.id, "image/png", "shot.png"]]);
expect(list[0]!.links.length).toBe(1);
const { code } = (await (await call(app, "POST", "admin/invites", { cookie: ana })).json()) as { code: string };
const bob = await signUp(app, "bob", code);
expect((await call(app, "GET", q, { cookie: bob })).status).toBe(404);
expect((await call(app, "GET", "attachments?entity=entry&entityId=nope", { cookie: ana })).status).toBe(404);
expect((await call(app, "GET", `attachments?entity=bogus&entityId=${add.id}`, { cookie: ana })).status).toBe(400);
expect((await call(app, "GET", q)).status).toBe(401);
});
test("me/preferences: defaults, validated PATCH, and quick add uses defaultVisibility", async () => {
const ana = await signUp(app, "ana");
expect(await (await call(app, "GET", "me/preferences", { cookie: ana })).json()).toEqual({ ratingScale: "5half", defaultVisibility: "private" });
const bad = await call(app, "PATCH", "me/preferences", { cookie: ana, body: { ratingScale: "7" } });
expect(bad.status).toBe(400);
expect((await call(app, "PATCH", "me/preferences", { cookie: ana, body: { extra: 1 } })).status).toBe(400);
const ok = await call(app, "PATCH", "me/preferences", { cookie: ana, body: { ratingScale: "10" } });
expect(await ok.json()).toEqual({ ratingScale: "10", defaultVisibility: "private" });
await call(app, "PATCH", "me/preferences", { cookie: ana, body: { defaultVisibility: "friends" } });
expect(await (await call(app, "GET", "me/preferences", { cookie: ana })).json()).toEqual({ ratingScale: "10", defaultVisibility: "friends" });
const e = (await (await call(app, "POST", "entries/quick", { cookie: ana, body: { title: "Dune" } })).json()) as { visibility: string };
expect(e.visibility).toBe("friends");
expect((await call(app, "PATCH", "me/preferences", { cookie: ana, body: { ratingScale: "5" }, noCsrf: true })).status).toBe(403);
});
test("JSON bodies need Content-Type application/json (415)", async () => {
const res = await app.fetch(
new Request(`${BASE}/auth/register`, {