Files
gerbeur/api/test/middleware/compress.test.ts

91 lines
3.6 KiB
TypeScript

import { BASE_URL } from "../helpers.ts";
import { assertEquals } from "@std/assert";
import { Application } from "@oak/oak";
import { brotliDecompressSync, gunzipSync } from "node:zlib";
import { compressMiddleware } from "../../middleware/compress.ts";
const BIG = { success: true, data: { items: Array(200).fill("dump title") } };
function makeApp(): Application {
const app = new Application();
app.use(compressMiddleware);
app.use((ctx) => {
const path = ctx.request.url.pathname;
if (path === "/big") ctx.response.body = BIG;
if (path === "/small") ctx.response.body = { success: true };
if (path === "/html") {
ctx.response.type = "text/html";
ctx.response.body = `<!doctype html>${"<p>gerbeur</p>".repeat(200)}`;
}
if (path === "/bytes") ctx.response.body = new Uint8Array(4096);
if (path === "/encoded") {
ctx.response.headers.set("Content-Encoding", "br");
ctx.response.type = "text/css";
ctx.response.body = "x".repeat(4096);
}
});
return app;
}
async function get(path: string, acceptEncoding?: string) {
const res = await makeApp().handle(
new Request(`${BASE_URL}${path}`, {
headers: acceptEncoding ? { "Accept-Encoding": acceptEncoding } : {},
}),
);
if (!res) throw new Error("no response");
return { res, bytes: new Uint8Array(await res.arrayBuffer()) };
}
const text = (bytes: Uint8Array) => new TextDecoder().decode(bytes);
Deno.test("compressMiddleware: JSON goes out in brotli, or gzip for a client without it", async () => {
const br = await get("/big", "gzip, deflate, br");
assertEquals(br.res.headers.get("Content-Encoding"), "br");
assertEquals(
br.res.headers.get("Content-Type"),
"application/json; charset=UTF-8",
);
assertEquals(br.res.headers.get("Vary"), "Accept-Encoding");
assertEquals(JSON.parse(text(brotliDecompressSync(br.bytes))), BIG);
const gzip = await get("/big", "gzip");
assertEquals(gzip.res.headers.get("Content-Encoding"), "gzip");
assertEquals(JSON.parse(text(gunzipSync(gzip.bytes))), BIG);
const refused = await get("/big", "br;q=0, gzip;q=0.5");
assertEquals(refused.res.headers.get("Content-Encoding"), "gzip");
});
Deno.test("compressMiddleware: a client that accepts no coding gets the JSON as it is", async () => {
for (const accept of [undefined, "identity", "br;q=0"]) {
const { res, bytes } = await get("/big", accept);
assertEquals(res.headers.get("Content-Encoding"), null);
// A cache still has to tell this answer from the compressed one.
assertEquals(res.headers.get("Vary"), "Accept-Encoding");
assertEquals(JSON.parse(text(bytes)), BIG);
}
});
Deno.test("compressMiddleware: the HTML page is compressed too", async () => {
const { res, bytes } = await get("/html", "br");
assertEquals(res.headers.get("Content-Encoding"), "br");
assertEquals(res.headers.get("Content-Type"), "text/html; charset=UTF-8");
assertEquals(text(brotliDecompressSync(bytes)).startsWith("<!doctype"), true);
});
Deno.test("compressMiddleware: short answers, bytes and encoded files are left alone", async () => {
const small = await get("/small", "br");
assertEquals(small.res.headers.get("Content-Encoding"), null);
assertEquals(small.res.headers.get("Vary"), null);
assertEquals(JSON.parse(text(small.bytes)), { success: true });
const bytes = await get("/bytes", "br");
assertEquals(bytes.res.headers.get("Content-Encoding"), null);
assertEquals(bytes.bytes.length, 4096);
const encoded = await get("/encoded", "br");
assertEquals(encoded.res.headers.get("Content-Encoding"), "br");
assertEquals(text(encoded.bytes), "x".repeat(4096));
});