mesh/delivery-group group feat/525-leaf-budget-three-minutes checking: 0 of 2 member(s) ready
mesh/delivery superseded: a newer head of the same pull request
mesh/merge-gate pass: builds mesh-controller → novox; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed
ADR 0304 lowered a leaf module's delivery budget from five minutes to three, the time its measured phases support once a leaf is judged on one reading and waits only for what it shares.
113 lines
5.1 KiB
Go
113 lines
5.1 KiB
Go
package main
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/novox/mesh-controller/internal/broker"
|
|
"github.com/novox/mesh-controller/internal/catalogue"
|
|
"github.com/novox/mesh-controller/internal/inventory"
|
|
)
|
|
|
|
// A leaf delivery of 3 minutes 10 seconds raises delivery.leaf.over-budget naming its longest phase; one of
|
|
// exactly three minutes, a core one of nine and a class without a budget raise nothing (ADR 0282 decision 7,
|
|
// the leaf's budget as ADR 0304 decision 4 changed it).
|
|
func TestADeliveryOverItsBudgetIsLoud(t *testing.T) {
|
|
a := ×Answer{Classes: []timesClass{
|
|
{Class: inventory.ClassLeaf, Latest: ×Latest{ID: "novox/mesh-catalog@abc", TookMS: (3*time.Minute + 10*time.Second).Milliseconds(),
|
|
Longest: "judgement 2m40s"}},
|
|
{Class: inventory.ClassCore, Latest: ×Latest{ID: "novox/mesh-controller@def", TookMS: (9 * time.Minute).Milliseconds(),
|
|
Longest: "build 1m"}},
|
|
{Class: "unclassed", Latest: ×Latest{ID: "x@y", TookMS: time.Hour.Milliseconds()}},
|
|
}}
|
|
obs := overBudgetObservations(a)
|
|
if len(obs) != 1 {
|
|
t.Fatalf("one class over its budget, got %d: %+v", len(obs), obs)
|
|
}
|
|
o := obs[0]
|
|
if o.Key() != "delivery.leaf.over-budget" || !strings.Contains(o.Summary, "judgement 2m40s") ||
|
|
!strings.Contains(o.Summary, "novox/mesh-catalog@abc") {
|
|
t.Fatalf("the condition names its delivery and longest phase: %s — %s", o.Key(), o.Summary)
|
|
}
|
|
// The next leaf delivery within its budget clears it: the probe raises nothing for the class.
|
|
a.Classes[0].Latest = ×Latest{ID: "novox/mesh-catalog@ghi", TookMS: (3 * time.Minute).Milliseconds()}
|
|
if obs := overBudgetObservations(a); len(obs) != 0 {
|
|
t.Fatalf("a delivery of exactly its budget is within it: %+v", obs)
|
|
}
|
|
a.Classes[1].Latest.TookMS = (10*time.Minute + time.Second).Milliseconds()
|
|
if obs := overBudgetObservations(a); len(obs) != 1 || obs[0].Key() != "delivery.core.over-budget" {
|
|
t.Fatalf("a core delivery over ten minutes: %+v", obs)
|
|
}
|
|
if obs := overBudgetObservations(nil); obs != nil {
|
|
t.Fatal("no answer raises nothing")
|
|
}
|
|
// No delivery of a class with a known time yet: nothing said of it.
|
|
if obs := overBudgetObservations(×Answer{Classes: []timesClass{{Class: inventory.ClassLeaf}}}); len(obs) != 0 {
|
|
t.Fatalf("a class with no delivery: %+v", obs)
|
|
}
|
|
}
|
|
|
|
// The probe's question is one the controller's grant names: a question the bus refuses checks nothing.
|
|
func TestTheControllerMayAskForTheDeliveryTimes(t *testing.T) {
|
|
found := false
|
|
for _, v := range broker.VerbsTheControllerAsksTheDeliveryOwner {
|
|
found = found || (v.Seat == catalogue.DeliverySeat && v.Verb == "times")
|
|
}
|
|
if !found {
|
|
t.Fatal("the grant does not name mesh-delivery.times")
|
|
}
|
|
}
|
|
|
|
// A walk's class is core when it walks a module of the controller's own path or one holding the mesh's resolver,
|
|
// leaf otherwise; its window closed at its batch's window, or its maximum, never after its cut.
|
|
func TestAWalksClassAndWindowAreReadAtItsCut(t *testing.T) {
|
|
entries := []inventory.Entry{
|
|
{Manifest: catalogue.Manifest{Module: "dnsmasq", Claims: []catalogue.Claim{{Name: resolverSeat}}}},
|
|
{Manifest: catalogue.Manifest{Module: "gitea"}},
|
|
}
|
|
walk := func(modules ...string) inventory.Plan {
|
|
p := inventory.Plan{Modules: map[string]*inventory.PlanModule{}}
|
|
for _, m := range modules {
|
|
p.Modules[m] = &inventory.PlanModule{}
|
|
}
|
|
return p
|
|
}
|
|
for want, w := range map[string]inventory.Plan{
|
|
inventory.ClassLeaf: walk("gitea"), inventory.ClassCore: walk("gitea", "mesh-controller"),
|
|
} {
|
|
if got := classOf(w, entries); got != want {
|
|
t.Errorf("%v: %s, want %s", w.Modules, got, want)
|
|
}
|
|
}
|
|
if got := classOf(walk("dnsmasq"), entries); got != inventory.ClassCore {
|
|
t.Errorf("the resolver's holder is core: %s", got)
|
|
}
|
|
now := time.Date(2026, 10, 10, 18, 0, 0, 0, time.UTC)
|
|
batch := inventory.Plan{Delivery: &inventory.PlanDelivery{Batch: &inventory.PlanBatch{
|
|
ClosesAt: now.Add(-20 * time.Second), AtMost: now.Add(5 * time.Minute)}}}
|
|
times := walkTimesAtCut(batch, walk("gitea"), entries, now)
|
|
if times.Cut == nil || !times.Cut.Equal(now) || times.WindowClosed == nil || !times.WindowClosed.Equal(now.Add(-20*time.Second)) ||
|
|
times.Class != inventory.ClassLeaf {
|
|
t.Fatalf("times at the cut: %+v", times)
|
|
}
|
|
batch.Delivery.Batch.AtMost = now.Add(-time.Minute)
|
|
if times := walkTimesAtCut(batch, walk("gitea"), entries, now); !times.WindowClosed.Equal(now.Add(-time.Minute)) {
|
|
t.Fatalf("a window closed at its maximum: %v", times.WindowClosed)
|
|
}
|
|
if times := walkTimesAtCut(inventory.Plan{Delivery: &inventory.PlanDelivery{Alone: true}}, walk("gitea"), entries, now); times.WindowClosed != nil {
|
|
t.Fatalf("a merge walked alone had no window: %v", times.WindowClosed)
|
|
}
|
|
}
|
|
|
|
// What each machine of the rest was sent is kept only for the machines the send reached.
|
|
func TestTheRestIsKeptForTheMachinesTheSendReached(t *testing.T) {
|
|
got := restOf([]string{"ace", "g14"}, []string{"ace", "shanks"}, map[string]inventory.SentDeclaration{"ace": {Digest: "d1"}})
|
|
if len(got) != 1 || got["ace"].Digest != "d1" {
|
|
t.Fatalf("rest: %+v", got)
|
|
}
|
|
if restOf([]string{"g14"}, []string{"ace"}, nil) != nil {
|
|
t.Fatal("no machine reached: nothing kept")
|
|
}
|
|
}
|