import { test, before, after } from "node:test"; import assert from "node:assert/strict"; import { setTimeout as sleep } from "node:timers/promises"; import { startApi } from "./support/nextcloud.js"; // The university is asked politely: every request names the service and its owner, and while // the university is down nothing reaches it until a pause that doubles with every failure. let api, watch; const health = async () => (await fetch(api.base + "/status/data.json", { headers: { Authorization: "Basic " + Buffer.from("admin:secret").toString("base64") } })).json(); before(async () => { api = await startApi({ PSU_PUBLIC_URL: "https://psu.example", PSU_CONTACT: "mailto:owner@example.com", PSU_MIN_REFRESH_MS: "0", // A short pause instead of five minutes: 300 ms, then 600 ms. PSU_SOURCE_BACKOFF_MS: "300", DASHBOARD_PASSWORD: "secret", }); watch = await import("../server/watch.js"); await watch.watchOnce(); }); after(() => api?.stop()); test("Источник: запросы представляются сервисом и контактом владельца", () => { assert.match(api.stats.userAgent, /^PSU-Schedule\/\d+\.\d+\.\d+ \(\+https:\/\/psu\.example; mailto:owner@example\.com\)$/); }); test("Источник: пока университет не отвечает, проверки ждут всё дольше", async () => { api.catalog.down(true); let requests = api.stats.requests; await watch.watchOnce(); assert.equal(api.stats.requests - requests, 1, "одна попытка, дальше обход не идёт"); let { catalog } = await health(); assert.equal(catalog.backoff.failures, 1); assert.ok(Date.parse(catalog.backoff.retryAt) > Date.now()); // Within the pause neither the watcher nor a forced refresh reaches the university, and // students still get the saved catalog. requests = api.stats.requests; await watch.watchOnce(); const refreshed = await fetch(api.base + "/api/catalog?refresh=1"); assert.equal(refreshed.status, 200); assert.equal((await refreshed.json()).stale, true); assert.equal(api.stats.requests, requests); // After the pause: one more attempt, and the next pause is twice as long. await sleep(350); await watch.watchOnce(); assert.equal(api.stats.requests - requests, 1); ({ catalog } = await health()); assert.equal(catalog.backoff.failures, 2); assert.ok(Date.parse(catalog.backoff.retryAt) - Date.now() > 350); // Back again: the first attempt after the pause reads the catalog, and the pause is over. api.catalog.down(false); await sleep(650); await watch.watchOnce(); ({ catalog } = await health()); assert.equal(catalog.backoff, null); assert.equal(catalog.failingSince, null); assert.ok(!(await (await fetch(api.base + "/api/catalog")).json()).stale); });