e07993c536
- 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).
77 lines
2.4 KiB
JavaScript
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);
|
|
});
|
|
});
|