Files
mesh-controller/internal/link/advisories_test.go
T
jochen 8ddc019cd2 Grant the self-check its ban-list question, say a refusal at once, judge the engine by its delivered version (hq to-be 45 Phase 1)
Live on 2026-10-06, two of the first self-check's findings were its own:

- D8 asked every machine's node-intrusion-prevention.banned, and the
  controller's grant did not name the subject: the bus refused it 24 times
  and D8 timed out after thirty seconds instead of saying so. The verbs the
  self-check asks are named in broker.VerbsTheSelfCheckAsks and granted
  (mesh.seat.<seat>.tool.<verb>.*); each probe declares the seat verbs it
  calls, askSeatTool refuses an undeclared one, and a test over the
  registry fails a probe whose question the controller is not granted.
  AskSeatTool now returns a refused publish at once ("the bus refused…")
  instead of waiting out its timeout; D8 asks the machines in parallel.
- D10 read every machine as behind right after a push: a node-engine says
  its version as the directory it is delivered into, the archive's digest
  (31045596c83a, catalogue versionOf), and D10 compared that with the
  build's commit (1545b00a). It now compares with the versions the
  registered build is delivered as, and a hand-placed engine's commit.
2026-10-06 10:44:38 +02:00

93 lines
3.2 KiB
Go

package link
import (
"fmt"
"os"
"strings"
"testing"
"time"
"github.com/nats-io/nats.go"
)
// **A reader's own consumer gone is not a consumer lost**: every bucket watch and stream read-back
// makes and deletes one, many a minute, and the bus says so each time (found running the controller
// against a real bus: five a tick).
func TestOnlyTheMeshsOwnConsumersAreSaidLost(t *testing.T) {
deleted := func(stream, consumer string) bool {
_, ok := ReadAdvisory("$JS.EVENT.ADVISORY.CONSUMER.DELETED."+stream+"."+consumer,
[]byte(`{"type":"io.nats.jetstream.advisory.v1.consumer_action","stream":"`+stream+`","consumer":"`+consumer+`","action":"delete"}`))
return ok
}
for _, c := range [][2]string{{"EVENTS", "controller"}, {"CONTROL", "controller"}, {"NODES", "anchor"},
{"EVENTS", "anchor_shop"}, {"SEAT_NODE_BUILD_AGENT", "SEAT_NODE_BUILD_AGENT_worker"}} {
if !deleted(c[0], c[1]) {
t.Errorf("%s on %s deleted is not said", c[1], c[0])
}
}
for _, c := range [][2]string{{"KV_mesh-controller_conditions", "381UWW5Y"}, {"EVENTS", "E7vVYoe6"},
{"SEAT_NODE_BUILD_AGENT", "Rcnt6jla"}, {"ASSIGNMENTS", "x"}} {
if deleted(c[0], c[1]) {
t.Errorf("a reader's own consumer %s on %s is said lost", c[1], c[0])
}
}
a, ok := ReadAdvisory("$JS.EVENT.ADVISORY.CONSUMER.MAX_DELIVERIES.EVENTS.anchor_shop",
[]byte(`{"stream":"EVENTS","consumer":"anchor_shop","stream_seq":7,"deliveries":5}`))
if !ok || a.Kind != AdvisoryMaxDeliveries || a.ID != "EVENTS.anchor_shop" ||
a.Said != "how shop on anchor hears what it consumes handed message 7 over 5 times and gave up on it: "+
"it will not be delivered again, and what it asked for was not done" {
t.Fatalf("%+v", a)
}
}
// **A question the bus refuses is answered as refused at once**, not after its timeout: against a
// server whose only user may not publish where it asks.
func TestNatsARefusedQuestionIsSaidAtOnce(t *testing.T) {
url := os.Getenv("MESH_TEST_NATS_REFUSING")
if url == "" {
t.Skip("MESH_TEST_NATS_REFUSING unset: a server whose user may not publish mesh.seat.>")
}
conn, err := nats.Connect(url)
if err != nil {
t.Fatal(err)
}
defer conn.Close()
started := time.Now()
_, err = AskSeatTool(t.Context(), conn, "node-intrusion-prevention", "banned", "anchor", map[string]any{}, 10*time.Second)
if err == nil || !strings.Contains(err.Error(), "the bus refused") {
t.Fatalf("answered %v", err)
}
if took := time.Since(started); took > 3*time.Second {
t.Fatalf("a refusal took %s to be known", took)
}
}
// The refusal reaches whoever waits on that subject, and only them.
func TestNatsARefusalReachesItsWaiter(t *testing.T) {
url := os.Getenv("MESH_TEST_NATS")
if url == "" {
t.Skip("MESH_TEST_NATS unset")
}
conn, err := nats.Connect(url)
if err != nil {
t.Fatal(err)
}
defer conn.Close()
mine, stop := refusalsOf(conn, "mesh.seat.s.tool.v.anchor")
defer stop()
other, stopOther := refusalsOf(conn, "mesh.seat.s.tool.v.laptop")
defer stopOther()
conn.ErrorHandler()(conn, nil, fmt.Errorf("%w: Permissions Violation for Publish to %q",
nats.ErrPermissionViolation, "mesh.seat.s.tool.v.anchor"))
select {
case <-mine:
case <-time.After(time.Second):
t.Fatal("the waiter was not told")
}
select {
case <-other:
t.Fatal("another subject's waiter was told")
default:
}
}