A controller restart lost every call's outcome, `status` composed the mesh while its caller waited (18.6s live on 2026-10-06, past the 10s window), a repair by hand left no trace, and the core's bounds had nothing measured to be set from. - calls: kept in the controller's bucket mesh-controller_calls (last 1000 or 14 days, answers bounded to 64 KiB), read by id across a restart; a controller starting marks a stopped one's running calls abandoned; each call names its caller from the inbox its answer goes to. - status: the serving controller composes it at start, after news from a machine, a build or an acting verb, and every minute; the verb answers the last composition at once with when and how long it took. Composing resolves each machine once instead of twice. - hand-act log in mesh-controller_hand-acts: push (required through the seat), plans stop/close, broker consumer-reset and the new hand-act record take --why/--cause/--condition; `hand-acts` lists them and repeated causes; status counts the week's. - durations (migration 0066): apply (send to first report), heartbeat gap, plan tier and build, recorded as heard; `durations` summarises them. - the controller's seat row takes this binary's definition of its own verbs, so the console no longer judges calls against an older build's schema. - the controller is granted its two buckets' subjects.
98 lines
3.3 KiB
Go
98 lines
3.3 KiB
Go
package inventory
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// **The durations the bounds are set from are recorded once each** (novox/hq to-be 45 Phase 0): a
|
|
// send measured at its first report and not again at every reconcile that repeats it; a send of
|
|
// something else measures nothing; a new send is a new measurement.
|
|
func TestAnApplyIsMeasuredOncePerSend(t *testing.T) {
|
|
inv := ForTest(t)
|
|
ctx := t.Context()
|
|
node, err := inv.AddNode(ctx, "anchor")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := inv.RecordSent(ctx, node.ID, "d1", nil); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for range 3 { // the report, then two reconciles saying the same
|
|
if err := inv.RecordApplyDuration(ctx, "anchor", "d1", OutcomeApplied); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
if err := inv.RecordApplyDuration(ctx, "anchor", "d0", OutcomeApplied); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ds, err := inv.Durations(ctx, DurationApply, time.Now().Add(-time.Hour))
|
|
if err != nil || len(ds) != 1 || ds[0].Subject != "anchor" || ds[0].Detail != OutcomeApplied || ds[0].Took < 0 {
|
|
t.Fatalf("%v %+v", err, ds)
|
|
}
|
|
time.Sleep(10 * time.Millisecond)
|
|
if err := inv.RecordSent(ctx, node.ID, "d2", nil); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := inv.RecordApplyDuration(ctx, "anchor", "d2", OutcomeFailed); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if ds, _ = inv.Durations(ctx, "", time.Now().Add(-time.Hour)); len(ds) != 2 {
|
|
t.Fatalf("a second send was not measured: %+v", ds)
|
|
}
|
|
}
|
|
|
|
// A machine's silence is the time since its last word, once per word.
|
|
func TestASilenceIsMeasuredFromTheLastWord(t *testing.T) {
|
|
inv := ForTest(t)
|
|
ctx := t.Context()
|
|
before := time.Now().Add(-90 * time.Second)
|
|
for range 2 {
|
|
if err := inv.RecordHeartbeatGap(ctx, "anchor", before); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
if err := inv.RecordHeartbeatGap(ctx, "anchor", time.Time{}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ds, err := inv.Durations(ctx, DurationHeartbeatGap, time.Now().Add(-time.Hour))
|
|
if err != nil || len(ds) != 1 || ds[0].Took < 90*time.Second || ds[0].Took > 2*time.Minute {
|
|
t.Fatalf("%v %+v", err, ds)
|
|
}
|
|
}
|
|
|
|
// A plan's tier is measured when the plan leaves it — moving on, or ending — and only then.
|
|
func TestAPlansTierIsMeasuredWhenItIsLeft(t *testing.T) {
|
|
inv := ForTest(t)
|
|
ctx := t.Context()
|
|
p := Plan{ID: "plan-1", Repository: "novox/mesh-tools", Commit: "abc", Created: time.Now().UTC(),
|
|
State: PlanBuilding, Tiers: [][]string{{"mesh-tools"}, {"builder"}},
|
|
Modules: map[string]*PlanModule{"mesh-tools": {}, "builder": {}}}
|
|
if err := inv.SavePlan(ctx, p); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
p.State = PlanRolling // the same tier, saved again
|
|
if err := inv.SavePlan(ctx, p); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if ds, _ := inv.Durations(ctx, DurationPlanTier, time.Now().Add(-time.Hour)); len(ds) != 0 {
|
|
t.Fatalf("a tier not left was measured: %+v", ds)
|
|
}
|
|
p.Tier = 1
|
|
if err := inv.SavePlan(ctx, p); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
p.State = PlanDone
|
|
if err := inv.SavePlan(ctx, p); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := inv.SavePlan(ctx, p); err != nil { // saved again once ended: nothing more to measure
|
|
t.Fatal(err)
|
|
}
|
|
ds, err := inv.Durations(ctx, DurationPlanTier, time.Now().Add(-time.Hour))
|
|
if err != nil || len(ds) != 2 || ds[0].Subject != "novox/mesh-tools" || ds[0].Ref != "plan-1/tier-0" ||
|
|
ds[1].Ref != "plan-1/tier-1" {
|
|
t.Fatalf("%v %+v", err, ds)
|
|
}
|
|
}
|