Files
AgentHub/server/__tests__/update-scheduler.test.js
T
nntrivi2001 e07993c536 perf: hourly update check only with a browser open; opt-in retention, incremental auto-vacuum and Compact
- update-scheduler: default interval 1 h (DASHBOARD_UPDATE_CHECK_INTERVAL_MS and its 60 s floor kept);
  a scheduled tick with no /ws client is skipped, and the next browser to connect gets one catch-up
  check when the last one is stale (websocket.js onConnection). Manual check unchanged.
- db-retention.js: shared session purge (used by POST /api/settings/cleanup too), opt-in
  AGENTHUB_RETENTION_DAYS applied by the hourly maintenance in 25-session transactions with yields
  (max 5000/run), bounded incremental_vacuum when the freelist is large, and a manual Compact
  (VACUUM in a worker, converting to auto_vacuum=INCREMENTAL).
- db.js: new databases are INCREMENTAL; an existing one up to 32 MB is converted at startup.
- GET/PUT /api/settings/retention, POST /api/settings/compact (confirm: true); Settings → Data →
  Session cleanup gets the Automatic cleanup control and Compact now (en + vi).
2026-10-16 10:20:38 +07:00

77 lines
2.4 KiB
JavaScript

/**
* @file Tests server/update-scheduler.js — the default interval is an hour
* (the env override and its 60 s floor still apply), a scheduled tick with no
* browser connected runs no check, and a browser connecting after that gets
* one catch-up check (only when the last check is stale). The check, the
* connection count and the connection event are injected; nothing touches git
* or the network.
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
*/
const { describe, it, afterEach } = require("node:test");
const assert = require("node:assert/strict");
delete process.env.DASHBOARD_UPDATE_CHECK;
const { startUpdateScheduler, intervalMs } = require("../update-scheduler");
const wait = (ms) => new Promise((r) => setTimeout(r, ms));
let handle = null;
afterEach(() => {
handle?.stop();
handle = null;
delete process.env.DASHBOARD_UPDATE_CHECK_INTERVAL_MS;
});
function harness(clients) {
const h = { checks: 0, clients, listeners: [] };
handle = startUpdateScheduler({
broadcast: () => {},
getConnectionCount: () => h.clients,
onConnection: (fn) => {
h.listeners.push(fn);
return () => {};
},
getStatus: async () => {
h.checks++;
return { update_available: false };
},
initialDelayMs: 10,
});
h.connect = () => h.listeners.forEach((fn) => fn());
return h;
}
describe("intervalMs", () => {
it("defaults to one hour and keeps the override with its floor", () => {
delete process.env.DASHBOARD_UPDATE_CHECK_INTERVAL_MS;
assert.equal(intervalMs(), 60 * 60 * 1000);
process.env.DASHBOARD_UPDATE_CHECK_INTERVAL_MS = "120000";
assert.equal(intervalMs(), 120000);
process.env.DASHBOARD_UPDATE_CHECK_INTERVAL_MS = "1000";
assert.equal(intervalMs(), 60 * 60 * 1000);
});
});
describe("startUpdateScheduler", () => {
it("checks on the first tick when a browser is connected", async () => {
const h = harness(1);
await wait(40);
assert.equal(h.checks, 1);
});
it("skips the tick with no browser, then checks once when one connects", async () => {
const h = harness(0);
await wait(40);
assert.equal(h.checks, 0);
h.clients = 1;
h.connect();
h.connect(); // a second tab in the same window: still one check
await wait(3_300);
assert.equal(h.checks, 1);
h.connect(); // fresh check: no catch-up
await wait(3_300);
assert.equal(h.checks, 1);
});
});