Files
mesh-delivery/sdk/heard/heard_test.go
T
jschoubben d4598f5772 F-delivery 4: a consumer that does not hear its family is a finding until it binds
The indexer's, the assigner's, the builder's and the watchman's consumers that were not there or could not be
heard only said so in the log, so the part stayed healthy while it heard nothing (ADR 0615 §5). sdk/heard is
the one shared helper: the finding family-not-heard names the observer and the family, written when the
consumer does not bind and deleted when it does.

Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
2026-11-04 16:34:02 +01:00

55 lines
2.1 KiB
Go

package heard
import (
"errors"
"strings"
"testing"
"time"
"git.novox.be/novox/mesh-sdk/go/health"
)
// Review round 1 of the registry milestone (ADR 0615 §5): a consumer that does not bind is a finding naming the observer
// and the family, with why; binding deletes it; no findings state still says it in the log.
func TestAConsumerNotHeardIsAFindingUntilItBinds(t *testing.T) {
at := time.Date(2026, 11, 4, 17, 0, 0, 0, time.UTC)
for _, row := range []struct {
name string
bindErr error
noState bool
want string // the finding's summary, or "" when none stands
}{
{name: "not there yet", bindErr: errors.New("consumer not found"),
want: "mesh-delivery-indexer does not hear assigner-views: the placement is the first view's alone (consumer not found)"},
{name: "bound"},
{name: "no findings state", bindErr: errors.New("consumer not found"), noState: true},
} {
kept := map[string][]byte{Key("mesh-delivery-indexer", "assigner-views"): []byte("{}")}
var logs []string
w := Writer{Observer: "mesh-delivery-indexer", Now: func() time.Time { return at },
Logf: func(f string, a ...any) { logs = append(logs, f) }}
if !row.noState {
w.Put = func(k string, v []byte) error { kept[k] = v; return nil }
w.Delete = func(k string) error { delete(kept, k); return nil }
}
err := w.Bind("assigner-views", "the placement is the first view's alone", func() error { return row.bindErr })
if !errors.Is(err, row.bindErr) {
t.Errorf("%s: Bind answered %v, want %v", row.name, err, row.bindErr)
}
raw, stands := kept[Key("mesh-delivery-indexer", "assigner-views")]
switch {
case row.noState:
if len(logs) == 0 {
t.Errorf("%s: nothing said", row.name)
}
case row.want == "" && stands:
t.Errorf("%s: the finding stands once bound", row.name)
case row.want != "":
f, err := health.DecodeFinding(raw)
if err != nil || f.Kind != Kind || f.Summary != row.want || f.Observer != "mesh-delivery-indexer" || !strings.Contains(f.Subject.ID, "assigner-views") {
t.Errorf("%s: the finding %+v (%v), want summary %q", row.name, f, err, row.want)
}
}
}
}