import { get, hasFfmpeg, html, makeImage, PNG, stubNetwork, webpWidth, } from "../helpers.ts"; import { assertEquals, assertFalse } from "@std/assert"; import type { Tralbum, YouTubePlaylist } from "../../../shared/player.ts"; // ── Images ─────────────────────────────────────────────────────────────────── Deno.test("GET /api/remote/image: rasters only, fetched once for everyone", async () => { using net = stubNetwork((url) => url.pathname.endsWith(".svg") ? new Response("", { headers: { "content-type": "image/svg+xml" } }) : new Response(PNG, { headers: { "content-type": "image/png" } }) ); const path = `/api/remote/image?url=${ encodeURIComponent("https://cdn.example.com/a.png") }`; const first = await get(path); assertEquals(first.status, 200); assertEquals(first.headers.get("content-type"), "image/png"); assertEquals(first.bytes, PNG); const second = await get(path); assertEquals(second.bytes, PNG); assertEquals(net.calls.length, 1); const svg = await get( `/api/remote/image?url=${ encodeURIComponent("https://cdn.example.com/a.svg") }`, ); assertEquals(svg.status, 400); }); Deno.test("GET /api/remote/image: `?w=` scales the image, fetched once for every width", async () => { if (!await hasFfmpeg()) return; const wide = await makeImage(800, 200); using net = stubNetwork(() => new Response(wide, { headers: { "content-type": "image/png" } }) ); const path = `/api/remote/image?url=${ encodeURIComponent("https://cdn.example.com/wide.png") }`; const small = await get(`${path}&w=128`); assertEquals(small.status, 200); assertEquals(small.headers.get("content-type"), "image/webp"); assertEquals(webpWidth(small.bytes), 128); assertEquals(webpWidth((await get(`${path}&w=320`)).bytes), 320); assertEquals((await get(`${path}&w=128`)).bytes, small.bytes); assertEquals((await get(path)).bytes, wide); assertEquals(net.calls.length, 1); }); Deno.test("GET /api/remote/image: an error answer is never served nor cached", async () => { let failing = true; using net = stubNetwork(() => failing ? new Response(PNG, { status: 503, headers: { "content-type": "image/png" }, }) : new Response(PNG, { headers: { "content-type": "image/png" } }) ); const path = `/api/remote/image?url=${ encodeURIComponent("https://cdn.example.com/flaky.png") }`; assertEquals((await get(path)).status, 400); failing = false; assertEquals((await get(path)).status, 200); assertEquals(net.calls.length, 2); }); // ── Bandcamp ───────────────────────────────────────────────────────────────── function escapeAttr(s: string): string { return s.replaceAll("&", "&").replaceAll('"', """); } function tralbumPage(data: unknown): string { return `
`; } const STREAM = "https://t4.bcbits.com/stream/abc/mp3-128/1?p=0&ts=1&t=x"; const ALBUM = { item_type: "album", artist: "Artist", art_id: 12345, current: { title: 'The "Album" & more' }, trackinfo: [ { track_num: 1, title: "One", duration: 180.5, streaming: 1, file: { "mp3-128": STREAM }, }, { track_num: 2, title: "Preorder", streaming: 1, unreleased_track: true, file: null, }, { track_num: 3, title: "", streaming: 1, file: { "mp3-128": "https://evil.test/x.mp3" }, }, { track_num: 4, title: "Capped", streaming: 1, is_capped: true, file: { "mp3-128": STREAM }, }, ], }; Deno.test("GET /api/remote/bandcamp: resolves, caches, and forces a refetch", async () => { using net = stubNetwork(() => html(tralbumPage(ALBUM))); const url = "https://cached.bandcamp.com/album/x"; const res = await get( `/api/remote/bandcamp?url=${encodeURIComponent(url)}`, ); assertEquals(res.status, 200); assertEquals(res.headers.get("cache-control"), "private, max-age=3600"); assertEquals(res.data.tracks.length, 4); // Same page (different query/case/trailing slash) → cache hit. await get( `/api/remote/bandcamp?url=${ encodeURIComponent("https://CACHED.bandcamp.com/album/x/?from=y") }`, ); assertEquals(net.calls.length, 1); await get(`/api/remote/bandcamp?url=${encodeURIComponent(url)}&force=1`); assertEquals(net.calls.length, 2); }); Deno.test("GET /api/remote/bandcamp: refusals and upstream failures", async () => { using _ = stubNetwork((url) => { if (url.hostname.startsWith("down")) { return new Response("", { status: 503 }); } if (url.hostname.startsWith("json")) return Response.json({}); return html("no tralbum"); }); const status = async (url: string) => (await get(`/api/remote/bandcamp?url=${encodeURIComponent(url)}`)).status; assertEquals(await status("https://example.com/album/x"), 400); assertEquals(await status("https://bandcamp.com.evil.test/album/x"), 400); assertEquals(await status("http://localhost/album/x"), 400); assertEquals(await status("https://down.bandcamp.com/album/x"), 502); assertEquals(await status("https://json.bandcamp.com/album/x"), 502); assertEquals(await status("https://empty.bandcamp.com/album/x"), 404); }); // ── YouTube ────────────────────────────────────────────────────────────────── function ytPage(data: unknown): string { return ``; } const YT_DATA = { metadata: { playlistMetadataRenderer: { title: "My mix" } }, contents: { list: [ { lockupViewModel: { contentType: "LOCKUP_CONTENT_TYPE_VIDEO", contentId: "vid1", metadata: { lockupMetadataViewModel: { title: { content: "First" } }, }, contentImage: { overlays: [{ thumbnailBadgeViewModel: { text: "1:02:03" } }], }, }, }, { lockupViewModel: { contentType: "LOCKUP_CONTENT_TYPE_PLAYLIST", contentId: "ignored", }, }, { playlistVideoRenderer: { videoId: "vid2", title: { runs: [{ text: "Second" }] }, lengthSeconds: "235", }, }, ], }, }; Deno.test("GET /api/remote/youtube: resolves and validates the id", async () => { let cookie: string | null = null; using net = stubNetwork((url, init) => { cookie = new Headers(init?.headers).get("cookie"); return url.searchParams.get("list") === "PLgone" ? new Response("", { status: 404 }) : html(ytPage(YT_DATA)); }); const ok = await get("/api/remote/youtube?list=PLgood"); assertEquals(ok.status, 200); assertEquals(ok.data.tracks.length, 2); assertEquals(ok.headers.get("cache-control"), "public, max-age=3600"); // The pre-answered consent cookie keeps EU requests off the consent page. assertEquals(cookie, "SOCS=CAI"); await get("/api/remote/youtube?list=PLgood"); // cached assertEquals(net.calls.length, 1); assertEquals((await get("/api/remote/youtube?list=bad%20id")).status, 400); assertEquals((await get("/api/remote/youtube")).status, 400); assertEquals((await get("/api/remote/youtube?list=PLgone")).status, 502); assertFalse(net.calls.some((u) => u.searchParams.get("list") === "bad id")); });