// Request validators and row guards in model/interfaces.ts and model/db.ts. import "./setup.ts"; import { assert, assertEquals, assertFalse } from "@std/assert"; import { isAuthPayload, isCreateCategoryRequest, isCreateCommentRequest, isCreatePlaylistRequest, isCreateUrlDumpRequest, isLoginUserRequest, isPasswordResetPayload, isReorderPlaylistRequest, isRole, isUpdateCategoryRequest, isUpdateCommentRequest, isUpdateDumpRequest, isUpdatePlaylistRequest, isUpdateUserRequest, validateRegisterUserRequest, } from "../model/interfaces.ts"; import { dumpApiToRow, type DumpRow, dumpRowToApi } from "../model/db.ts"; Deno.test("isRole", () => { assert(isRole("user") && isRole("moderator") && isRole("admin")); assertFalse(isRole("root")); assertFalse(isRole(undefined)); }); Deno.test("isLoginUserRequest", () => { assert(isLoginUserRequest({ username: "a", password: "b" })); assertFalse(isLoginUserRequest({ username: "a" })); assertFalse(isLoginUserRequest({ username: 1, password: "b" })); assertFalse(isLoginUserRequest(null)); }); Deno.test("validateRegisterUserRequest", () => { const ok = { username: "a_b_1", password: "12345678", email: "a@b.co", inviteToken: "t", }; assertEquals(validateRegisterUserRequest(ok), null); assertEquals( validateRegisterUserRequest({ ...ok, email: "a@b" }), "Invalid email address", ); assert( validateRegisterUserRequest({ ...ok, username: "" })?.startsWith( "Username", ), ); assert( validateRegisterUserRequest({ ...ok, username: "é" })?.startsWith( "Username", ), ); assertEquals(validateRegisterUserRequest({}), "Invalid request"); }); Deno.test("isUpdateUserRequest", () => { assert(isUpdateUserRequest({})); assert(isUpdateUserRequest({ description: null })); assert(isUpdateUserRequest({ description: "hi", email: "a@b.co" })); assertFalse(isUpdateUserRequest({ description: "x".repeat(2001) })); assertFalse(isUpdateUserRequest({ email: "nope" })); assertFalse(isUpdateUserRequest({ username: "no spaces" })); assertFalse(isUpdateUserRequest({ password: "short" })); assertFalse(isUpdateUserRequest({ role: "root" })); assertFalse(isUpdateUserRequest("string")); }); Deno.test("isAuthPayload refuses purpose-scoped tokens", () => { const base = { userId: "u", username: "n", role: "user", exp: 1 }; assert(isAuthPayload(base)); assertFalse(isAuthPayload({ ...base, purpose: "media" })); assertFalse(isAuthPayload({ ...base, role: "root" })); assertFalse(isAuthPayload({ ...base, exp: "1" })); assert(isPasswordResetPayload({ purpose: "password-reset", userId: "u" })); assertFalse(isPasswordResetPayload({ purpose: "media", userId: "u" })); }); Deno.test("comment requests: non-blank, bounded, optional parent", () => { assert(isCreateCommentRequest({ body: "hi" })); assert(isCreateCommentRequest({ body: "hi", parentId: null })); assert(isCreateCommentRequest({ body: "hi", parentId: "p" })); assertFalse(isCreateCommentRequest({ body: " " })); assertFalse(isCreateCommentRequest({ body: "x".repeat(5001) })); assertFalse(isCreateCommentRequest({ body: "hi", parentId: 3 })); assert(isUpdateCommentRequest({ body: "edit" })); assertFalse(isUpdateCommentRequest({ body: "" })); }); Deno.test("playlist requests", () => { assert(isCreatePlaylistRequest({ title: "t", isPublic: true })); assert( isCreatePlaylistRequest({ title: "t", isPublic: false, description: null }), ); assertFalse(isCreatePlaylistRequest({ title: "", isPublic: true })); assertFalse( isCreatePlaylistRequest({ title: "x".repeat(101), isPublic: true }), ); assertFalse(isCreatePlaylistRequest({ title: "t" })); assertFalse( isCreatePlaylistRequest({ title: "t", isPublic: true, description: "x".repeat(2001), }), ); assert(isUpdatePlaylistRequest({})); assert(isUpdatePlaylistRequest({ description: null })); assertFalse(isUpdatePlaylistRequest({ title: "" })); assertFalse(isUpdatePlaylistRequest({ isPublic: "yes" })); assert(isReorderPlaylistRequest({ dumpIds: [] })); assertFalse(isReorderPlaylistRequest({ dumpIds: [1] })); assertFalse(isReorderPlaylistRequest({})); }); Deno.test("dump requests", () => { assert(isCreateUrlDumpRequest({ url: "https://x" })); assert( isCreateUrlDumpRequest({ url: "https://x", title: "t", comment: null, isPrivate: true, categoryIds: ["a"], }), ); assertFalse(isCreateUrlDumpRequest({})); assertFalse( isCreateUrlDumpRequest({ url: "https://x", title: "x".repeat(201) }), ); assertFalse( isCreateUrlDumpRequest({ url: "https://x", comment: "x".repeat(5001) }), ); assertFalse(isCreateUrlDumpRequest({ url: "https://x", isPrivate: "true" })); assertFalse(isCreateUrlDumpRequest({ url: "https://x", categoryIds: [1] })); assert(isUpdateDumpRequest({})); assert(isUpdateDumpRequest({ comment: null, url: null })); assertFalse(isUpdateDumpRequest({ title: " " })); assertFalse(isUpdateDumpRequest({ title: "x".repeat(201) })); assertFalse(isUpdateDumpRequest({ categoryIds: "a" })); }); Deno.test("category requests", () => { assert(isCreateCategoryRequest({ slug: "a", name: "A" })); assert(isCreateCategoryRequest({ slug: "a", name: "A", position: 2 })); assertFalse(isCreateCategoryRequest({ slug: "a" })); assertFalse(isCreateCategoryRequest({ slug: "a", name: "A", position: "2" })); assert(isUpdateCategoryRequest({})); assertFalse(isUpdateCategoryRequest({ name: 1 })); }); Deno.test("dump rows round-trip through the API shape", () => { const row: DumpRow = { id: "id", kind: "url", title: "t", slug: "t-id", comment: null, user_id: "u", created_at: "2024-01-01T00:00:00.000Z", updated_at: null, url: "https://x", rich_content: JSON.stringify({ type: "generic", url: "https://x" }), file_name: null, file_mime: null, file_size: null, vote_count: 3, comment_count: 2, is_private: 1, custom_thumbnail_mime: null, }; const dump = dumpRowToApi(row); assertEquals(dump.isPrivate, true); assertEquals(dump.richContent?.url, "https://x"); assertEquals(dump.comment, undefined); assertEquals(dumpApiToRow(dump), row); });