package main import ( "context" "encoding/json" "strings" "testing" "time" "github.com/nats-io/nats.go" "github.com/novox/mesh-controller/internal/inventory" ) // novox/hq issue 529: a walk waiting because its delivery is held for a person said only "its walk waits for // mesh-delivery's word — `plans go … --why …` starts it by hand", pointing a reader at a hand start instead of at // the person's decision. The line names what mesh-delivery says holds the walk: the held delivery, its reason and // `mesh-delivery.release`; or the delivery not at its turn. func waitingWalk(now time.Time) inventory.Plan { return inventory.Plan{ID: "plan-1791793277221379309", Repository: "novox/mesh-sdk", Branch: "main", Commit: "9747f3e1b1ac", State: inventory.PlanBuilding, Tiers: [][]string{{"mesh-sdk"}}, Created: now.Add(-9 * time.Minute), Modules: map[string]*inventory.PlanModule{}, Delivery: &inventory.PlanDelivery{Awaits: "mesh-delivery"}} } func TestAWalkWaitingOnAHeldDeliveryNamesTheHold(t *testing.T) { now := time.Date(2026, 10, 12, 8, 35, 0, 0, time.UTC) p := waitingWalk(now) held := "novox/mesh-sdk@d41d255f28ad held since 2026-10-12T08:23:00Z: its group adr-0307-state-history's composed " + "check did not pass for the heads that merged — a person decides: `mesh-delivery.release` (id " + "novox/mesh-sdk@d41d255f28ad, why) lets it go, `mesh-delivery.stop` ends it" waits := walkWaits{p.ID: {{ID: "novox/mesh-sdk@d41d255f28ad", State: "held", Walk: p.ID, Says: held}}} line := planLineSaid(p, now, pauseView{}, tierAtLeast, nil, waits) for _, want := range []string{"held for a person", "composed check did not pass", "mesh-delivery.release"} { if !strings.Contains(line, want) { t.Fatalf("the line does not say %q: %q", want, line) } } if strings.Contains(line, "starts it by hand") { t.Fatalf("a held walk's line points at a hand start: %q", line) } // Told nothing — an older mesh-delivery, or none answering — it reads as before. if line := planLineSaid(p, now, pauseView{}, tierAtLeast, nil, nil); !strings.Contains(line, "starts it by hand") { t.Fatalf("told nothing, the line reads %q", line) } } func TestAWalkWaitingOnADeliveryNotAtItsTurnSaysSo(t *testing.T) { now := time.Date(2026, 10, 12, 8, 57, 0, 0, time.UTC) p := waitingWalk(now) waits := walkWaits{p.ID: {{ID: "novox/mesh-sdk@9747f3e1b1ac", State: "published", Walk: p.ID, Says: "not let go yet: novox/mesh-tools@3f0c1a6cec18 (published) carried by it not at its turn"}}} line := planLineSaid(p, now, pauseView{}, tierAtLeast, nil, waits) if !strings.Contains(line, "not at its turn") || !strings.Contains(line, "novox/mesh-tools@3f0c1a6cec18") { t.Fatalf("the line reads %q", line) } // No hand start offered: it would skip the group's order (review of issue 529). if strings.Contains(line, "plans go") { t.Fatalf("a walk not at its turn points at a hand start: %q", line) } } // A walk nothing holds — the nzbgeek case, its merges' deliveries ended as nothing-to-move — is said so, and the hand // start is the way on. func TestAWalkHeldByNothingSaysWhyAndTheHandStart(t *testing.T) { now := time.Date(2026, 10, 11, 20, 30, 0, 0, time.UTC) p := waitingWalk(now) waits := walkWaits{p.ID: {{ID: "novox/mesh-media-catalog@cdcdcdcd0000", State: "unheld", Walk: p.ID, Says: "held by nothing: its merges' deliveries ended as nothing-to-move (novox/mesh-media-catalog@abababab0000), " + "yet the walk moves nzbgeek, nzbget — nothing gives it its word"}}} line := planLineSaid(p, now, pauseView{}, tierAtLeast, nil, waits) if !strings.Contains(line, "ended as nothing-to-move") || !strings.Contains(line, "plans go "+p.ID) { t.Fatalf("the line reads %q", line) } } // What holds each waiting walk is asked of mesh-delivery's `stalled` with `walks`, a verb the controller's grant // already names; only lines naming a walk are kept, by walk. func TestWhatHoldsTheWaitingWalksIsAskedOfTheDeliveryOwner(t *testing.T) { was := askDeliveryOwner defer func() { askDeliveryOwner = was }() var asked map[string]any askDeliveryOwner = func(_ context.Context, _ *nats.Conn, verb string, args map[string]any) (json.RawMessage, error) { if verb != "stalled" { t.Fatalf("asked %s", verb) } asked = args return json.Marshal([]stalledLine{{ID: "novox/a@1", State: "held", Walk: "plan-1", Says: "held: why"}, {ID: "novox/b@2", State: "held", Says: "a delivery past its bound, no walk"}}) } got := walksWaiting(t.Context(), nil, true) if asked["walks"] != true { t.Fatalf("stalled was asked with %v", asked) } if len(got) != 1 || len(got["plan-1"]) != 1 || got["plan-1"][0].Says != "held: why" { t.Fatalf("the waiting walks are %+v", got) } if got := walksWaiting(t.Context(), nil, false); got != nil { t.Fatalf("with no holder on record it asked: %+v", got) } }