Files
OpenFrontIO/tests/server/RenderHtml.test.ts
T
Josh Harris 2d6342cd22 Add stale-if-error to app shell Cache-Control (#4009)
## Description:

Adds `stale-if-error=86400` to the `Cache-Control` header set on the
rendered app shell (`/`) in
[src/server/RenderHtml.ts](src/server/RenderHtml.ts). This lets shared
caches (CloudFlare, nginx `proxy_cache`) keep serving the last good
`index.html` for up to 24h if origin returns a 5xx, alongside the
existing `stale-while-revalidate` window.

Pairs with enabling HTML caching for the `/` route on CloudFlare in
"respect origin headers" mode — it already honors `s-maxage` (5 min edge
TTL) and `stale-while-revalidate`; this just extends the same safety net
to origin-error cases.

No behavior change for successful responses; browsers still revalidate
every load via `max-age=0`.

## Please complete the following:

- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced

## Please put your Discord username so you can be contacted if a bug or
regression is found:

jish

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 21:13:48 +01:00

68 lines
1.9 KiB
TypeScript

import fs from "fs/promises";
import os from "os";
import path from "path";
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
import {
clearAppShellContentCache,
getAppShellContent,
setAppShellCacheHeaders,
} from "../../src/server/RenderHtml";
describe("RenderHtml", () => {
const originalGitCommit = process.env.GIT_COMMIT;
let tempDir: string | null = null;
beforeEach(() => {
vi.stubEnv("NUM_WORKERS", "1");
vi.stubEnv("TURNSTILE_SITE_KEY", "test-key");
vi.stubEnv("DOMAIN", "localhost");
});
afterEach(async () => {
vi.unstubAllEnvs();
process.env.GIT_COMMIT = originalGitCommit;
clearAppShellContentCache();
if (tempDir) {
await fs.rm(tempDir, { recursive: true, force: true });
tempDir = null;
}
});
test("reuses cached app shell content", async () => {
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "render-html-"));
const htmlPath = path.join(tempDir, "index.html");
await fs.writeFile(
htmlPath,
"<script>window.GIT_COMMIT = <%- gitCommit %>;</script>",
"utf8",
);
process.env.GIT_COMMIT = "first";
const first = await getAppShellContent(htmlPath);
process.env.GIT_COMMIT = "second";
const second = await getAppShellContent(htmlPath);
expect(first).toContain('"first"');
expect(second).toBe(first);
expect(second).not.toContain('"second"');
});
test("sets shared-cache headers for the app shell", () => {
const headers = new Map<string, string>();
const response = {
setHeader(name: string, value: string) {
headers.set(name, value);
},
} as any;
setAppShellCacheHeaders(response);
expect(headers.get("Cache-Control")).toBe(
"public, max-age=0, s-maxage=300, stale-while-revalidate=86400, stale-if-error=86400",
);
expect(headers.get("Content-Type")).toBe("text/html");
});
});