111 lines
5.3 KiB
Go
111 lines
5.3 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"slices"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
|
"github.com/novox/mesh-controller/internal/inventory"
|
|
)
|
|
|
|
// novox/hq ADR 0353, kept behind gateFirstMachine for walks a controller before ADR 0360 started: after its first machine passes, a build goes to every other machine at once, each judged by
|
|
// its own gate and a failing one put back by itself; and a gate's readings outlive the controller that took them —
|
|
// a machine already sent what a send is for is not sent again, and its judging goes on from what was read.
|
|
//
|
|
// Seen 2026-10-14: release-1791930194184991192 sent ace, g14, shanks and the control node one after another in
|
|
// eleven minutes; the new controller its last send started sent the control node again the controller build it
|
|
// had just applied, and judged it from its first reading.
|
|
|
|
// aBacklogOnThree is the backlog fixture with a third machine, desk, running app at c1 like the others.
|
|
|
|
// events records every send and every judging, in order.
|
|
func events(t *testing.T) *[]string {
|
|
t.Helper()
|
|
var said []string
|
|
wasSend, wasGather := sendRollout, gatherGateFacts
|
|
sendRollout = func(ctx context.Context, open *stores, names []string) ([]string, error) {
|
|
said = append(said, "send "+strings.Join(names, ","))
|
|
return wasSend(ctx, open, names)
|
|
}
|
|
gatherGateFacts = func(ctx context.Context, open *stores, component string) (gateFacts, error) {
|
|
said = append(said, "judge")
|
|
return wasGather(ctx, open, component)
|
|
}
|
|
t.Cleanup(func() { sendRollout, gatherGateFacts = wasSend, wasGather })
|
|
return &said
|
|
}
|
|
|
|
// Once the first machine passed, the rest are both sent before either is judged (ADR 0353 §1); before, the second
|
|
// was judged to its verdict before the third was sent.
|
|
|
|
// A machine of the rest whose gate fails is put back at once, and the other machine of the step goes on to its own
|
|
// verdict (ADR 0353 §3); before, the walk stopped at the failure and the machine after it was never sent.
|
|
|
|
// A walk whose first machine already runs its build under another walk's gate sends nothing and goes on from that
|
|
// gate's readings (ADR 0353 §7); before, it sent the machine again and judged from its first reading.
|
|
|
|
// A walk's first send that was made and not kept — the controller replaced between the two — is not made again
|
|
// by the controller that takes over: the gate counts from that send (ADR 0353 §5, §6).
|
|
|
|
// A release's send kept before it was made, and made, is judged by the controller that takes over without being
|
|
// made again (ADR 0353 §8).
|
|
|
|
// A build no gate has passed goes to one machine of a step, its first; a machine that would be its second waits
|
|
// for the next step, and one whose every build passed goes now (ADR 0353 §2).
|
|
func TestABuildThatPassedNoGateGoesToOneMachineOfAStep(t *testing.T) {
|
|
withTheGate(t)
|
|
waiting := map[string][]inventory.CarriedMove{
|
|
"desk": {{Module: "app", To: "c2"}, {Module: "new", To: "n1"}},
|
|
"laptop": {{Module: "app", To: "c2"}, {Module: "new", To: "n1"}},
|
|
"shanks": {{Module: "app", To: "c2"}},
|
|
"novox": {{Module: "app", To: "c2"}, {Module: "ctl", To: "k1"}},
|
|
}
|
|
passed := func(module, commit string) bool { return module == "app" && commit == "c2" }
|
|
never := func(string) bool { return false }
|
|
step := restStep([]string{"desk", "laptop", "shanks", "novox"}, waiting, passed, never)
|
|
if !slices.Equal(step, []string{"desk", "shanks", "novox"}) {
|
|
t.Fatalf("the step is %v: laptop would be new's second machine before it passed", step)
|
|
}
|
|
if step := restStep([]string{"laptop"}, waiting, func(string, string) bool { return true }, never); !slices.Equal(step, []string{"laptop"}) {
|
|
t.Fatalf("a machine whose every build passed waits: %v", step)
|
|
}
|
|
}
|
|
|
|
// The node-engine and the mesh's resolver go to the rest one machine at a time, even passed (ADR 0353 §9): a step
|
|
// carries either to one machine only, while what else passed goes everywhere at once.
|
|
func TestTheNodeEngineAndTheResolverGoToTheRestOneAtATime(t *testing.T) {
|
|
withTheGate(t)
|
|
waiting := map[string][]inventory.CarriedMove{
|
|
"desk": {{Module: hostModule, To: "h2"}},
|
|
"laptop": {{Module: hostModule, To: "h2"}, {Module: "app", To: "c2"}},
|
|
"shanks": {{Module: "app", To: "c2"}},
|
|
"novox": {{Module: "dns", To: "d2"}},
|
|
"ace": {{Module: "dns", To: "d2"}},
|
|
}
|
|
resolver := catalogue.Manifest{Module: "dns", Claims: []catalogue.Claim{{Name: resolverSeat}}}
|
|
alone := func(m string) bool {
|
|
if m == "dns" {
|
|
return oneAtATime(m, resolver)
|
|
}
|
|
return oneAtATime(m, catalogue.Manifest{Module: m})
|
|
}
|
|
step := restStep([]string{"desk", "laptop", "shanks", "novox", "ace"}, waiting,
|
|
func(string, string) bool { return true }, alone)
|
|
if !slices.Equal(step, []string{"desk", "shanks", "novox"}) {
|
|
t.Fatalf("the step is %v: the node-engine and the resolver each reach one machine of it", step)
|
|
}
|
|
if oneAtATime("app", catalogue.Manifest{Module: "app"}) {
|
|
t.Fatal("a leaf module is held to one machine at a time")
|
|
}
|
|
}
|
|
|
|
// A first machine that fails still stops the walk (ADR 0236 §5, kept by ADR 0353 §3): the rest are never sent.
|
|
|
|
// The control node is sent last in a step of the rest (ADR 0353 §1): the machine holding the controller is
|
|
// ordered after every other.
|
|
|
|
// A machine of the rest that says nothing on its send is left silent after fifteen minutes: not failed, not put
|
|
// back; a rest that went silent whole ends failed, said as a condition (ADR 0353 §4).
|