import { describe, expect, test } from "bun:test"; import { contentTypeFor, hasFileExtension, htmlResponse, serveStaticAsset, type StaticAssets, } from "../../src/server/static"; function createTestAssets(): StaticAssets { return { files: { "/assets/index-a1b2c3.css": new Blob([".app{}"], { type: "text/css" }), "/assets/index-a1b2c3.js": new Blob(["console.log(1)"], { type: "text/javascript" }), "/assets/vendor-react-x9y8z7.js": new Blob(["react"], { type: "text/javascript" }), "/favicon.svg": new Blob([""], { type: "image/svg+xml" }), }, indexHtml: new Blob([""], { type: "text/html" }), }; } describe("contentTypeFor", () => { test("JavaScript 文件", () => { expect(contentTypeFor("/assets/index-a1b2c3.js")).toBe("text/javascript; charset=utf-8"); }); test("mjs 文件", () => { expect(contentTypeFor("/assets/chunk.mjs")).toBe("text/javascript; charset=utf-8"); }); test("CSS 文件", () => { expect(contentTypeFor("/assets/style.css")).toBe("text/css; charset=utf-8"); }); test("SVG 文件", () => { expect(contentTypeFor("/icon.svg")).toBe("image/svg+xml"); }); test("未知扩展名返回 octet-stream", () => { expect(contentTypeFor("/file.xyz")).toBe("application/octet-stream"); }); test("无扩展名返回 octet-stream", () => { expect(contentTypeFor("/noext")).toBe("application/octet-stream"); }); }); describe("hasFileExtension", () => { test("有扩展名", () => { expect(hasFileExtension("/assets/index.js")).toBe(true); expect(hasFileExtension("/favicon.svg")).toBe(true); }); test("无扩展名", () => { expect(hasFileExtension("/dashboard")).toBe(false); expect(hasFileExtension("/")).toBe(false); expect(hasFileExtension("/api/targets")).toBe(false); }); }); describe("htmlResponse", () => { test("返回 HTML 响应带正确 headers", async () => { const blob = new Blob([""]); const response = htmlResponse(blob); expect(response.headers.get("Content-Type")).toBe("text/html; charset=utf-8"); expect(response.headers.get("Cache-Control")).toBe("no-cache"); expect(await response.text()).toBe(""); }); }); describe("serveStaticAsset", () => { test("根路径返回 indexHtml", async () => { const assets = createTestAssets(); const response = serveStaticAsset("/", assets); expect(response.status).toBe(200); expect(response.headers.get("Content-Type")).toBe("text/html; charset=utf-8"); expect(response.headers.get("Cache-Control")).toBe("no-cache"); expect(await response.text()).toBe(""); }); test("已知资源返回对应文件和 immutable 缓存", async () => { const assets = createTestAssets(); const response = serveStaticAsset("/assets/index-a1b2c3.js", assets); expect(response.status).toBe(200); expect(response.headers.get("Content-Type")).toBe("text/javascript; charset=utf-8"); expect(response.headers.get("Cache-Control")).toBe("public, max-age=31536000, immutable"); expect(await response.text()).toBe("console.log(1)"); }); test("未知带扩展名路径返回 404", () => { const assets = createTestAssets(); const response = serveStaticAsset("/assets/missing.js", assets); expect(response.status).toBe(404); }); test("SPA fallback — 无扩展名路径返回 indexHtml", async () => { const assets = createTestAssets(); const response = serveStaticAsset("/dashboard", assets); expect(response.status).toBe(200); expect(response.headers.get("Content-Type")).toBe("text/html; charset=utf-8"); expect(response.headers.get("Cache-Control")).toBe("no-cache"); expect(await response.text()).toBe(""); }); test("SVG 资源返回正确 Content-Type", () => { const assets = createTestAssets(); const response = serveStaticAsset("/favicon.svg", assets); expect(response.status).toBe(200); expect(response.headers.get("Content-Type")).toBe("image/svg+xml"); expect(response.headers.get("Cache-Control")).toBe("public, max-age=31536000, immutable"); }); test("CSS 资源返回正确 Content-Type", () => { const assets = createTestAssets(); const response = serveStaticAsset("/assets/index-a1b2c3.css", assets); expect(response.status).toBe(200); expect(response.headers.get("Content-Type")).toBe("text/css; charset=utf-8"); }); });