feat: add admin tags and albums APIs
This commit is contained in:
153
apps/web/src/__tests__/albums-admin-auth.test.ts
Normal file
153
apps/web/src/__tests__/albums-admin-auth.test.ts
Normal file
@@ -0,0 +1,153 @@
|
||||
import { test, expect } from "bun:test";
|
||||
|
||||
function createMockDb(responses: Array<unknown>) {
|
||||
const calls: Array<{ sql: string; values: unknown[] }> = [];
|
||||
const db = async <T>(strings: TemplateStringsArray, ...values: unknown[]) => {
|
||||
calls.push({ sql: strings.join(""), values });
|
||||
const next = responses.shift();
|
||||
return next as T;
|
||||
};
|
||||
return { db, calls };
|
||||
}
|
||||
|
||||
test("albums POST rejects when missing admin token", async () => {
|
||||
const { handleCreateAlbum } = await import("../../app/api/albums/handlers");
|
||||
const res = await handleCreateAlbum({ adminOk: false, body: {} });
|
||||
expect(res.status).toBe(401);
|
||||
expect(res.body).toEqual({ error: "admin_required" });
|
||||
});
|
||||
|
||||
test("albums GET rejects when missing admin token", async () => {
|
||||
const { handleListAlbums } = await import("../../app/api/albums/handlers");
|
||||
const res = await handleListAlbums({ adminOk: false });
|
||||
expect(res.status).toBe(401);
|
||||
expect(res.body).toEqual({ error: "admin_required" });
|
||||
});
|
||||
|
||||
test("album add asset rejects when missing admin token", async () => {
|
||||
const { handleAddAlbumAsset } = await import(
|
||||
"../../app/api/albums/handlers"
|
||||
);
|
||||
const res = await handleAddAlbumAsset({
|
||||
adminOk: false,
|
||||
params: { id: "00000000-0000-4000-8000-000000000000" },
|
||||
body: { assetId: "00000000-0000-4000-8000-000000000000" },
|
||||
});
|
||||
expect(res.status).toBe(401);
|
||||
expect(res.body).toEqual({ error: "admin_required" });
|
||||
});
|
||||
|
||||
test("album remove asset rejects when missing admin token", async () => {
|
||||
const { handleRemoveAlbumAsset } = await import(
|
||||
"../../app/api/albums/handlers"
|
||||
);
|
||||
const res = await handleRemoveAlbumAsset({
|
||||
adminOk: false,
|
||||
params: { id: "00000000-0000-4000-8000-000000000000" },
|
||||
body: { assetId: "00000000-0000-4000-8000-000000000000" },
|
||||
});
|
||||
expect(res.status).toBe(401);
|
||||
expect(res.body).toEqual({ error: "admin_required" });
|
||||
});
|
||||
|
||||
test("albums GET returns rows", async () => {
|
||||
const { handleListAlbums } = await import("../../app/api/albums/handlers");
|
||||
const { db } = createMockDb([
|
||||
[
|
||||
{
|
||||
id: "00000000-0000-4000-8000-000000000010",
|
||||
name: "Summer",
|
||||
created_at: "2026-02-01T00:00:00.000Z",
|
||||
},
|
||||
],
|
||||
]);
|
||||
const res = await handleListAlbums({ adminOk: true, db: db as never });
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.body).toEqual([
|
||||
{
|
||||
id: "00000000-0000-4000-8000-000000000010",
|
||||
name: "Summer",
|
||||
created_at: "2026-02-01T00:00:00.000Z",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test("albums POST inserts and writes audit log", async () => {
|
||||
const { handleCreateAlbum } = await import("../../app/api/albums/handlers");
|
||||
const { db, calls } = createMockDb([
|
||||
[
|
||||
{
|
||||
id: "00000000-0000-4000-8000-000000000020",
|
||||
name: "Trips",
|
||||
created_at: "2026-02-01T00:00:00.000Z",
|
||||
},
|
||||
],
|
||||
[],
|
||||
]);
|
||||
const res = await handleCreateAlbum({
|
||||
adminOk: true,
|
||||
body: { name: "Trips" },
|
||||
db: db as never,
|
||||
});
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.body).toEqual({
|
||||
id: "00000000-0000-4000-8000-000000000020",
|
||||
name: "Trips",
|
||||
created_at: "2026-02-01T00:00:00.000Z",
|
||||
});
|
||||
expect(calls.some((call) => call.sql.includes("insert into audit_log"))).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
test("album add asset inserts and writes audit log", async () => {
|
||||
const { handleAddAlbumAsset } = await import(
|
||||
"../../app/api/albums/handlers"
|
||||
);
|
||||
const { db, calls } = createMockDb([
|
||||
[
|
||||
{
|
||||
album_id: "00000000-0000-4000-8000-000000000030",
|
||||
asset_id: "00000000-0000-4000-8000-000000000031",
|
||||
ord: 2,
|
||||
},
|
||||
],
|
||||
[],
|
||||
]);
|
||||
const res = await handleAddAlbumAsset({
|
||||
adminOk: true,
|
||||
params: { id: "00000000-0000-4000-8000-000000000030" },
|
||||
body: { assetId: "00000000-0000-4000-8000-000000000031", ord: 2 },
|
||||
db: db as never,
|
||||
});
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.body).toEqual({
|
||||
album_id: "00000000-0000-4000-8000-000000000030",
|
||||
asset_id: "00000000-0000-4000-8000-000000000031",
|
||||
ord: 2,
|
||||
});
|
||||
expect(calls.some((call) => call.sql.includes("insert into audit_log"))).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
test("album remove asset deletes and writes audit log", async () => {
|
||||
const { handleRemoveAlbumAsset } = await import(
|
||||
"../../app/api/albums/handlers"
|
||||
);
|
||||
const { db, calls } = createMockDb([[], [], []]);
|
||||
const res = await handleRemoveAlbumAsset({
|
||||
adminOk: true,
|
||||
params: { id: "00000000-0000-4000-8000-000000000040" },
|
||||
body: { assetId: "00000000-0000-4000-8000-000000000041" },
|
||||
db: db as never,
|
||||
});
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.body).toEqual({ ok: true });
|
||||
expect(calls.some((call) => call.sql.includes("delete from album_assets"))).toBe(
|
||||
true,
|
||||
);
|
||||
expect(calls.some((call) => call.sql.includes("insert into audit_log"))).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
75
apps/web/src/__tests__/tags-admin-auth.test.ts
Normal file
75
apps/web/src/__tests__/tags-admin-auth.test.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import { test, expect } from "bun:test";
|
||||
|
||||
function createMockDb(responses: Array<unknown>) {
|
||||
const calls: Array<{ sql: string; values: unknown[] }> = [];
|
||||
const db = async <T>(strings: TemplateStringsArray, ...values: unknown[]) => {
|
||||
calls.push({ sql: strings.join(""), values });
|
||||
const next = responses.shift();
|
||||
return next as T;
|
||||
};
|
||||
return { db, calls };
|
||||
}
|
||||
|
||||
test("tags POST rejects when missing admin token", async () => {
|
||||
const { handleCreateTag } = await import("../../app/api/tags/handlers");
|
||||
const res = await handleCreateTag({ adminOk: false, body: {} });
|
||||
expect(res.status).toBe(401);
|
||||
expect(res.body).toEqual({ error: "admin_required" });
|
||||
});
|
||||
|
||||
test("tags GET rejects when missing admin token", async () => {
|
||||
const { handleListTags } = await import("../../app/api/tags/handlers");
|
||||
const res = await handleListTags({ adminOk: false });
|
||||
expect(res.status).toBe(401);
|
||||
expect(res.body).toEqual({ error: "admin_required" });
|
||||
});
|
||||
|
||||
test("tags GET returns rows", async () => {
|
||||
const { handleListTags } = await import("../../app/api/tags/handlers");
|
||||
const { db } = createMockDb([
|
||||
[
|
||||
{
|
||||
id: "00000000-0000-4000-8000-000000000001",
|
||||
name: "Pets",
|
||||
created_at: "2026-02-01T00:00:00.000Z",
|
||||
},
|
||||
],
|
||||
]);
|
||||
const res = await handleListTags({ adminOk: true, db: db as never });
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.body).toEqual([
|
||||
{
|
||||
id: "00000000-0000-4000-8000-000000000001",
|
||||
name: "Pets",
|
||||
created_at: "2026-02-01T00:00:00.000Z",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test("tags POST inserts and writes audit log", async () => {
|
||||
const { handleCreateTag } = await import("../../app/api/tags/handlers");
|
||||
const { db, calls } = createMockDb([
|
||||
[
|
||||
{
|
||||
id: "00000000-0000-4000-8000-000000000002",
|
||||
name: "Trips",
|
||||
created_at: "2026-02-01T00:00:00.000Z",
|
||||
},
|
||||
],
|
||||
[],
|
||||
]);
|
||||
const res = await handleCreateTag({
|
||||
adminOk: true,
|
||||
body: { name: "Trips" },
|
||||
db: db as never,
|
||||
});
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.body).toEqual({
|
||||
id: "00000000-0000-4000-8000-000000000002",
|
||||
name: "Trips",
|
||||
created_at: "2026-02-01T00:00:00.000Z",
|
||||
});
|
||||
expect(calls.some((call) => call.sql.includes("insert into audit_log"))).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user