138 lines
4.4 KiB
TypeScript
138 lines
4.4 KiB
TypeScript
import {
|
|
count,
|
|
createUser,
|
|
db,
|
|
del,
|
|
formWithFile,
|
|
get,
|
|
insertDump,
|
|
PNG,
|
|
post,
|
|
test,
|
|
} from "../helpers.ts";
|
|
import type { AttachmentResponse } from "../../../shared/attachment.ts";
|
|
import type { Comment } from "../../../shared/comment.ts";
|
|
import { assertEquals } from "@std/assert";
|
|
import { runStartupTasks } from "../../db/startup.ts";
|
|
|
|
test("attachments: upload an image, serve it back", async () => {
|
|
const user = await createUser();
|
|
const res = await post<AttachmentResponse>("/api/attachments", {
|
|
auth: user.auth,
|
|
form: formWithFile(PNG),
|
|
});
|
|
assertEquals(res.status, 201);
|
|
const id = res.data.id;
|
|
const served = await get(`/api/attachments/${id}`);
|
|
assertEquals(served.status, 200);
|
|
assertEquals(served.bytes, PNG);
|
|
assertEquals(served.headers.get("content-type"), "image/png");
|
|
assertEquals(served.headers.get("x-content-type-options"), "nosniff");
|
|
assertEquals(served.headers.get("cache-control"), "public, max-age=3600");
|
|
assertEquals(
|
|
(await get(`/api/attachments/${crypto.randomUUID()}`)).status,
|
|
404,
|
|
);
|
|
});
|
|
|
|
test("attachments: refuses non-images, non-multipart and anonymous uploads", async () => {
|
|
const user = await createUser();
|
|
assertEquals(
|
|
(await post("/api/attachments", {
|
|
auth: user.auth,
|
|
form: formWithFile(
|
|
new TextEncoder().encode("<script>"),
|
|
"x.png",
|
|
"image/png",
|
|
),
|
|
})).status,
|
|
400,
|
|
);
|
|
assertEquals(
|
|
(await post("/api/attachments", { auth: user.auth, json: {} })).status,
|
|
400,
|
|
);
|
|
const empty = new FormData();
|
|
empty.set("other", "x");
|
|
assertEquals(
|
|
(await post("/api/attachments", { auth: user.auth, form: empty })).status,
|
|
400,
|
|
);
|
|
assertEquals(
|
|
(await post("/api/attachments", { form: formWithFile(PNG) })).status,
|
|
401,
|
|
);
|
|
assertEquals(count(`SELECT COUNT(*) as n FROM attachments;`), 0);
|
|
});
|
|
|
|
test("attachments are claimed by the first text that links them", async () => {
|
|
const user = await createUser();
|
|
const { id: dumpId } = insertDump({ userId: user.id });
|
|
const up = await post<AttachmentResponse>("/api/attachments", {
|
|
auth: user.auth,
|
|
form: formWithFile(PNG),
|
|
});
|
|
const attachmentId = up.data.id;
|
|
const link = ``;
|
|
const comment = await post<Comment>(`/api/dumps/${dumpId}/comments`, {
|
|
auth: user.auth,
|
|
json: { body: link },
|
|
});
|
|
const owner = () =>
|
|
(db.prepare(`SELECT resource_id FROM attachments WHERE id = ?;`).get(
|
|
attachmentId,
|
|
) as { resource_id: string | null }).resource_id;
|
|
assertEquals(owner(), comment.data.id);
|
|
// A second text can't take it over.
|
|
await post(`/api/dumps/${dumpId}/comments`, {
|
|
auth: user.auth,
|
|
json: { body: link },
|
|
});
|
|
assertEquals(owner(), comment.data.id);
|
|
});
|
|
|
|
test("attachments in a private dump need its owner's media token", async () => {
|
|
const owner = await createUser();
|
|
const other = await createUser();
|
|
const up = await post<AttachmentResponse>("/api/attachments", {
|
|
auth: owner.auth,
|
|
form: formWithFile(PNG),
|
|
});
|
|
const id = up.data.id;
|
|
const { id: dumpId } = insertDump({ userId: owner.id, isPrivate: true });
|
|
await post(`/api/dumps/${dumpId}/comments`, {
|
|
auth: owner.auth,
|
|
json: { body: `` },
|
|
});
|
|
assertEquals((await get(`/api/attachments/${id}`)).status, 404);
|
|
assertEquals(
|
|
(await get(`/api/attachments/${id}?token=${other.mediaToken}`)).status,
|
|
404,
|
|
);
|
|
const served = await get(`/api/attachments/${id}?token=${owner.mediaToken}`);
|
|
assertEquals(served.status, 200);
|
|
assertEquals(served.headers.get("cache-control"), "private, max-age=3600");
|
|
});
|
|
|
|
test("attachments of a deleted resource are gone, and purged at startup", async () => {
|
|
const user = await createUser();
|
|
const up = await post<AttachmentResponse>("/api/attachments", {
|
|
auth: user.auth,
|
|
form: formWithFile(PNG),
|
|
});
|
|
const id = up.data.id;
|
|
const { id: dumpId } = insertDump({ userId: user.id });
|
|
const comment = await post<Comment>(`/api/dumps/${dumpId}/comments`, {
|
|
auth: user.auth,
|
|
json: { body: `` },
|
|
});
|
|
assertEquals((await get(`/api/attachments/${id}`)).status, 200);
|
|
await del(`/api/comments/${comment.data.id}`, { auth: user.auth });
|
|
assertEquals((await get(`/api/attachments/${id}`)).status, 404);
|
|
await runStartupTasks(db);
|
|
assertEquals(
|
|
count(`SELECT COUNT(*) as n FROM attachments WHERE id = ?;`, id),
|
|
0,
|
|
);
|
|
});
|