//go:build integration package main import ( "reflect" "strings" "testing" "time" sdkdelivery "git.novox.be/novox/mesh-sdk/go/delivery" "github.com/novox/mesh-controller/internal/inventory" ) // A6, A7 whole against the store and a real bus (ADR 0503 §6, ADR 0498 check §7; issue 1851): a walk whose modules the // builder built goes from asked to built with no build asked, its keys taken in off the plans' hold, then sends its tier // to the first machine as before; a key still building holds the tier, said, and moves on when the builder's key is done. func TestAWalkTakesItsTierFromTheBuildersKeysAndSendsIt(t *testing.T) { withTheGate(t) judgeLeavesAsTheCore(t) tm := aTierMesh(t, "app1", "app2") ctx := t.Context() inv := tm.open.inventory noBuildAsked(t) b := aBuilderOnTheBus(t) // The walk carries c3 of the catalogue; its tier is unasked. p := tm.plan(t) p.Commit = "c3c3c3c3c3c3c3c3" for _, m := range []string{"app1", "app2"} { p.Modules[m] = &inventory.PlanModule{} } if err := inv.SavePlan(ctx, &p); err != nil { t.Fatal(err) } fp1, fp2 := fpOf('1'), fpOf('2') m1 := b.indexed(t, "app1", "c3", "novox/mesh-catalog", "modules/app1", p.Commit, fp1) m2 := b.indexed(t, "app2", "c3", "novox/mesh-catalog", "modules/app2", p.Commit, fp2) b.built(t, "app1", "c3", fp1, m1, sdkdelivery.BuildDone) b.built(t, "app2", "c3", fp2, m2, sdkdelivery.BuildBuilding) advancePlans(ctx, tm.open) becomes(t, 10*time.Second, "app1 taken from its key", func() bool { advancePlans(ctx, tm.open) return tm.plan(t).Modules["app1"].State == "built" }) p = tm.plan(t) if s := p.Modules["app2"]; s.State != "asked" || !s.Key || !strings.Contains(s.Why, "building on ace") { t.Fatalf("A7: app2, whose key is building, is %+v", s) } if len(tm.sent) != 0 { t.Fatalf("A7: the tier was sent while app2 waits on its key: %v", tm.sent) } if s := p.Modules["app1"]; !strings.Contains(s.Made, "taken from the builder's key") || s.Fingerprint != fp1 { t.Fatalf("A6: plans does not say app1 was taken from the builder's key: %+v", s) } b.built(t, "app2", "c3", fp2, m2, sdkdelivery.BuildDone) becomes(t, 10*time.Second, "the tier sent to the first machine", func() bool { advancePlans(ctx, tm.open) return len(tm.sent) > 0 }) p = tm.plan(t) if !reflect.DeepEqual(tm.sent[0], []string{"anchor"}) { t.Fatalf("A6: the tier taken from keys was sent %v, not to the first machine", tm.sent) } current, err := inv.CurrentBuilds(ctx) if err != nil { t.Fatal(err) } for _, m := range []string{"app1", "app2"} { if current[m].Commit != p.Commit || !strings.HasPrefix(p.Modules[m].Build, "key-") { t.Fatalf("A6: %s stands on %+v (%s), not the key's build at %s", m, current[m], p.Modules[m].Build, p.Commit) } } } // A7: a failed key fails the walk with the builder's words, and nothing is sent. func TestAFailedKeyFailsTheWalkInTheBuildersWords(t *testing.T) { tm := aTierMesh(t, "app1") ctx := t.Context() inv := tm.open.inventory noBuildAsked(t) b := aBuilderOnTheBus(t) p := tm.plan(t) p.Commit = "c4c4c4c4c4c4c4c4" p.Modules["app1"] = &inventory.PlanModule{} if err := inv.SavePlan(ctx, &p); err != nil { t.Fatal(err) } fp := fpOf('4') manifest := b.indexed(t, "app1", "c4", "novox/mesh-catalog", "modules/app1", p.Commit, fp) b.built(t, "app1", "c4", fp, manifest, sdkdelivery.BuildFailed) for i := 0; i < 3; i++ { advancePlans(ctx, tm.open) } p = tm.plan(t) if p.State != inventory.PlanFailed || !strings.Contains(p.Note, "digests differ: ace sha256:1, g14 sha256:2") { t.Fatalf("A7: the walk is %s: %q", p.State, p.Note) } if len(tm.sent) != 0 { t.Fatalf("A7: a failed walk sent %v", tm.sent) } }