Interval change keeps unreported samples for the reporter (closes #124)
check / check (push) Successful in 2m9s

The interval menu cleared every host's history, and the reporter builds
each report from that history, so the samples recorded since the last
delivered report were never sent. The menu now calls
HostState.clearHistory, which moves the history's entries to
clearedHistory, keeping the last maxHistoryPoints as the history does;
buildReport reads both. The per-host marks are unchanged, so a sample a
delivered report had is not sent again, and the page's history still
starts over. Reporter is exported for the new unit test.

Model: opus-5-5
This commit is contained in:
2026-10-14 09:08:33 +00:00
parent b1eefe0c29
commit c6f325cff5
4 changed files with 90 additions and 10 deletions
+54
View File
@@ -14,6 +14,7 @@ import {
latencyClass,
latencyHex,
measureLatency,
Reporter,
tick,
} from "../../src/main.js";
@@ -378,6 +379,59 @@ test("a check that fails in the first round, which is discarded, writes nothing
assert.deepEqual(consoleLines("info"), []);
});
// Every report is delivered here, and only Target has checks to report.
// Between the first report and the second the interval changes: the
// interval menu calls clearHistory on each target. The third report would
// hold no check the first two did not, so it is not sent.
test("checks recorded before an interval change and not yet reported go in the next report, and no check is reported twice", async (t) => {
const state = new AppState([
{ name: "Target", url: "https://target.test" },
]);
const target = state.local[0];
const reporter = new Reporter(state, "client", CONFIG.reportInterval);
t.mock.method(globalThis, "fetch", async () => ({ ok: true }));
target.pushSample(1000, { latency: 10, error: null });
await reporter.flush();
target.pushSample(2000, { latency: 20, error: null });
target.pushSample(3000, { latency: null, error: "timeout" });
target.clearHistory();
target.pushSample(4000, { latency: 40, error: null });
await reporter.flush();
await reporter.flush();
assert.equal(fetch.mock.calls.length, 2);
const [first, second] = fetch.mock.calls.map(
(call) => JSON.parse(call.arguments[1].body).hosts,
);
const name = "Target";
const url = "https://target.test";
assert.deepEqual(first, [
{
name,
url,
status: "online",
history: [{ t: 1000, latency: 10, error: null }],
},
]);
assert.deepEqual(second, [
{
name,
url,
status: "online",
history: [
{ t: 2000, latency: 20, error: null },
{ t: 3000, latency: null, error: "timeout" },
{ t: 4000, latency: 40, error: null },
],
},
]);
// The page's history starts over.
assert.deepEqual(target.history, [
{ timestamp: 4000, latency: 40, error: null },
]);
});
// The page shows < > " & and ' in a row's markup as
// < > " & and '.
test(`a target whose name and URL hold < > " & and ' shows those characters in its row`, () => {