53 lines
3.0 KiB
TypeScript
53 lines
3.0 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { decodeCursor, encodeCursor, MAX_COMMENT_IMAGE_BYTES, parseCommentForm, parseTarget } from "./validation";
|
|
|
|
const id = "11111111-1111-4111-8111-111111111111";
|
|
function form(text = "hello", count = 0, bytes = 1, type = "image/png") {
|
|
const value = new FormData();
|
|
value.set("text", text);
|
|
for (let i = 0; i < count; i++) value.append("image", new File([new Uint8Array(bytes)], `${i}.png`, { type }));
|
|
return value;
|
|
}
|
|
describe("comment form limits", () => {
|
|
it("accepts text and image-only comments", () => {
|
|
expect(parseCommentForm(form(" hello ")).text).toBe("hello");
|
|
expect(parseCommentForm(form("", 5)).files).toHaveLength(5);
|
|
expect(() => parseCommentForm(form(""))).toThrow("empty-comment");
|
|
});
|
|
it("enforces the combined retained and new image limit", () => {
|
|
expect(() => parseCommentForm(form("", 6))).toThrow("invalid-comment");
|
|
const value = form("editing", 5);
|
|
value.set("version", "1"); value.append("keepImageId", id);
|
|
expect(() => parseCommentForm(value, true)).toThrow("invalid-comment");
|
|
});
|
|
it("accepts exactly ten MiB and rejects one additional byte", () => {
|
|
expect(parseCommentForm(form("", 1, MAX_COMMENT_IMAGE_BYTES)).files).toHaveLength(1);
|
|
expect(() => parseCommentForm(form("", 1, MAX_COMMENT_IMAGE_BYTES + 1))).toThrow("image-too-large");
|
|
});
|
|
it.each(["video/mp4", "image/svg+xml", "image/gif", "application/octet-stream"])("rejects %s", (type) => {
|
|
expect(() => parseCommentForm(form("", 1, 1, type))).toThrow("unsupported-image-type");
|
|
});
|
|
it("enforces text, version, reply, and retained-image validation", () => {
|
|
expect(() => parseCommentForm(form("x".repeat(4001)))).toThrow("invalid-comment");
|
|
const value = form(); value.set("version", "0");
|
|
expect(() => parseCommentForm(value, true)).toThrow("invalid-comment");
|
|
value.set("version", "1"); value.set("replyToId", "bad");
|
|
expect(() => parseCommentForm(value, true)).toThrow("invalid-comment");
|
|
value.delete("replyToId"); value.append("keepImageId", id); value.append("keepImageId", id);
|
|
expect(() => parseCommentForm(value, true)).toThrow("invalid-comment");
|
|
});
|
|
});
|
|
describe("discussion identities and cursors", () => {
|
|
it("validates guide UUIDs and schedule IDs", () => {
|
|
expect(parseTarget(`guide:${id}`)).toEqual({ kind: "guide", id });
|
|
expect(parseTarget("stygian:5269001")).toEqual({ kind: "stygian", id: 5269001 });
|
|
for (const value of ["guide:invalid", "stygian:0", "stygian:NaN", "stygian:-1", "stygian:9999999999"]) expect(() => parseTarget(value)).toThrow("invalid-target");
|
|
});
|
|
it("round-trips keyset cursors and rejects arbitrary input", () => {
|
|
const cursor = { time: "2026-10-07T00:00:00.000Z", id, likes: 3 };
|
|
expect(decodeCursor(encodeCursor(cursor))).toEqual(cursor);
|
|
expect(() => decodeCursor("invalid")).toThrow("invalid-cursor");
|
|
expect(() => decodeCursor(encodeCursor({ ...cursor, likes: -1 }))).toThrow("invalid-cursor");
|
|
});
|
|
});
|