1 Commits
Author SHA1 Message Date
clawbot f5b4800951 Hetzner checks answer again; failed checks go to the console (closes #114)
check / check (push) Canceled after 0s
The Hetzner speed-test servers close the connection without an answer
when the URL has a query string, and every check added ?_cb= and the
time, so all six showed unreachable. Checks now fetch each target's URL
as written; cache: "no-store" still keeps the browser's cache out of the
measurement.

Each recorded check that fails writes one console.error line, and the
same line to the debug log: the target's name and URL, the time, what
failed and how long the request took. A target that answers after
failed checks writes one console.info line. Checks the page does not
record write nothing, so the debug log no longer lists failures in the
first round or the recovery probe.

Model: opus-5-5
2026-10-07 10:01:01 +00:00
2 changed files with 20 additions and 5 deletions
+2 -2
View File
@@ -574,15 +574,15 @@ export async function measureLatency(url, signal) {
}
return { latency, error: null, reason: null };
} catch (err) {
const took = Math.round(performance.now() - start);
clearTimeout(timeoutId);
if (err.name === "AbortError") {
return {
latency: null,
error: "timeout",
reason: `timed out after ${CONFIG.requestTimeout} ms`,
reason: `timed out after ${took} ms (limit ${CONFIG.requestTimeout} ms)`,
};
}
const took = Math.round(performance.now() - start);
return {
latency: null,
error: "unreachable",
+18 -3
View File
@@ -131,11 +131,26 @@ for (const interval of [10000, 30000]) {
assert.deepEqual(await settled(check), {
latency: null,
error: "timeout",
reason: `timed out after ${timeout} ms`,
reason: `timed out after ${timeout} ms (limit ${timeout} ms)`,
});
});
}
// A browser can run a timer late, on a busy page or in a background tab.
// Moving the mocked clock on 30000ms at once runs the 24000ms timeout with
// the clock already at 30000ms.
test("at a 30000ms interval, a check whose timeout runs late gives how long the request took and the time limit", async (t) => {
CONFIG.updateInterval = 30000;
mockTargets(t, () => Infinity);
const check = measureLatency("https://target.test");
t.mock.timers.tick(30000);
assert.deepEqual(await settled(check), {
latency: null,
error: "timeout",
reason: "timed out after 30000 ms (limit 24000 ms)",
});
});
test("a check fetches the target's URL as written, with no query string added", async (t) => {
mockTargets(t, () => 10);
const check = measureLatency("https://fsn1-speed.hetzner.com");
@@ -289,14 +304,14 @@ test("a check that fails with a network error writes one console line with the t
assert.deepEqual(consoleLines("info"), []);
});
test("a check that times out writes one console line with the target, the time and the time limit", async (t) => {
test("a check that times out writes one console line with the target, the time, how long the request took and the time limit", async (t) => {
const { state } = stateWithOneTarget();
mockTargets(t, () => Infinity);
const round = tick(state, new AbortController().signal);
t.mock.timers.tick(24000);
assert.notEqual(await settled(round), "still waiting");
assert.deepEqual(consoleLines("error"), [
"netwatch: check failed: Target https://target.test at 1970-01-01T00:00:24.000Z: timed out after 24000 ms",
"netwatch: check failed: Target https://target.test at 1970-01-01T00:00:24.000Z: timed out after 24000 ms (limit 24000 ms)",
]);
assert.deepEqual(consoleLines("info"), []);
});