// The app-wide middleware: error rendering, CORS, OG tags for crawlers, and // static file serving with its cache rules. import { BASE_URL, createUser, get, insertDump, insertPlaylist, request, test, TEST_DIR, } from "./helpers.ts"; import { assertEquals, assertFalse, assertStringIncludes } from "@std/assert"; import { Application } from "@oak/oak"; import { errorMiddleware } from "../middleware/error.ts"; import { ogMiddleware } from "../middleware/og.ts"; import { routeStaticFilesFrom } from "../lib/static.ts"; import { APIErrorCode, APIException } from "../model/interfaces.ts"; async function handle( app: Application, path: string, headers: HeadersInit = {}, ) { const res = await app.handle(new Request(`${BASE_URL}${path}`, { headers })); if (!res) throw new Error("no response"); return { res, text: await res.text() }; } Deno.test("errorMiddleware: APIExceptions keep their status, anything else is a bare 500", async () => { const app = new Application(); app.use(errorMiddleware); app.use((ctx) => { if (ctx.request.url.pathname === "/api-error") { throw new APIException(APIErrorCode.NOT_FOUND, 404, "Nope"); } throw new Error("secret internals"); }); const known = await handle(app, "/api-error"); assertEquals(known.res.status, 404); assertEquals(JSON.parse(known.text), { success: false, error: { code: "NOT_FOUND", message: "Nope" }, }); const unknown = await handle(app, "/boom"); assertEquals(unknown.res.status, 500); assertEquals(JSON.parse(unknown.text).error, { code: "SERVER_ERROR", message: "Unexpected server error", }); }); test("CORS: allowed origins get the headers, others don't", async () => { const allowed = await get("/api/categories", { headers: { Origin: "http://allowed.test" }, }); assertEquals( allowed.headers.get("access-control-allow-origin"), "http://allowed.test", ); // The public URL is always allowed. const self = await get("/api/categories", { headers: { Origin: BASE_URL } }); assertEquals(self.headers.get("access-control-allow-origin"), BASE_URL); const evil = await get("/api/categories", { headers: { Origin: "http://evil.test" }, }); assertEquals(evil.status, 200); assertEquals(evil.headers.get("access-control-allow-origin"), null); const preflight = await request("OPTIONS", "/api/dumps", { headers: { Origin: "http://allowed.test", "Access-Control-Request-Method": "POST", "Access-Control-Request-Headers": "authorization,content-type", }, }); assertEquals(preflight.status, 204); assertEquals( preflight.headers.get("access-control-allow-origin"), "http://allowed.test", ); }); test("GET /ws: refuses other origins, and plain requests from allowed ones", async () => { assertEquals( (await get("/ws", { headers: { Origin: "http://evil.test" } })).status, 403, ); assertEquals((await get("/ws")).status, 403); assertEquals( (await get("/ws", { headers: { Origin: "http://allowed.test" } })).status, 426, ); }); function ogApp() { const app = new Application(); app.use(ogMiddleware); app.use((ctx) => { ctx.response.body = "fallthrough"; }); return app; } test("ogMiddleware: dump pages get escaped OG tags", async () => { const user = await createUser(); const dump = insertDump({ userId: user.id, title: `Tom & "Jerry"