73 lines
2.3 KiB
JavaScript
73 lines
2.3 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
import { createApp } from "../src/app.js";
|
|
import { baseConfig, listen } from "./helpers.js";
|
|
|
|
const feed = {
|
|
body: "<rss></rss>",
|
|
etag: '"feed-tag"',
|
|
lastModified: new Date("2025-01-01T00:00:00Z"),
|
|
};
|
|
|
|
test("serves health and RSS endpoints with conditional GET", async (t) => {
|
|
const app = createApp(baseConfig, {
|
|
feedService: { async get() { return feed; } },
|
|
silent: true,
|
|
});
|
|
const server = await listen(app);
|
|
t.after(server.close);
|
|
|
|
const health = await fetch(`${server.url}/healthz`);
|
|
assert.equal(health.status, 200);
|
|
assert.deepEqual(await health.json(), { status: "ok" });
|
|
|
|
const response = await fetch(`${server.url}/rss.xml`);
|
|
assert.equal(response.status, 200);
|
|
assert.match(response.headers.get("content-type"), /application\/rss\+xml/);
|
|
assert.equal(response.headers.get("etag"), feed.etag);
|
|
assert.equal(await response.text(), feed.body);
|
|
|
|
const fresh = await fetch(`${server.url}/rss.xml`, {
|
|
headers: { "if-none-match": feed.etag },
|
|
});
|
|
assert.equal(fresh.status, 304);
|
|
});
|
|
|
|
test("optionally protects the feed with HTTP Basic auth", async (t) => {
|
|
const config = structuredClone(baseConfig);
|
|
config.feed.username = "reader";
|
|
config.feed.password = "secret:with-colon";
|
|
const app = createApp(config, {
|
|
feedService: { async get() { return feed; } },
|
|
silent: true,
|
|
});
|
|
const server = await listen(app);
|
|
t.after(server.close);
|
|
|
|
const denied = await fetch(`${server.url}/rss.xml`);
|
|
assert.equal(denied.status, 401);
|
|
assert.match(denied.headers.get("www-authenticate"), /Basic/);
|
|
|
|
const accepted = await fetch(`${server.url}/rss.xml`, {
|
|
headers: {
|
|
authorization: `Basic ${Buffer.from("reader:secret:with-colon").toString("base64")}`,
|
|
},
|
|
});
|
|
assert.equal(accepted.status, 200);
|
|
});
|
|
|
|
test("returns 502 without leaking upstream error details", async (t) => {
|
|
const app = createApp(baseConfig, {
|
|
feedService: { async get() { throw new Error("secret upstream detail"); } },
|
|
silent: true,
|
|
});
|
|
const server = await listen(app);
|
|
t.after(server.close);
|
|
|
|
const response = await fetch(`${server.url}/rss.xml`);
|
|
assert.equal(response.status, 502);
|
|
assert.deepEqual(await response.json(), {
|
|
error: "Could not generate feed from Suwayomi",
|
|
});
|
|
});
|