Files
mesh-controller/cmd/mesh-controller/silent_mid_tier_test.go
T

86 lines
3.7 KiB
Go

package main
import (
"slices"
"testing"
"time"
"github.com/novox/mesh-controller/internal/inventory"
)
// A machine silent past the bound at a tier between the first and the last is named silent and left as it was, as the
// last tier's is (novox/hq ADR 0282 §1, ADR 0360 §4-5, issue 1160): one rule, walkReports, read by the walk's end and by
// the gates between tiers.
func silentWalk(now time.Time) (inventory.Plan, []inventory.Reported) {
sent := now.Add(-time.Minute)
long := now.Add(-inventory.ApplySilentAfter - time.Minute)
at := now.Add(-10 * time.Second)
p := inventory.Plan{Tier: 0, Tiers: [][]string{{"engine"}, {"app"}}, Modules: map[string]*inventory.PlanModule{
"engine": {Rest: map[string]inventory.SentDeclaration{
"anchor": {Digest: "d-anchor", At: sent},
"g14": {Digest: "d-g14", At: long},
"fresh": {Digest: "d-fresh", At: sent}}},
"app": {}}}
reports := []inventory.Reported{{Node: "anchor", Outcome: inventory.OutcomeApplied, At: &at, Declared: "d-anchor"}}
return p, reports
}
func needsOf(running ...string) []gateNeed {
return []gateNeed{{module: "engine", running: running, since: func(string) time.Time { return time.Time{} }}}
}
// 1160/AC1: at a middle tier the machine silent past the bound is named, not waited for, and the plan goes on with the
// machines that reported; one still inside the bound is waited for.
func TestAMiddleTierGateNamesTheSilentMachineAndGoesOnWithThoseThatReported(t *testing.T) {
now := time.Now().UTC()
p, reports := silentWalk(now)
silent := silentNodes(p, reports, now, nil)
waiting, quiet, lost := settleTierGate(needsOf("anchor", "g14"), reports, silent)
if len(waiting) != 0 || !slices.Equal(quiet, []string{"g14"}) || lost {
t.Fatalf("1160/AC1: waiting %v, silent %v, lost %v; want g14 named silent and nothing waited for", waiting, quiet, lost)
}
waiting, quiet, _ = settleTierGate(needsOf("anchor", "g14", "fresh"), reports, silent)
if len(waiting) != 1 || waiting[0] != "engine on fresh" || !slices.Equal(quiet, []string{"g14"}) {
t.Fatalf("1160/AC1: a machine inside the bound is still waited for: waiting %v, silent %v", waiting, quiet)
}
}
// 1160/AC2: the walk's end and the gate read the same silent machines.
func TestTheGateAndTheWalksEndShareOneRuleOfSilence(t *testing.T) {
now := time.Now().UTC()
p, reports := silentWalk(now)
_, _, ended := walkReports(p, reports, now, nil)
got := silentNodes(p, reports, now, nil)
if len(got) != len(ended) || !got["g14"] || !slices.Equal(ended, []string{"g14"}) {
t.Fatalf("1160/AC2: the gate reads %v silent, the walk's end %v", got, ended)
}
}
// 1160/AC3: a later tier is not sent to a machine silent.
func TestALaterTierIsNotSentToASilentMachine(t *testing.T) {
got := withoutSilent([]string{"anchor", "g14"}, map[string]bool{"g14": true})
if !slices.Equal(got, []string{"anchor"}) {
t.Fatalf("1160/AC3: a later tier goes to %v; want anchor alone", got)
}
in := []string{"anchor", "g14"}
withoutSilent(in, map[string]bool{"g14": true})
if !slices.Equal(in, []string{"anchor", "g14"}) {
t.Fatalf("1160/AC3: the machines running it were changed: %v", in)
}
}
// 1160/AC4: every machine the gate waits on silent: the plan is lost, not gone on as if delivered.
func TestAGateWhoseEveryMachineIsSilentIsLost(t *testing.T) {
now := time.Now().UTC()
p, reports := silentWalk(now)
reports = nil
p.Modules["engine"].Rest = map[string]inventory.SentDeclaration{
"g14": {Digest: "d", At: now.Add(-time.Hour)}, "laptop": {Digest: "d", At: now.Add(-time.Hour)}}
silent := silentNodes(p, reports, now, nil)
_, quiet, lost := settleTierGate(needsOf("g14", "laptop"), reports, silent)
if !lost || !slices.Equal(quiet, []string{"g14", "laptop"}) {
t.Fatalf("1160/AC4: lost %v, silent %v; want both named and the gate lost", lost, quiet)
}
}