package main import ( "context" "strings" "testing" "time" "github.com/novox/mesh-controller/internal/catalogue" "github.com/novox/mesh-controller/internal/inventory" "github.com/novox/mesh-controller/internal/link" ) // TestAWalkLeavesOutAModuleWhoseProgramDidNotChange is novox/hq issue 721: mesh-controller #335 changed one sentence // that mesh-builder never reaches; its check found build-agent the same as the build the mesh holds, so the walk moves // mesh-controller alone and says build-agent is unchanged. func TestAWalkLeavesOutAModuleWhoseProgramDidNotChange(t *testing.T) { pull := link.PullRef{Owner: "novox", Repo: "mesh-controller", Number: 335} head := "34050dc6aaaabbbbccccdddd" aPlan := func() *inventory.Plan { return &inventory.Plan{Tiers: [][]string{{"mesh-controller", "build-agent"}}, Modules: map[string]*inventory.PlanModule{"mesh-controller": {}, "build-agent": {}}, Delivery: &inventory.PlanDelivery{Merges: []inventory.PlanMerge{{Repository: "novox/mesh-controller", Commit: "c2ec0a60", Number: 335, Head: head}}}} } held := map[string]string{"build-agent/agent": "store/build-agent@sha256:aaaa", "mesh-controller/controller": "store/mesh-controller@sha256:cccc"} builds := []link.CheckedBuild{ {Pull: pull, Head: head[:12], Built: link.CheckBuilt{Module: "build-agent", Key: "k1", Made: []link.MadeArtifact{{Name: "agent", Kind: "image", Reference: "scratch/build-agent@sha256:bbbb"}}, Same: "unchanged: its program is the same as running build agent aaaa", SameAs: map[string]string{"agent": "10.0.0.1:5000/build-agent@sha256:aaaa"}}}, {Pull: pull, Head: head[:12], Built: link.CheckBuilt{Module: "mesh-controller", Key: "k2", Made: []link.MadeArtifact{{Name: "controller", Kind: "image", Reference: "scratch/mesh-controller@sha256:dddd"}}}}, } p := aPlan() same := sameAsRunning(p, builds, held) if len(same) != 1 || same["build-agent"] == "" { t.Fatalf("the walk leaves out %v, want build-agent alone", same) } withhold(p, []string{"build-agent"}, nil) if len(p.Tiers) != 1 || len(p.Tiers[0]) != 1 || p.Tiers[0][0] != "mesh-controller" { t.Fatalf("the walk moves %v, want mesh-controller alone", p.Tiers) } // **The held build moved since the check**: what it was compared with no longer runs. moved := map[string]string{"build-agent/agent": "store/build-agent@sha256:eeee"} if same := sameAsRunning(aPlan(), builds, moved); len(same) != 0 { t.Fatalf("a module compared with a build the mesh no longer holds is left out: %v", same) } // **A check of another head** says nothing of this merge. other := aPlan() other.Delivery.Merges[0].Head = "ffffffffffffffff" if same := sameAsRunning(other, builds, held); len(same) != 0 { t.Fatalf("a check of another head left out %v", same) } // **Another pull request the walk answers** that did not find it the same leaves it in. two := aPlan() two.Delivery.Merges = append(two.Delivery.Merges, inventory.PlanMerge{Repository: "novox/mesh-catalog", Commit: "1234", Number: 9, Head: "abcdefabcdefabcdef"}) if same := sameAsRunning(two, builds, held); len(same) != 0 { t.Fatalf("a module another merge moves is left out: %v", same) } // **A merge with no pull request** leaves the walk as it is. direct := aPlan() direct.Delivery.Merges[0].Number = 0 if same := sameAsRunning(direct, builds, held); len(same) != 0 { t.Fatalf("a walk answering a merge without a pull request left out %v", same) } } // TestADependentOfABaseThatCameOutTheSameIsLeftOut is ADR 0267 rule 3 (novox/hq issue 607): a dependent is in a walk // for its base; when the base comes out the same as the build the mesh holds, the dependent stands on that build // still, and leaves the walk with it — and so does a dependent of that dependent. One a merge moves itself stays. func TestADependentOfABaseThatCameOutTheSameIsLeftOut(t *testing.T) { edges := []inventory.Edge{{From: "app", To: "base", Kind: inventory.EdgeDeclared}, {From: "app2", To: "app", Kind: inventory.EdgeDeclared}, {From: "own", To: "base", Kind: inventory.EdgeDeclared}} plan := &inventory.Plan{Modules: map[string]*inventory.PlanModule{"base": {}, "app": {}, "app2": {}, "own": {}}} same := withDependentsOf(map[string]string{"base": "unchanged: its program is the same"}, plan, []string{"base", "own"}, edges) if !strings.Contains(same["app"], "only for base") || !strings.Contains(same["app2"], "only for app") { t.Fatalf("the dependents of a base that came out the same are not left out: %v", same) } if _, left := same["own"]; left { t.Fatalf("a module its merge moves itself is left out for its base: %v", same) } // A dependent of a base that still moves stays. edges = append(edges, inventory.Edge{From: "app", To: "own", Kind: inventory.EdgeDeclared}) same = withDependentsOf(map[string]string{"base": "unchanged"}, plan, []string{"base", "own"}, edges) if _, left := same["app"]; left { t.Fatalf("a dependent of a base that still moves is left out: %v", same) } } // Against the store, the path a real merge takes (novox/hq issues 721 and 611): a merge whose check built its one // module the same as the build the mesh holds is cut into a walk done with nothing to walk, whose record names the // module unchanged and why — the walk mesh-delivery reads as delivered, never stranded. func TestAWalkWhoseEveryBuildCameOutTheSameIsDone(t *testing.T) { open := windowed(t) endsItsLine(t) asksRecorded(t) ctx := t.Context() inv := open.inventory if err := inv.RecordBuild(ctx, inventory.Build{ID: "build-one", Repository: "novox/one", Ref: "main", Module: "one", Commit: "c0", On: "builder", Asked: t0.Add(-time.Hour), Made: []inventory.Artifact{{Name: "one", Kind: "image", Reference: "store/one@sha256:aaaa"}}, Sources: []inventory.BuildSource{{Paths: []string{"main.go", "go.mod"}}}}); err != nil { t.Fatal(err) } // What runs: the module registered with that build's image (RegisteredHeld reads the module table). if err := inv.RegisterModule(ctx, catalogue.Manifest{Module: "one", Version: "1", Resources: []map[string]any{ {"id": "app", "type": "container", "name": "one", "image": "store/one@sha256:aaaa"}}}, inventory.Source{Repository: "novox/one", Ref: "main", BuiltFrom: "c0", Asked: time.Now()}); err != nil { t.Fatal(err) } pull := link.PullRef{Owner: "novox", Repo: "one", Number: 7} was := checkedBuildsOf checkedBuildsOf = func(context.Context, []link.PullRef) ([]link.CheckedBuild, error) { return []link.CheckedBuild{{Pull: pull, Head: "headaaaaaaaa", Built: link.CheckBuilt{Module: "one", Key: "k", Made: []link.MadeArtifact{{Name: "one", Kind: "image", Reference: "scratch/one@sha256:bbbb"}}, Same: "unchanged: its program is the same as running build one aaaa", SameAs: map[string]string{"one": "10.0.0.1:5000/one@sha256:aaaa"}}}}, nil } t.Cleanup(func() { checkedBuildsOf = was }) code := repoMerge("one", "one1aaaaaaaa", t0) code.Number, code.HeadSHA = 7, "headaaaaaaaa1111" hear(t, open, code, t0.Add(time.Second)) cutAt(t, open, t0.Add(2*time.Minute)) ws, _ := walks(t, open) if len(ws) != 1 { t.Fatalf("walks: %+v", ws) } w := ws[0] if w.State != inventory.PlanDone || len(w.Tiers) != 0 || len(w.Modules) != 0 { t.Fatalf("the walk reads %s, tiers %v, modules %v; want done with nothing to walk", w.State, w.Tiers, w.Modules) } if w.Delivery == nil || !strings.Contains(w.Delivery.Unchanged["one"], "its program is the same as running build") { t.Fatalf("the walk does not say one matched what runs: %+v", w.Delivery) } }