go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/cv/internal/cvtesting/e2e/submit_tree_checks_test.go (about) 1 // Copyright 2021 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package e2e 16 17 import ( 18 "fmt" 19 "testing" 20 21 gerritpb "go.chromium.org/luci/common/proto/gerrit" 22 23 cfgpb "go.chromium.org/luci/cv/api/config/v2" 24 "go.chromium.org/luci/cv/internal/common/tree" 25 "go.chromium.org/luci/cv/internal/configs/prjcfg/prjcfgtest" 26 gf "go.chromium.org/luci/cv/internal/gerrit/gerritfake" 27 "go.chromium.org/luci/cv/internal/run" 28 29 . "github.com/smartystreets/goconvey/convey" 30 ) 31 32 func TestSubmissionDuringClosedTree(t *testing.T) { 33 // t.Parallel() 34 Convey("Test closed tree", t, func() { 35 ct := Test{} 36 ctx, cancel := ct.SetUp(t) 37 defer cancel() 38 39 const lProject = "infra" 40 const gHost = "g-review" 41 const gRepo = "re/po" 42 const gRef = "refs/heads/main" 43 const gChange = 400 44 const gChangeSubmit = 200 45 Convey("CV fails Full Run if tree status app is down", func() { 46 ct.TreeFake.InjectErr(fmt.Errorf("tree status app is down")) 47 48 cfg := MakeCfgSingular("cg0", gHost, gRepo, gRef) 49 cfg.ConfigGroups[0].Verifiers.TreeStatus = &cfgpb.Verifiers_TreeStatus{ 50 Url: "https://tree-status.example.com/", 51 } 52 prjcfgtest.Create(ctx, lProject, cfg) 53 So(ct.PMNotifier.UpdateConfig(ctx, lProject), ShouldBeNil) 54 55 ct.GFake.AddFrom(gf.WithCIs(gHost, gf.ACLRestricted(lProject), gf.CI( 56 gChange, gf.Project(gRepo), gf.Ref(gRef), 57 gf.Owner("user-1"), 58 gf.CQ(+2, ct.Clock.Now(), gf.U("user-2")), 59 gf.Approve(), 60 gf.Updated(ct.Clock.Now()), 61 gf.Desc("Just a normal CL"), 62 ))) 63 64 // Only a committer can trigger a FullRun for someone else's CL. 65 ct.AddCommitter("user-2") 66 67 ct.LogPhase(ctx, "CV starts Runs") 68 var r *run.Run 69 ct.RunUntil(ctx, func() bool { 70 rs := ct.LoadRunsOf(ctx, lProject) 71 if len(rs) == 0 { 72 return false 73 } 74 r = rs[0] 75 return true 76 }) 77 78 ct.LogPhase(ctx, "CV fails to submit the CL") 79 ct.RunUntil(ctx, func() bool { 80 r = ct.LoadRun(ctx, r.ID) 81 return r.Status == run.Status_FAILED 82 }) 83 info := ct.GFake.GetChange(gHost, gChange).Info 84 So(info.GetStatus(), ShouldEqual, gerritpb.ChangeStatus_NEW) 85 So(info.Messages[len(info.Messages)-1].Message, ShouldContainSubstring, "Could not submit this CL because the tree status app") 86 So(info.Messages[len(info.Messages)-1].Message, ShouldContainSubstring, "repeatedly returned failures") 87 So(info.GetLabels()["Commit-Queue"].Value, ShouldEqual, 0) 88 }) 89 90 Convey("CV submits Full Run only iff No-Tree-Checks: True", func() { 91 descriptions := map[int]string{ 92 gChange: "Just a normal CL", 93 gChangeSubmit: "This is a revert.\n\nNo-Tree-Checks: True\nNo-Try: True", 94 } 95 var expectedErr error 96 Convey("when tree is closed", func() { 97 ct.TreeFake.ModifyState(ctx, tree.Closed) 98 }) 99 Convey("when tree status app is failing", func() { 100 expectedErr = fmt.Errorf("tree status app is down") 101 ct.TreeFake.InjectErr(expectedErr) 102 }) 103 104 cfg := MakeCfgSingular("cg0", gHost, gRepo, gRef) 105 cfg.ConfigGroups[0].Verifiers.TreeStatus = &cfgpb.Verifiers_TreeStatus{ 106 Url: "https://tree-status.example.com/", 107 } 108 prjcfgtest.Create(ctx, lProject, cfg) 109 So(ct.PMNotifier.UpdateConfig(ctx, lProject), ShouldBeNil) 110 111 for _, gChange := range []int{gChange, gChangeSubmit} { 112 ct.GFake.AddFrom(gf.WithCIs(gHost, gf.ACLRestricted(lProject), gf.CI( 113 gChange, gf.Project(gRepo), gf.Ref(gRef), 114 gf.Owner("user-1"), 115 gf.CQ(+2, ct.Clock.Now(), gf.U("user-2")), 116 gf.Approve(), 117 gf.Updated(ct.Clock.Now()), 118 gf.Desc(descriptions[gChange]), 119 ))) 120 } 121 // Only a committer can trigger a FullRun for someone else' CL. 122 ct.AddCommitter("user-2") 123 124 ct.LogPhase(ctx, "CV starts Runs") 125 var rWait, rSubmit *run.Run 126 ct.RunUntil(ctx, func() bool { 127 rs := ct.LoadRunsOf(ctx, lProject) 128 if len(rs) != 2 { 129 return false 130 } 131 if rs[0].CLs[0] == ct.LoadGerritCL(ctx, gHost, gChangeSubmit).ID { 132 rSubmit, rWait = rs[0], rs[1] 133 } else { 134 rSubmit, rWait = rs[1], rs[0] 135 } 136 return true 137 }) 138 139 ct.LogPhase(ctx, "CV submits CL with No-Tree-Checks and waits with the other CL") 140 ct.RunUntil(ctx, func() bool { 141 rSubmit = ct.LoadRun(ctx, rSubmit.ID) 142 rWait = ct.LoadRun(ctx, rWait.ID) 143 return rWait.Status == run.Status_WAITING_FOR_SUBMISSION && rSubmit.Status == run.Status_SUCCEEDED 144 }) 145 So(ct.GFake.GetChange(gHost, gChange).Info.GetStatus(), ShouldEqual, gerritpb.ChangeStatus_NEW) 146 So(ct.GFake.GetChange(gHost, gChangeSubmit).Info.GetStatus(), ShouldEqual, gerritpb.ChangeStatus_MERGED) 147 148 // And the tree must not open. 149 st, err := ct.TreeFake.Client().FetchLatest(ctx, "whatever") 150 So(err, ShouldEqual, expectedErr) 151 So(st.State, ShouldNotEqual, tree.Open) 152 }) 153 }) 154 }