From 648c225919e7d5d5ad41f7c3355b650d5eb9549a Mon Sep 17 00:00:00 2001 From: clawbot <35+clawbot@noreply.example.org> Date: Mon, 19 Oct 2026 03:44:13 +0200 Subject: [PATCH] Discarded-check unit tests check nothing was recorded (closes #151) The three unit tests of a check whose result is discarded (its round given up, the user paused, or the first round) checked only that the row showed nothing. Each now also checks that the host's history is still empty, its status still pending, and nothing was written to the console, so a change that recorded the result and skipped only the row fails them. The console helper moves above its first use. Model: opus-5-5 --- TODO.md | 7 +++++++ test/unit/main.test.js | 31 ++++++++++++++++++++++--------- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/TODO.md b/TODO.md index 539dfe2..33acea5 100644 --- a/TODO.md +++ b/TODO.md @@ -21,6 +21,13 @@ Run `make frontend-viewport-test` in CI as its own step; it is not part of # Completed Steps +- 2026-10-19: the three unit tests of a check whose result is discarded (its + round given up, the user paused, or the first round) also check that the + host's history is still empty, its status still `pending`, and nothing was + written to the console + ([#151](https://git.eeqj.de/sneak/netwatch/issues/151)). They checked only the + row, so a change that recorded the result and skipped only the row would have + kept them passing - 2026-10-18: when the backend runs on its own, a `DATA_DIR` that cannot be created or read, or where no file can be created, stops the start with an error naming `DATA_DIR` and its value, as the other settings' errors do diff --git a/test/unit/main.test.js b/test/unit/main.test.js index be51957..466204e 100644 --- a/test/unit/main.test.js +++ b/test/unit/main.test.js @@ -83,6 +83,11 @@ function statusText(state, host) { return elements[`.status-text[data-host="${index}"]`]?.innerHTML; } +// What this test wrote to the browser console with console[method]. +function consoleLines(method) { + return console[method].mock.calls.map((call) => call.arguments[0]); +} + // Mocks the clock for test t, so that a check lasting seconds takes no real // time, and replaces fetch with targets that each answer after // answerAfter(url) milliseconds of that clock, or never when that is @@ -202,9 +207,10 @@ test("at a 30000ms interval, a target answering after 1000ms shows in its row wh }); // In the next three tests, the answering target's check is still waiting -// when something happens after which its result must not show. +// when something happens after which its result must not be recorded or +// show in its row, and the check must write nothing to the console. -test("at a 30000ms interval, a check still waiting when its round is given up does not show in its row", async (t) => { +test("at a 30000ms interval, a check still waiting when its round is given up is not recorded and does not show in its row", async (t) => { CONFIG.updateInterval = 30000; const state = new AppState([ { name: "Answering", url: "https://answering.test" }, @@ -220,9 +226,13 @@ test("at a 30000ms interval, a check still waiting when its round is given up do roundChecks.abort(); assert.notEqual(await settled(round), "still waiting"); assert.equal(latencyFigure(state, answering), undefined); + assert.deepEqual(answering.history, []); + assert.equal(answering.status, "pending"); + assert.deepEqual(consoleLines("error"), []); + assert.deepEqual(consoleLines("info"), []); }); -test("at a 30000ms interval, a check still waiting when the user pauses does not show in its row", async (t) => { +test("at a 30000ms interval, a check still waiting when the user pauses is not recorded and does not show in its row", async (t) => { CONFIG.updateInterval = 30000; const state = new AppState([ { name: "Answering", url: "https://answering.test" }, @@ -237,9 +247,13 @@ test("at a 30000ms interval, a check still waiting when the user pauses does not t.mock.timers.tick(500); assert.equal(await settled(round), "still waiting"); assert.equal(latencyFigure(state, answering), undefined); + assert.deepEqual(answering.history, []); + assert.equal(answering.status, "pending"); + assert.deepEqual(consoleLines("error"), []); + assert.deepEqual(consoleLines("info"), []); }); -test("at a 30000ms interval, a check in the first round does not show in its row", async (t) => { +test("at a 30000ms interval, a check in the first round is not recorded and does not show in its row", async (t) => { CONFIG.updateInterval = 30000; const state = new AppState([ { name: "Answering", url: "https://answering.test" }, @@ -251,6 +265,10 @@ test("at a 30000ms interval, a check in the first round does not show in its row t.mock.timers.tick(1000); assert.equal(await settled(round), "still waiting"); assert.equal(latencyFigure(state, answering), undefined); + assert.deepEqual(answering.history, []); + assert.equal(answering.status, "pending"); + assert.deepEqual(consoleLines("error"), []); + assert.deepEqual(consoleLines("info"), []); }); test("at a 30000ms interval, after the user pauses and resumes during a round, no row reads paused once its last check ends", async (t) => { @@ -300,11 +318,6 @@ function stateWithOneTarget() { return { state, target: state.local[0] }; } -// What this test wrote to the browser console with console[method]. -function consoleLines(method) { - return console[method].mock.calls.map((call) => call.arguments[0]); -} - test("a check that fails with a network error writes one console line with the target, the time, the error and how long the request took", async (t) => { const { state, target } = stateWithOneTarget(); mockTargets(t, () => 23, target.url);