90 lines
2.9 KiB
Go
90 lines
2.9 KiB
Go
//go:build integration
|
|
|
|
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/nats-io/nats.go/jetstream"
|
|
|
|
"github.com/novox/mesh-controller/internal/broker"
|
|
"github.com/novox/mesh-controller/internal/conditions"
|
|
"github.com/novox/mesh-controller/internal/inventory"
|
|
"github.com/novox/mesh-controller/internal/testbus"
|
|
)
|
|
|
|
// **Looks at an unchanged condition write the bucket once, and `conditions` still says how often and when
|
|
// it was seen** (novox/hq issue 420): the looks are in the controller's store, and the verb, run as a
|
|
// process of its own, reads them from there. On 2026-10-10 one open condition had rewritten the bucket 7 003
|
|
// times in two days.
|
|
func TestTheConditionsVerbReadsTheLooksTheBucketNoLongerHolds(t *testing.T) {
|
|
inv := inventory.ForTest(t)
|
|
bus := testbus.Start(t)
|
|
serving, err := broker.Dial(bus.ClientURL())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer serving.Close()
|
|
if err := serving.EnsureControllerBuckets(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ctx := t.Context()
|
|
store, history, err := conditions.OnTheBus(ctx, serving.Conn())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
k := conditions.NewKeeper(ctx, conditions.Options{Store: store, History: history, Looks: storeLooks{inv}})
|
|
defer k.Close(context.Background())
|
|
look := conditions.Observation{Scope: conditions.ScopeMachine, ID: "anchor", Kind: "silent", Machine: "anchor",
|
|
Severity: conditions.Warning, Summary: "anchor has not been heard from", Source: "S1"}
|
|
for range 4 {
|
|
if _, err := k.Observe(ctx, look); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
api, err := jetstream.New(serving.Conn())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
stream, err := api.Stream(ctx, "KV_"+broker.ConditionsBucket)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
info, err := stream.Info(ctx)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if info.State.LastSeq != 1 {
|
|
t.Fatalf("four looks at one unchanged condition wrote the bucket %d times, want 1", info.State.LastSeq)
|
|
}
|
|
|
|
// The verb as a process other than the serving controller: it reads the bucket and the store.
|
|
keptBefore := conditionsFrom
|
|
conditionsFrom = nil
|
|
defer func() { conditionsFrom = keptBefore }()
|
|
t.Setenv(broker.NATSVar, bus.ClientURL())
|
|
t.Setenv(broker.CertificateVar, "")
|
|
var shown bytes.Buffer
|
|
if err := showCondition(ctx, []string{"machine.anchor.silent", "--json"}, &shown); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var c conditions.Condition
|
|
if err := json.Unmarshal(shown.Bytes(), &c); err != nil {
|
|
t.Fatalf("%v: %s", err, shown.String())
|
|
}
|
|
if c.Observations != 4 || len(c.Evidence) != 4 || c.LastObserved.IsZero() || c.LooksUnread != "" ||
|
|
time.Since(c.LastObserved) > time.Minute {
|
|
t.Fatalf("conditions show reads %d observation(s), %d evidence line(s), last %s (%q)", c.Observations,
|
|
len(c.Evidence), c.LastObserved, c.LooksUnread)
|
|
}
|
|
listed, err := openConditions(ctx)
|
|
if err != nil || len(listed) != 1 || listed[0].Observations != 4 {
|
|
t.Fatalf("conditions lists %+v: %v", listed, err)
|
|
}
|
|
}
|