Files
mesh-catalog/modules/mesh-delivery/cmd/mesh-delivery/table_test.go
T
jochen d1de0edd4a
mesh/merge-gate pass: builds mesh-delivery → 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
mesh/delivery delivered
Ask the check of a delivery that waits for one nobody asked (hq issue 290)
A pull request's head announced again from the bus's history at
mesh-delivery's first start was proposed with no verdict and nothing
ever asked its check: the controller had taken that announcement long
before, and stalled raised it after an hour for the operator.

A proposed delivery with no verdict and no check asked is now asked
through delivery-check once it has waited past a grace longer than a
check takes; an announcement carrying its head's decided gate status
takes it. The proposed bound runs from the ask, and H2's close may
re-ask once (a table row) before the delivery is the operator's.
2026-10-07 11:44:21 +02:00

213 lines
8.3 KiB
Go

package main
import (
"errors"
"testing"
"time"
)
// The table walked (novox/hq ADR 0239 decision 2): every row it holds can be taken when its guard holds and
// is refused when it does not; every pair it does not hold is refused, by name; and the rules the table
// exists for are true of the table itself, not of the code around it.
// aDeliveryFor is a delivery whose facts make the row's guard hold.
func aDeliveryFor(r Row) (*Delivery, Facts) {
now := time.Date(2026, 10, 6, 12, 0, 0, 0, time.UTC)
d := &Delivery{ID: "novox/app@c0ffee000000", Repository: "novox/app", Commit: "c0ffee000000", Number: 7}
f := Facts{Now: now}
switch r.Event {
case EvAppeared:
d.MergedAs, d.HeldWhy = "c0ffee000000", "no pull request"
case EvAdopted, EvGo:
d.Walk = &WalkSeen{ID: "plan-1", State: walkRolling}
if r.From[0] == Held {
d.Walk.WaitedFor, d.Walk.GoBy = "mesh-delivery", "a person (jochen)"
}
case EvNewHead:
d.NewerHead = "beef"
case EvClosed:
d.ClosedUnmerged = true
case EvMerged:
d.MergedAs, d.Walk = "aaaa1111", &WalkSeen{ID: "plan-1", State: walkBuilding, Waits: true, WaitedFor: "mesh-delivery"}
case EvMergedUnchecked:
d.MergedAs = "aaaa1111"
case EvChecked, EvAccepted:
d.Check = &Verdict{Gate: "pass"}
case EvRefused:
d.Check = &Verdict{Gate: "fail"}
case EvRecheck, EvRelease, EvStop:
f.Why, f.By = "a person's reason", "jochen"
if r.Event == EvStop && !r.Act {
f = Facts{Now: now}
d.Walk = &WalkSeen{ID: "plan-1", State: walkFailed, StoppedBy: "mesh-delivery for jochen"}
}
case EvReask:
f.Why, f.By = "delivery stalled", byH2
d.CheckAsk = &CheckAsk{ID: "check-1", At: now.Add(-2 * time.Hour), By: byOwner}
case EvHold:
d.HeldWhy, d.MergedAs = "its group's check did not pass", "aaaa1111"
case EvDone:
if r.From[0] == Published {
d.Plan = &DeliveryPlan{Summary: "moves nothing"}
} else {
d.Walk = &WalkSeen{ID: "plan-1", State: walkDone}
}
case EvFailed:
d.Walk = &WalkSeen{ID: "plan-1", State: walkFailed}
case EvSuperseded:
d.Walk = &WalkSeen{ID: "plan-1", State: walkSuperseded}
}
return d, f
}
func TestEveryRowOfTheTableIsTakenWhenItsGuardHoldsAndRefusedWhenNot(t *testing.T) {
for _, r := range Table {
for _, from := range r.From {
d, f := aDeliveryFor(r)
d.State = from
got, err := Apply(d, r.Event, r.Act, f)
if err != nil {
t.Errorf("%s —%s→ %s was refused with its guard holding: %v", orNothing(from), r.Event, r.To, err)
continue
}
if d.State != r.To || got.From != from || got.To != r.To || len(d.Transitions) != 1 {
t.Errorf("%s —%s→ left it %s, transition %+v", orNothing(from), r.Event, d.State, got)
}
// The guard not holding: a delivery with no facts.
bare := &Delivery{ID: "novox/app@bare", State: from, Check: &Verdict{Gate: "pass"}}
if r.Event == EvAccepted || r.Event == EvChecked {
bare.Check = nil
if r.Event == EvAccepted {
bare.Check = &Verdict{Gate: "fail"}
}
}
if r.Event == EvRefused {
bare.Check = &Verdict{Gate: "pass"}
}
if _, err := Apply(bare, r.Event, r.Act, Facts{Now: f.Now}); err == nil {
t.Errorf("%s —%s→ %s was taken with no fact for its guard", orNothing(from), r.Event, r.To)
} else if bare.State != from || len(bare.Transitions) != 0 {
t.Errorf("a refused transition moved the delivery to %s", bare.State)
}
}
}
}
func TestEveryPairTheTableDoesNotHoldIsRefusedByName(t *testing.T) {
events := []Event{EvAnnounced, EvAppeared, EvAdopted, EvChecked, EvAccepted, EvRefused, EvRecheck, EvReask, EvNewHead,
EvClosed, EvMerged, EvMergedUnchecked, EvGo, EvHold, EvRelease, EvDone, EvFailed, EvSuperseded, EvStop}
for _, from := range append([]State{None}, AllStates...) {
for _, ev := range events {
for _, act := range []bool{false, true} {
if rowFor(from, ev, act) != nil {
continue
}
d := &Delivery{ID: "novox/app@c0ffee", State: from, MergedAs: "x", NewerHead: "y", Check: &Verdict{Gate: "pass"},
Walk: &WalkSeen{ID: "p", State: walkDone}, HeldWhy: "h", ClosedUnmerged: true, Number: 1, Commit: "c"}
_, err := Apply(d, ev, act, Facts{Now: time.Now(), Why: "w", By: "b"})
var refused ErrRefused
if !errors.As(err, &refused) || refused.Event != ev || refused.From != from {
t.Errorf("%s —%s (act %v)→ was not refused by name: %v", orNothing(from), ev, act, err)
}
if d.State != from {
t.Errorf("a pair the table does not hold moved %s to %s", orNothing(from), d.State)
}
}
}
}
}
// The rules the table is for, read off the table.
func TestTheTableKeepsItsRules(t *testing.T) {
for _, r := range Table {
for _, from := range r.From {
// Nothing leaves a final state.
if from.Final() {
t.Errorf("%s is final and the table leaves it by %s", from, r.Event)
}
// Nothing goes back before the trunk once a delivery is on it.
if (from == Published || from == Delivering || from == Held) &&
(r.To == Proposed || r.To == Checked || r.To == Ready || r.To == Rejected) {
t.Errorf("%s goes back to %s by %s", from, r.To, r.Event)
}
}
// The trunk rule: only a ready delivery is published, by its merge; nothing off the trunk delivers.
if r.To == Published && (r.Event != EvMerged || len(r.From) != 1 || r.From[0] != Ready) {
t.Errorf("%v —%s→ published: only a ready delivery's merge publishes", r.From, r.Event)
}
if r.To == Delivering {
for _, from := range r.From {
if from == Proposed || from == Checked || from == Ready || from == Rejected {
t.Errorf("%s delivers by %s without reaching the trunk", from, r.Event)
}
}
}
// Held is left only by a person or a walk that started on a word that was not this owner's.
if contains(r.From, Held) && r.To == Delivering && r.Event == EvRelease && !r.Act {
t.Error("a held delivery is released by observation")
}
}
// Every state that is not final has a bound or is the operator's to wait in, and every H2 transition is
// a row the table holds from that state: observed once the walk's record is read again, or H2's own ask.
for state, b := range Bounds {
for _, ev := range b.H2 {
if rowFor(state, ev, false) == nil && rowFor(state, ev, true) == nil {
t.Errorf("H2 may take %s from %s, which the table does not hold", ev, state)
}
}
}
// H2 re-asks a check once: a second re-ask is refused by the table itself.
d0 := &Delivery{ID: "novox/app@c0", State: Proposed, Number: 1, CheckAsk: &CheckAsk{ID: "check-1", Reasked: 1}}
if _, err := Apply(d0, EvReask, true, Facts{Now: time.Now(), By: byH2, Why: "stalled"}); err == nil {
t.Fatal("a check re-asked once was re-asked again")
}
// A ready delivery off the trunk is refused publication, whatever else is true of it.
d := &Delivery{ID: "novox/app@c0", State: Ready, Walk: &WalkSeen{ID: "plan-x", State: walkRolling}}
if _, err := Apply(d, EvMerged, false, Facts{Now: time.Now()}); err == nil {
t.Fatal("a commit not on the trunk was published")
}
}
func TestTheMachineStepTable(t *testing.T) {
for _, c := range []struct {
from, to StepState
may bool
}{
{"", StepSent, true}, {StepSent, StepJudging, true}, {StepJudging, StepPassed, true},
{StepJudging, StepFailed, true}, {StepFailed, StepRolledBack, true}, {StepSent, StepFailed, true},
{StepPassed, StepFailed, false}, {StepRolledBack, StepPassed, false}, {StepPassed, StepSent, false},
{StepJudging, StepSent, false}, {StepFailed, StepPassed, false},
} {
if StepMay(c.from, c.to) != c.may {
t.Errorf("%q → %q: may %v, wanted %v", c.from, c.to, !c.may, c.may)
}
}
}
// settle stands a delivery where its facts put it, whatever order they came in.
func TestSettleIsTheOrderOfTheFactsNotOfTheirArrival(t *testing.T) {
now := time.Now()
d := &Delivery{ID: "novox/app@c0", Number: 1, Commit: "c0"}
if _, err := Apply(d, EvAnnounced, false, Facts{Now: now}); err != nil {
t.Fatal(err)
}
// The verdict, the merge and a finished walk, all known before anything settled.
d.Check = &Verdict{Gate: "pass"}
d.MergedAs = "m1"
d.Walk = &WalkSeen{ID: "plan-1", State: walkDone}
ts := settle(d, Facts{Now: now})
var path []State
for _, t := range ts {
path = append(path, t.To)
}
want := []State{Checked, Ready, Published, Delivering, Delivered}
if len(path) != len(want) {
t.Fatalf("settled through %v, wanted %v", path, want)
}
for i := range want {
if path[i] != want[i] {
t.Fatalf("settled through %v, wanted %v", path, want)
}
}
}