diff --git a/internal/builder/siblings_test.go b/internal/builder/siblings_test.go index 5fd1fd2c..edeab57a 100644 --- a/internal/builder/siblings_test.go +++ b/internal/builder/siblings_test.go @@ -56,6 +56,12 @@ func (w aSiblingWorld) beside() map[string]Beside { // hostCheck is the node-engine's pull request checked, with or without its sibling. func (w aSiblingWorld) hostCheck(t *testing.T, id, workspace, cache string, siblings ...Sibling) CheckVerdict { + t.Helper() + return w.hostCheckAs(t, "", id, workspace, cache, siblings...) +} + +// hostCheckAs is hostCheck for a request that names who judges: an older request says "validator" for a node-engine change. +func (w aSiblingWorld) hostCheckAs(t *testing.T, judge, id, workspace, cache string, siblings ...Sibling) CheckVerdict { t.Helper() if workspace == "" { workspace = t.TempDir() @@ -63,7 +69,7 @@ func (w aSiblingWorld) hostCheck(t *testing.T, id, workspace, cache string, sibl beside := w.beside() delete(beside, "mesh-host") v, err := Check(t.Context(), Command, CheckSpec{ID: id, Repository: w.host, Ref: w.hostHead, Owner: "novox", - Repo: "mesh-host", Number: 114, Base: "main", Judge: "validator", JudgeCache: cache, + Repo: "mesh-host", Number: 114, Base: "main", Judge: judge, JudgeCache: cache, Paths: []string{"validate/v.go"}, Modules: []string{"mesh-host"}, Beside: beside, Siblings: siblings, Toolchain: "golang"}, workspace, aFactsRegistry(t), GitCredential{}, nil) if err != nil { @@ -97,7 +103,7 @@ func (w aSiblingWorld) hostSibling() Sibling { // The node-engine's change fails its own check without its sibling (the controller's lock beside it is the trunk's), // and passes with it read at its merge: the check reads the lock the sibling's change brings, the judge is the -// controller built from the sibling's merge with the node-engine change's own validator, and the verdict names both. +// controller built from the sibling's merge with the validator that controller vendors (mesh-sdk's), and the verdict names both. func TestAHostMemberIsCheckedBesideItsControllerSibling(t *testing.T) { aRuntimeThatBuildsTheJudge(t) w := theSiblingWorld(t) @@ -112,8 +118,8 @@ func TestAHostMemberIsCheckedBesideItsControllerSibling(t *testing.T) { if v.Repo == nil || v.Repo.Verdict != "pass" { t.Fatalf("beside its sibling the node-engine change should pass its own check: %+v\n%s", v.Repo, v.Report) } - if v.Gate == nil || v.Gate.Verdict != "pass" || !strings.Contains(v.Gate.Summary, "the sibling controller with the sibling's validator") { - t.Fatalf("the judge was not the controller built from the sibling's merge with the change's own validator: %+v\n%s", + if v.Gate == nil || v.Gate.Verdict != "pass" || !strings.Contains(v.Gate.Summary, "the sibling controller with the vendored validator") { + t.Fatalf("the judge was not the controller built from the sibling's merge with the validator it vendors: %+v\n%s", v.Gate, v.Report) } baseCommit := w.controllerMain @@ -127,8 +133,8 @@ func TestAHostMemberIsCheckedBesideItsControllerSibling(t *testing.T) { if want := "sibling novox/mesh-controller#346 merge " + v.Beside["mesh-controller"]; v.JudgeFrom != want { t.Fatalf("the judge came from %q, not %q", v.JudgeFrom, want) } - if v.Validator != "the change's own validator" { - t.Fatalf("the validator was %q", v.Validator) + if want := "mesh-sdk's validator at the pin of sibling novox/mesh-controller#346 merge " + v.Beside["mesh-controller"]; v.Validator != want { + t.Fatalf("the validator was %q, not %q", v.Validator, want) } // The statuses' "beside siblings" part is the forge module's to write, from the verdict's siblings. if strings.Contains(v.Gate.Summary, "beside siblings") || strings.Contains(v.Repo.Summary, "beside siblings") { @@ -140,43 +146,45 @@ func TestAHostMemberIsCheckedBesideItsControllerSibling(t *testing.T) { } } -// The controller's change fails with no sibling (it builds only with the node-engine change's validator), and passes with -// it: judged by itself, with the sibling node-engine's validator in place of the one it vendors. -func TestAControllerMemberIsJudgedByItselfWithItsHostSiblingsValidator(t *testing.T) { +// The controller's change is judged by itself with the validator it vendors, whatever the node-engine sibling brings: the +// node-engine holds no validator any more (issue 1391, novox/hq ADR 0446 clause 5). Nothing of the sibling is put into the judge. +func TestAControllerMemberIsJudgedByItselfWithTheValidatorItVendors(t *testing.T) { aRuntimeThatBuildsTheJudge(t) w := theSiblingWorld(t) - // The controller change calls a function only the node-engine change brings. - gitInRepo(t, w.controller, "checkout", "--quiet", "change") - main := aJudgeOf("the sibling controller")["cmd/mesh-controller/main.go"] - w.controllerHead = commitFiles(t, w.controller, map[string]string{ - "cmd/mesh-controller/main.go": strings.Replace(main, "validate.Word)", "validate.Word+validate.More)", 1)}) - gitInRepo(t, w.controller, "checkout", "--quiet", "main") - gitInRepo(t, w.host, "checkout", "--quiet", "change") - w.hostHead = commitFiles(t, w.host, map[string]string{ - "validate/more.go": "package validate\n\nconst More = \" and more\"\n"}) - gitInRepo(t, w.host, "checkout", "--quiet", "main") alone := w.controllerCheck(t, "check-867-controller-alone", "") - if alone.Gate == nil || alone.Gate.Verdict != "fail" || !strings.Contains(alone.Gate.Summary, "does not build") { - t.Fatalf("with no sibling the controller change should not build (the validator it vendors lacks More): %+v", alone.Gate) - } - v := w.controllerCheck(t, "check-867-controller", "", w.hostSibling()) - if v.Gate == nil || v.Gate.Verdict != "pass" || !strings.Contains(v.Gate.Summary, "the sibling controller with the sibling's validator and more") { - t.Fatalf("the controller change was not judged by itself with the sibling's validator: %+v\n%s", v.Gate, v.Report) + for what, got := range map[string]CheckVerdict{"alone": alone, "beside its sibling": v} { + if got.Gate == nil || got.Gate.Verdict != "pass" || !strings.Contains(got.Gate.Summary, "the sibling controller with the vendored validator") { + t.Fatalf("the controller change %s was not judged by itself with the validator it vendors: %+v\n%s", what, got.Gate, got.Report) + } } if len(v.Siblings) != 1 || v.Siblings[0].Repository != "novox/mesh-host" || v.Siblings[0].BaseCommit != w.hostMain { t.Fatalf("the verdict names siblings %+v", v.Siblings) } - if want := "sibling novox/mesh-host#114 merge " + v.Beside["mesh-host"] + "'s validator"; v.Validator != want { - t.Fatalf("the validator was %q, not %q", v.Validator, want) + if v.Validator != "" { + t.Fatalf("the validator was %q: a controller judged by itself has the one it vendors", v.Validator) } - if !strings.HasPrefix(v.JudgeFrom, "this change's own merge ") { - t.Fatalf("the judge came from %q", v.JudgeFrom) +} + +// A node-engine member whose tree holds no validator (validate/ and internal/declaration/ deleted, as R4b deletes them) is judged +// beside its controller sibling all the same: nothing of it is copied into the judge, so nothing is missing (issue 1391). +func TestAHostMemberWithNoValidatorOfItsOwnDoesNotFailTheJudge(t *testing.T) { + aRuntimeThatBuildsTheJudge(t) + w := theSiblingWorld(t) + gitInRepo(t, w.host, "checkout", "--quiet", "change") + gitInRepo(t, w.host, "rm", "--quiet", "-r", "validate", "internal") + gitInRepo(t, w.host, "commit", "--quiet", "-m", "the validator is mesh-sdk's") + w.hostHead = gitInRepo(t, w.host, "rev-parse", "HEAD") + gitInRepo(t, w.host, "checkout", "--quiet", "main") + + v := w.hostCheckAs(t, "validator", "check-1391-host-without-validator", "", "", w.controllerSibling()) + if v.Gate == nil || v.Gate.Verdict != "pass" || !strings.Contains(v.Gate.Summary, "the sibling controller with the vendored validator") { + t.Fatalf("a node-engine change with no validator of its own was not judged: %+v\n%s", v.Gate, v.Report) } - // The member's own tree keeps the validator it vendors: its own script reads the vendored files as they are. - if body := gitInRepo(t, w.controller, "show", "main:vendor/github.com/novox/mesh-host/validate/v.go"); strings.Contains(body, "sibling") { - t.Fatalf("the trunk's vendored validator was touched: %s", body) + alone := w.hostCheckAs(t, "validator", "check-1391-host-alone", "", "") + if alone.Gate == nil || strings.Contains(alone.Gate.Summary, "could not be put in the judge") { + t.Fatalf("the judge tried to copy the validator of a change that holds none: %+v", alone.Gate) } } @@ -208,7 +216,7 @@ func TestACheckThatCouldNotLearnItsSiblingsSaysSo(t *testing.T) { beside := w.beside() delete(beside, "mesh-host") v, err := Check(t.Context(), Command, CheckSpec{ID: "check-867-unknown", Repository: w.host, Ref: w.hostHead, - Owner: "novox", Repo: "mesh-host", Number: 114, Base: "main", Judge: "validator", Paths: []string{"validate/v.go"}, + Owner: "novox", Repo: "mesh-host", Number: 114, Base: "main", Paths: []string{"validate/v.go"}, Modules: []string{"mesh-host"}, Beside: beside, SiblingsUnknown: "mesh-delivery did not answer", Toolchain: "golang"}, t.TempDir(), aFactsRegistry(t), GitCredential{}, nil) if err != nil { @@ -217,7 +225,7 @@ func TestACheckThatCouldNotLearnItsSiblingsSaysSo(t *testing.T) { if v.SiblingsUnknown != "mesh-delivery did not answer" || len(v.Siblings) != 0 || v.JudgeFrom != "" { t.Fatalf("the verdict says %q, siblings %+v, judge from %q", v.SiblingsUnknown, v.Siblings, v.JudgeFrom) } - if !strings.Contains(v.Gate.Summary, "the trunk's controller with the sibling's validator") { + if !strings.Contains(v.Gate.Summary, "the trunk's controller with the vendored validator") { t.Fatalf("a check without siblings was not judged as before: %+v", v.Gate) } if strings.Contains(v.Gate.Summary, "[beside siblings") { @@ -226,7 +234,7 @@ func TestACheckThatCouldNotLearnItsSiblingsSaysSo(t *testing.T) { } // The judge a group made is kept only for the same inputs: the controller's merge and the base commits it was merged -// onto, the validator's source and commit. The same inputs take the judge kept; any other builds it again. +// onto. The same inputs take the judge kept; any other builds it again. func TestTheGroupsJudgeIsReusedOnlyForTheSameInputs(t *testing.T) { built := aRuntimeThatBuildsTheJudge(t) w := theSiblingWorld(t) @@ -253,14 +261,6 @@ func TestTheGroupsJudgeIsReusedOnlyForTheSameInputs(t *testing.T) { if n := builds(t, built); n != 3 { t.Fatalf("a moved base did not build the judge again: %d builds", n) } - // The validator's source moves: the member's own change differs. - gitInRepo(t, w.host, "checkout", "--quiet", "change") - w.hostHead = commitFiles(t, w.host, aHostSaying("another validator")) - gitInRepo(t, w.host, "checkout", "--quiet", "main") - w.hostCheck(t, "check-867-cache-5", "", cache, w.controllerSibling()) - if n := builds(t, built); n != 4 { - t.Fatalf("a moved validator did not build the judge again: %d builds", n) - } } // The contrast the isolation test stands on: built the old way, in the workspace with its HOME and no judge-build