import { assertEquals } from "@std/assert"; import { find, insert } from "utils/db.ts"; import { getCryptoString } from "utils/server.ts"; let testCounter = 0; function getTestDbPath() { testCounter++; return `data/test_api_post_${testCounter}_${Date.now()}.db`; } function cleanup(dbPath: string) { try { Deno.removeSync(dbPath); } catch { // File may not exist } } function makeCtx(method: string, body?: object, cookie?: string) { const headers: Record = { "Content-Type": "application/json", }; if (cookie) headers["Cookie"] = cookie; const initMethod = method === "GET" && body ? "POST" : method; const req = new Request("http://localhost", { method: initMethod, headers, body: body ? JSON.stringify(body) : undefined, }); if (method === "GET" && body) { Object.defineProperty(req, "method", { value: "GET" }); } return { req }; } async function seedUserAndToken(email: string, password: string) { const hashedPw = await getCryptoString(password, "MD5"); const user = insert("User", { name: email.split("@")[0], email, password: hashedPw, }); const userId = user[0]["id"] as string | number; const token = await getCryptoString( "post-token-" + userId + Date.now(), "MD5", ); insert("Token", { token, user_id: userId }); return { userId, token }; } Deno.test("API post - create a new post", async () => { const dbPath = getTestDbPath(); Deno.env.set("POSTDOWN_DB_PATH", dbPath); try { const { token } = await seedUserAndToken( "postcreate@example.com", "password", ); const { handler } = await import("../../routes/api/post.tsx"); const ctx = makeCtx("POST", { title: "My First Post", content: "# Hello World", }, `pd-user-token=${token}`); const res = await handler.POST!(ctx as any); const body = await res.json(); assertEquals(body.success, true); assertEquals(typeof body.data, "string"); } finally { Deno.env.delete("POSTDOWN_DB_PATH"); cleanup(dbPath); } }); Deno.test("API post - create without token returns error", async () => { const dbPath = getTestDbPath(); Deno.env.set("POSTDOWN_DB_PATH", dbPath); try { const { handler } = await import("../../routes/api/post.tsx"); const ctx = makeCtx("POST", { title: "Unauthorized Post", content: "Should fail", }); const res = await handler.POST!(ctx as any); const body = await res.json(); assertEquals(body.success, false); } finally { Deno.env.delete("POSTDOWN_DB_PATH"); cleanup(dbPath); } }); Deno.test("API post - get own post", async () => { const dbPath = getTestDbPath(); Deno.env.set("POSTDOWN_DB_PATH", dbPath); try { const { userId, token } = await seedUserAndToken( "postget@example.com", "password", ); insert("Post", { id: "test-post-1", title: "Test Post", content: "Some content", user_id: userId, shared: 0, }); const { handler } = await import("../../routes/api/post.tsx"); const ctx = makeCtx("GET", { id: "test-post-1" }, `pd-user-token=${token}`); const res = await handler.GET!(ctx as any); const body = await res.json(); assertEquals(body.success, true); assertEquals(body.data.title, "Test Post"); assertEquals(body.data.content, "Some content"); } finally { Deno.env.delete("POSTDOWN_DB_PATH"); cleanup(dbPath); } }); Deno.test("API post - get shared post without token", async () => { const dbPath = getTestDbPath(); Deno.env.set("POSTDOWN_DB_PATH", dbPath); try { const { userId } = await seedUserAndToken( "postshared@example.com", "password", ); insert("Post", { id: "shared-post-1", title: "Shared Post", content: "Public content", user_id: userId, shared: 1, }); const { handler } = await import("../../routes/api/post.tsx"); const ctx = makeCtx("GET", { id: "shared-post-1" }); const res = await handler.GET!(ctx as any); const body = await res.json(); assertEquals(body.success, true); assertEquals(body.data.title, "Shared Post"); } finally { Deno.env.delete("POSTDOWN_DB_PATH"); cleanup(dbPath); } }); Deno.test("API post - get non-shared post without token returns error", async () => { const dbPath = getTestDbPath(); Deno.env.set("POSTDOWN_DB_PATH", dbPath); try { const { userId } = await seedUserAndToken( "postprivate@example.com", "password", ); insert("Post", { id: "private-post-1", title: "Private Post", content: "Secret content", user_id: userId, shared: 0, }); const { handler } = await import("../../routes/api/post.tsx"); const ctx = makeCtx("GET", { id: "private-post-1" }); const res = await handler.GET!(ctx as any); const body = await res.json(); assertEquals(body.success, false); } finally { Deno.env.delete("POSTDOWN_DB_PATH"); cleanup(dbPath); } }); Deno.test("API post - update post title", async () => { const dbPath = getTestDbPath(); Deno.env.set("POSTDOWN_DB_PATH", dbPath); try { const { userId, token } = await seedUserAndToken( "postupdate@example.com", "password", ); insert("Post", { id: "update-post-1", title: "Old Title", content: "Content", user_id: userId, shared: 0, }); const { handler } = await import("../../routes/api/post.tsx"); const ctx = makeCtx("PUT", { id: "update-post-1", title: "New Title", }, `pd-user-token=${token}`); const res = await handler.PUT!(ctx as any); const body = await res.json(); assertEquals(body.success, true); const post = find("Post", { id: "update-post-1" }, ["title"]); assertEquals(post[0]["title"], "New Title"); } finally { Deno.env.delete("POSTDOWN_DB_PATH"); cleanup(dbPath); } }); Deno.test("API post - update without token returns error", async () => { const dbPath = getTestDbPath(); Deno.env.set("POSTDOWN_DB_PATH", dbPath); try { const { handler } = await import("../../routes/api/post.tsx"); const ctx = makeCtx("PUT", { id: "some-post", title: "Hacked Title", }); const res = await handler.PUT!(ctx as any); const body = await res.json(); assertEquals(body.success, false); } finally { Deno.env.delete("POSTDOWN_DB_PATH"); cleanup(dbPath); } }); Deno.test("API post - delete own post", async () => { const dbPath = getTestDbPath(); Deno.env.set("POSTDOWN_DB_PATH", dbPath); try { const { userId, token } = await seedUserAndToken( "postdelete@example.com", "password", ); insert("Post", { id: "delete-post-1", title: "Delete Me", content: "Bye", user_id: userId, shared: 0, }); const { handler } = await import("../../routes/api/post.tsx"); const ctx = makeCtx("DELETE", { id: "delete-post-1", }, `pd-user-token=${token}`); const res = await handler.DELETE!(ctx as any); const body = await res.json(); assertEquals(body.success, true); const post = find("Post", { id: "delete-post-1" }, ["id"]); assertEquals(post.length, 0); } finally { Deno.env.delete("POSTDOWN_DB_PATH"); cleanup(dbPath); } }); Deno.test("API post - delete without token returns error", async () => { const dbPath = getTestDbPath(); Deno.env.set("POSTDOWN_DB_PATH", dbPath); try { const { handler } = await import("../../routes/api/post.tsx"); const ctx = makeCtx("DELETE", { id: "some-post" }); const res = await handler.DELETE!(ctx as any); const body = await res.json(); assertEquals(body.success, false); } finally { Deno.env.delete("POSTDOWN_DB_PATH"); cleanup(dbPath); } });