All checks were successful
Build and Publish Docker Image / build-and-push (push) Successful in 46s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
183 lines
6.1 KiB
TypeScript
183 lines
6.1 KiB
TypeScript
// 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);
|
|
});
|