Files
psu-schedule/test/status.test.js
Zakhar Timoshenko 3ae7b38fca feat: implement dashboard with authentication and event logging
- Added dashboard functionality at /status with Basic Auth using DASHBOARD_PASSWORD.
- Created server-side event logging to track catalog and university status.
- Implemented traffic metrics tracking and display on the dashboard.
- Enhanced API tests to cover new dashboard features and authentication.
- Introduced CSS and JS for dashboard styling and functionality.
- Added tests for dashboard access control and data retrieval.
2026-10-21 00:04:33 +03:00

55 lines
2.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { test, before, after } from "node:test";
import assert from "node:assert/strict";
import { startApi } from "./support/nextcloud.js";
// The dashboard at /status: the browser's sign-in window, a lockout after ten wrong passwords,
// the page under the strict CSP, and its data.
let api;
before(async () => {
api = await startApi({ DASHBOARD_PASSWORD: "пароль-123" });
});
after(() => api?.stop());
const basic = (user, password) => ({ Authorization: "Basic " + Buffer.from(`${user}:${password}`).toString("base64") });
const get = (path, headers = {}) => fetch(api.base + path, { headers });
test("Дашборд: без пароля — окно входа браузера", async () => {
const response = await get("/status/");
assert.equal(response.status, 401);
assert.match(response.headers.get("www-authenticate"), /^Basic realm=/);
assert.equal(response.headers.get("cache-control"), "no-store");
assert.equal(response.headers.get("x-robots-tag"), "noindex");
assert.equal((await get("/status/data.json")).status, 401);
});
test("Дашборд: с паролем — страница, её файлы и данные", async () => {
const auth = basic("любой", "пароль-123");
const page = await get("/status/", auth);
assert.equal(page.status, 200);
const html = await page.text();
assert.match(html, /<script type="module" src="status\.js"><\/script>/);
// Nothing inline: the strict CSP allows only the server's own files.
assert.doesNotMatch(html, /<script>|style=/);
assert.match(page.headers.get("content-security-policy"), /script-src 'self'/);
assert.equal((await get("/status/status.js", auth)).status, 200);
assert.equal((await get("/status/status.css", auth)).status, 200);
await get("/api/groups?q=23");
const data = await (await get("/status/data.json", auth)).json();
assert.equal(typeof data.server.apiVersion, "number");
assert.ok(Array.isArray(data.server.waiting));
assert.ok(data.catalog.files > 0);
assert.equal(data.directory.documents, 2);
assert.equal(data.traffic.series.length, 60);
assert.ok(Array.isArray(data.events));
assert.equal(typeof data.diagnostics.problems, "number");
assert.equal(data.diagnostics.failed.length, 2);
assert.equal(data.telegram.enabled, false);
});
test("Дашборд: десять неверных паролей — блокировка на 10 минут", async () => {
for (let i = 0; i < 10; i++) assert.equal((await get("/status/", basic("admin", "не тот"))).status, 401);
const locked = await get("/status/", basic("admin", "пароль-123"));
assert.equal(locked.status, 429);
assert.ok(Number(locked.headers.get("retry-after")) > 0);
});