import { createUser, db, del, get, insertDump, patch, test, } from "./helpers.ts"; import type { Invitee, PublicUser, SelfUser, UserSuggestion, } from "../model/interfaces.ts"; import { assertEquals, assertFalse } from "@std/assert"; test("public profile: by username or id, without private fields", async () => { const user = await createUser(); for ( const path of [`/api/users/${user.username}`, `/api/users/by-id/${user.id}`] ) { const res = await get(path); assertEquals(res.status, 200, path); assertEquals(res.data.username, user.username); assertFalse("email" in res.data); assertFalse("passwordHash" in res.data); } assertEquals((await get(`/api/users/no_one_here`)).status, 404); const me = await get("/api/users/me", { auth: user.auth }); assertEquals(me.data.email, user.email); assertFalse("passwordHash" in me.data); }); test("PATCH /api/users/me: description and email only", async () => { const user = await createUser(); const taken = await createUser(); const res = await patch("/api/users/me", { auth: user.auth, json: { description: "about me", email: "new@example.test" }, }); assertEquals(res.status, 200); assertEquals(res.data.description, "about me"); assertFalse("email" in res.data); assertEquals( (await get("/api/users/me", { auth: user.auth })).data.email, "new@example.test", ); // A role, username or password in the body is ignored, not applied. await patch("/api/users/me", { auth: user.auth, json: { role: "admin", username: "hijack", password: "whatever-long" }, }); const row = db.prepare(`SELECT role, username FROM users WHERE id = ?;`).get( user.id, ) as { role: string; username: string; }; assertEquals(row, { role: "user", username: user.username }); assertEquals( (await patch("/api/users/me", { auth: user.auth, json: { email: taken.email }, })).status, 409, ); assertEquals( (await patch("/api/users/me", { auth: user.auth, json: { email: "bad" } })) .status, 400, ); assertEquals( (await patch("/api/users/me", { auth: user.auth, json: { description: "x".repeat(2001) }, })).status, 400, ); }); test("PATCH /api/users/:id/role: admins only, never their own", async () => { const admin = await createUser({ role: "admin" }); const mod = await createUser({ role: "moderator" }); const user = await createUser(); assertEquals( (await patch(`/api/users/${user.id}/role`, { auth: mod.auth, json: { role: "admin" }, })) .status, 403, ); assertEquals( (await patch(`/api/users/${admin.id}/role`, { auth: admin.auth, json: { role: "user" }, })) .status, 400, ); assertEquals( (await patch(`/api/users/${user.id}/role`, { auth: admin.auth, json: { role: "god" }, })) .status, 400, ); const res = await patch(`/api/users/${user.id}/role`, { auth: admin.auth, json: { role: "moderator" }, }); assertEquals(res.status, 200); assertEquals(res.data.role, "moderator"); assertFalse("email" in res.data); // The promoted user's existing token now carries moderator powers. const { id: dumpId } = insertDump({ userId: admin.id }); assertEquals( (await del(`/api/dumps/${dumpId}`, { auth: user.auth })).status, 200, ); assertEquals( (await patch(`/api/users/${crypto.randomUUID()}/role`, { auth: admin.auth, json: { role: "user" }, })).status, 404, ); }); test("GET /api/users/search: prefix match for mentions, capped at 8", async () => { for (let i = 0; i < 10; i++) await createUser({ username: `zed_${i}` }); await createUser({ username: "other_zed" }); const res = await get("/api/users/search?q=zed"); assertEquals(res.data.length, 8); assertEquals(res.data[0], { id: res.data[0].id, username: "zed_0", avatarMime: null, }); assertEquals((await get("/api/users/search?q=")).data, []); }); test("GET /api/users/:username/invitees: the invite tree below a user", async () => { const root = await createUser(); const child = await createUser({ invitedBy: root.id, createdAt: "2024-01-01T00:00:00Z", }); const grandchild = await createUser({ invitedBy: child.id, createdAt: "2024-01-02T00:00:00Z", }); const tree = (await get(`/api/users/${root.username}/invitees`)).data; assertEquals( tree.map(( n: { username: string; invitedById: string }, ) => [n.username, n.invitedById]), [[child.username, root.id], [grandchild.username, child.id]], ); const profile = await get(`/api/users/${child.username}`); assertEquals(profile.data.invitedByUsername, root.username); });