Files
mesh-catalog/modules/messenger/cmd/messenger/condition_test.go
T
jschoubben a8c1c275dd messenger hears and asks the condition keeper alone, never the controller (ADR 0564 clause 9)
The three mesh-controller condition-event consumes, the invoke of mesh-controller.conditions, the follow switch
(takeover.go) and its read of mesh-conditions.takeover are gone: the messenger consumes mesh-conditions' events and
reads its open conditions from seat mesh-conditions.conditions at its start. An ask of the keeper is shown on a
condition's message like the controller's was. The manifest test now refuses any controller entry; the event test
refuses the controller's events; the words fixture's action verb names the keeper's. No test added.

(issue 774, 774/S12)

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 00:19:58 +01:00

100 lines
3.9 KiB
Go

package main
import (
"strings"
"testing"
"time"
)
// The contract with the condition keeper's condition events (to-be 45 §2), as condition.go states it.
func TestTheEventsAreTheKeepersSeat(t *testing.T) {
for key, want := range map[string]string{
"mesh-conditions.condition-raised": EventRaised,
"mesh-conditions.condition-changed": EventChanged,
"mesh-conditions.condition-cleared": EventCleared,
} {
if got, ok := eventOf(key); !ok || got != want {
t.Errorf("%s: %q %v", key, got, ok)
}
}
for _, key := range []string{"mesh-controller.condition-raised", "mesh-conditions.applied", "gitea.condition-raised", "condition-raised"} {
if _, ok := eventOf(key); ok {
t.Errorf("%s read as a condition event", key)
}
}
}
func TestAConditionAsTheStoreHoldsIt(t *testing.T) {
body := `{
"key": "machine.ace.silent", "kind": "silent",
"subject": {"scope": "machine", "id": "ace", "machine": "ace"},
"severity": "urgent", "summary": "the home server has not been heard for 15 min",
"evidence": [{"at": "2026-10-06T12:00:00Z", "what": "last heartbeat"}],
"source": "S1", "raised": "2026-10-06T12:15:00Z", "last-observed": "2026-10-06T12:16:00Z",
"observations": 3, "tried": [], "resolver": "self",
"silenced": {"until": "2026-10-06T14:00:00Z", "by": "operator", "why": "moving it"},
"epoch": 57
}`
c, err := DecodeCondition(EventRaised, []byte(body))
if err != nil {
t.Fatal(err)
}
if c.Key != "machine.ace.silent" || c.Kind != "silent" || c.Scope != "machine" || c.ID != "ace" ||
c.Severity != Urgent || c.Source != "S1" || c.Observations != 3 || c.Resolver != "self" {
t.Fatalf("%+v", c)
}
if !c.Raised.Equal(time.Date(2026, 10, 6, 12, 15, 0, 0, time.UTC)) || c.LastObserved.IsZero() {
t.Fatalf("times: %+v", c)
}
if !c.SilencedAt(time.Date(2026, 10, 6, 13, 0, 0, 0, time.UTC)) || c.SilencedAt(time.Date(2026, 10, 6, 15, 0, 0, 0, time.UTC)) {
t.Fatalf("silenced: %v", c.SilencedTill)
}
if c.SubjectWords() != "machine ace" {
t.Fatalf("subject words: %q", c.SubjectWords())
}
}
func TestOtherSpellingsAndAKeyMadeFromItsParts(t *testing.T) {
c, err := DecodeCondition(EventChanged, []byte(`{"kind":"stalled","subject":{"scope":"plan","id":"41"},
"severity":"warning","summary":"plan 41 waits","last_observed":"2026-10-06T12:00:00Z","silenced":null,
"source":{"row":"S3"}}`))
if err != nil {
t.Fatal(err)
}
if c.Key != "plan.41.stalled" || c.Source != "S3" || c.LastObserved.IsZero() || !c.SilencedTill.IsZero() {
t.Fatalf("%+v", c)
}
c, err = DecodeCondition(EventRaised, []byte(`{"key":"provider.keycloak.ace.gitea.failing","subject":"keycloak",
"severity":"warning","summary":"the identity provider fails gitea"}`))
if err != nil || c.Scope != "provider" || c.Kind != "failing" || c.ID != "keycloak" {
t.Fatalf("%+v %v", c, err)
}
}
func TestAClearedEventNeedsOnlyItsKey(t *testing.T) {
c, err := DecodeCondition(EventCleared, []byte(`{"key":"machine.ace.silent","cleared":"2026-10-06T12:30:00Z"}`))
if err != nil || c.Key != "machine.ace.silent" || c.Cleared.IsZero() {
t.Fatalf("%+v %v", c, err)
}
}
func TestWhatCannotBeReadIsRefusedByName(t *testing.T) {
for body, says := range map[string]string{
`not json`: "not a JSON object",
`null`: "null",
`{"severity":"urgent","summary":"x"}`: "no key",
`{"key":"a.b.c","summary":"x"}`: "no severity",
`{"key":"a.b.c","severity":"critical","summary":"x"}`: "neither urgent nor warning",
`{"key":"a.b.c","severity":"urgent"}`: "no summary",
`{"key":"a.b.c","severity":"urgent","summary":"x","raised":"yesterday"}`: "not an RFC 3339 time",
`{"key":7}`: "key is not a string",
`{"key":"a.b.c","severity":"urgent","summary":"x","silenced":{"until":"soon"}}`: "silenced.until",
} {
_, err := DecodeCondition(EventRaised, []byte(body))
if err == nil || !strings.Contains(err.Error(), says) {
t.Errorf("%s: %v, want %q", body, err, says)
}
}
}