Watch
0
0
Fork
You've already forked hyperhive
0

swarm-ui: a broadcast page that sends one message to several agents

Pick agents from a grid of their icons (running or all), type the text, send.
The page posts the names and the text to the controller and shows what
happened to each agent, with a retry that selects just the ones that failed.
The selection rules and the controller's answer are read by plain functions
with tests.
This commit is contained in:
iris 2026-10-18 00:03:36 +02:00 • committed by mara
commit 89b20a5a50
7 changed files with 695 additions and 0 deletions

View file

@ -0,0 +1,96 @@
// The broadcast page's selection and answer rules. Run by `npm test`, which
// Node runs with its built-in test runner and type stripping, so the `.ts`
// module is imported as it is.
import assert from "node:assert/strict";
import { test } from "node:test";
import {
failedAgents,
MAX_BROADCAST,
isRunning,
parseResults,
selectedVisible,
sendLabel,
tickAll,
toggled,
visibleAgents,
} from "../src/pages/broadcast/broadcast-types.ts";
const up = { running: true };
const down = { running: false };
const agents = [
{ name: "zed", snapshot: up },
{ name: "bob", snapshot: down },
{ name: "ann", snapshot: null },
{ name: "cy", snapshot: up },
];
test("only an agent that reported running counts as running", () => {
assert.equal(isRunning({ snapshot: up }), true);
assert.equal(isRunning({ snapshot: down }), false);
assert.equal(isRunning({ snapshot: null }), false);
});
test("the filter shows every agent or just the running ones, by name", () => {
assert.deepEqual(
visibleAgents(agents, "all").map((a) => a.name),
["ann", "bob", "cy", "zed"],
);
assert.deepEqual(
visibleAgents(agents, "running").map((a) => a.name),
["cy", "zed"],
);
});
test("a ticked agent the filter hides is not sent to, and comes back with the filter", () => {
const ticked = ["bob", "cy"];
const running = visibleAgents(agents, "running");
assert.deepEqual(selectedVisible(ticked, running), ["cy"]);
assert.deepEqual(selectedVisible(ticked, visibleAgents(agents, "all")), [
"bob",
"cy",
]);
});
test("toggling flips one name and keeps the rest", () => {
assert.deepEqual(toggled(["a", "b"], "c"), ["a", "b", "c"]);
assert.deepEqual(toggled(["a", "b"], "a"), ["b"]);
});
test("tick all adds the shown agents once", () => {
const shown = visibleAgents(agents, "running");
assert.deepEqual(tickAll(["cy", "bob"], shown), ["cy", "bob", "zed"]);
});
test("the send button says how many it goes to", () => {
assert.equal(sendLabel(0), "send");
assert.equal(sendLabel(1), "send to 1 agent");
assert.equal(sendLabel(3), "send to 3 agents");
});
test("the controller's answer is read per agent", () => {
const results = parseResults({
results: [
{ agent: "a", sent: true },
{ agent: "b", sent: false, reason: "no subscriber" },
{ agent: "c", sent: false, reason: "" },
],
});
assert.deepEqual(results, [
{ agent: "a", sent: true, reason: null },
{ agent: "b", sent: false, reason: "no subscriber" },
{ agent: "c", sent: false, reason: null },
]);
assert.deepEqual(failedAgents(results ?? []), ["b", "c"]);
});
test("an answer that is not the agreed shape is null, not an empty list", () => {
assert.equal(parseResults(null), null);
assert.equal(parseResults({}), null);
assert.equal(parseResults({ results: "x" }), null);
assert.equal(parseResults({ results: [{ agent: "a" }] }), null);
assert.equal(parseResults({ results: [null] }), null);
});
test("one broadcast may name at most 64 agents", () => {
assert.equal(MAX_BROADCAST, 64);
});