go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/cv/internal/rpc/v0/tryjobs_test.go (about) 1 // Copyright 2023 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 rpc 16 17 import ( 18 "testing" 19 20 bbpb "go.chromium.org/luci/buildbucket/proto" 21 bbutil "go.chromium.org/luci/buildbucket/protoutil" 22 23 cfgpb "go.chromium.org/luci/cv/api/config/v2" 24 apiv0pb "go.chromium.org/luci/cv/api/v0" 25 "go.chromium.org/luci/cv/internal/common" 26 "go.chromium.org/luci/cv/internal/configs/prjcfg/prjcfgtest" 27 "go.chromium.org/luci/cv/internal/cvtesting" 28 "go.chromium.org/luci/cv/internal/run" 29 "go.chromium.org/luci/cv/internal/tryjob" 30 31 . "github.com/smartystreets/goconvey/convey" 32 . "go.chromium.org/luci/common/testing/assertions" 33 ) 34 35 func TestMakeTryjobInvocations(t *testing.T) { 36 t.Parallel() 37 38 Convey("MakeTryjobInvocations", t, func() { 39 ct := cvtesting.Test{} 40 ctx, cancel := ct.SetUp(t) 41 defer cancel() 42 const lProject = "infra" 43 const bbHost = "buildbucket.example.com" 44 r := &run.Run{ 45 ID: common.MakeRunID(lProject, ct.Clock.Now(), 1, []byte("deadbeef")), 46 } 47 48 builderFoo := &bbpb.BuilderID{ 49 Project: lProject, 50 Bucket: "try", 51 Builder: "foo", 52 } 53 builderFooEquivalent := &bbpb.BuilderID{ 54 Project: lProject, 55 Bucket: "try", 56 Builder: "foo-equi", 57 } 58 builderBar := &bbpb.BuilderID{ 59 Project: lProject, 60 Bucket: "try", 61 Builder: "bar", 62 } 63 cg := &cfgpb.ConfigGroup{ 64 Name: "main", 65 Verifiers: &cfgpb.Verifiers{ 66 Tryjob: &cfgpb.Verifiers_Tryjob{ 67 Builders: []*cfgpb.Verifiers_Tryjob_Builder{ 68 { 69 Name: bbutil.FormatBuilderID(builderFoo), 70 EquivalentTo: &cfgpb.Verifiers_Tryjob_EquivalentBuilder{ 71 Name: bbutil.FormatBuilderID(builderFooEquivalent), 72 }, 73 }, 74 { 75 Name: bbutil.FormatBuilderID(builderBar), 76 }, 77 }, 78 }, 79 }, 80 } 81 82 prjcfgtest.Create(ctx, lProject, &cfgpb.Config{ 83 ConfigGroups: []*cfgpb.ConfigGroup{cg}, 84 }) 85 r.ConfigGroupID = prjcfgtest.MustExist(ctx, lProject).ConfigGroupIDs[0] 86 87 Convey("comprehensive", func() { 88 r.Tryjobs = &run.Tryjobs{ 89 State: &tryjob.ExecutionState{ 90 Requirement: &tryjob.Requirement{ 91 Definitions: []*tryjob.Definition{ 92 { 93 Backend: &tryjob.Definition_Buildbucket_{ 94 Buildbucket: &tryjob.Definition_Buildbucket{ 95 Host: bbHost, 96 Builder: builderFoo, 97 }, 98 }, 99 Critical: true, 100 }, 101 { 102 Backend: &tryjob.Definition_Buildbucket_{ 103 Buildbucket: &tryjob.Definition_Buildbucket{ 104 Host: bbHost, 105 Builder: builderBar, 106 }, 107 }, 108 }, 109 }, 110 }, 111 Executions: []*tryjob.ExecutionState_Execution{ 112 { 113 Attempts: []*tryjob.ExecutionState_Execution_Attempt{ 114 { 115 Status: tryjob.Status_ENDED, 116 ExternalId: string(tryjob.MustBuildbucketID(bbHost, 50000)), 117 Result: &tryjob.Result{ 118 Status: tryjob.Result_FAILED_TRANSIENTLY, 119 Backend: &tryjob.Result_Buildbucket_{ 120 Buildbucket: &tryjob.Result_Buildbucket{ 121 Id: 50000, 122 Builder: builderFoo, 123 }, 124 }, 125 }, 126 Reused: true, 127 }, 128 { 129 Status: tryjob.Status_ENDED, 130 ExternalId: string(tryjob.MustBuildbucketID(bbHost, 49999)), 131 Result: &tryjob.Result{ 132 Status: tryjob.Result_SUCCEEDED, 133 Backend: &tryjob.Result_Buildbucket_{ 134 Buildbucket: &tryjob.Result_Buildbucket{ 135 Id: 49999, 136 Builder: builderFoo, 137 }, 138 }, 139 }, 140 }, 141 }, 142 }, 143 { 144 Attempts: []*tryjob.ExecutionState_Execution_Attempt{ 145 { 146 Status: tryjob.Status_TRIGGERED, 147 ExternalId: string(tryjob.MustBuildbucketID(bbHost, 60000)), 148 Result: &tryjob.Result{ 149 Status: tryjob.Result_UNKNOWN, 150 Backend: &tryjob.Result_Buildbucket_{ 151 Buildbucket: &tryjob.Result_Buildbucket{ 152 Id: 60000, 153 Builder: builderBar, 154 }, 155 }, 156 }, 157 }, 158 }, 159 }, 160 }, 161 }, 162 } 163 ti, err := makeTryjobInvocations(ctx, r) 164 So(err, ShouldBeNil) 165 So(ti, ShouldResembleProto, []*apiv0pb.TryjobInvocation{ 166 { 167 BuilderConfig: cg.GetVerifiers().GetTryjob().GetBuilders()[0], 168 Status: apiv0pb.TryjobStatus_SUCCEEDED, 169 Critical: true, 170 Attempts: []*apiv0pb.TryjobInvocation_Attempt{ 171 { 172 Status: apiv0pb.TryjobStatus_SUCCEEDED, 173 Result: &apiv0pb.TryjobResult{ 174 Backend: &apiv0pb.TryjobResult_Buildbucket_{ 175 Buildbucket: &apiv0pb.TryjobResult_Buildbucket{ 176 Host: bbHost, 177 Id: 49999, 178 Builder: builderFoo, 179 }, 180 }, 181 }, 182 Reuse: false, 183 }, 184 { 185 Status: apiv0pb.TryjobStatus_FAILED, 186 Result: &apiv0pb.TryjobResult{ 187 Backend: &apiv0pb.TryjobResult_Buildbucket_{ 188 Buildbucket: &apiv0pb.TryjobResult_Buildbucket{ 189 Host: bbHost, 190 Id: 50000, 191 Builder: builderFoo, 192 }, 193 }, 194 }, 195 Reuse: true, 196 }, 197 }, 198 }, 199 { 200 BuilderConfig: cg.GetVerifiers().GetTryjob().GetBuilders()[1], 201 Status: apiv0pb.TryjobStatus_RUNNING, 202 Critical: false, 203 Attempts: []*apiv0pb.TryjobInvocation_Attempt{ 204 { 205 Status: apiv0pb.TryjobStatus_RUNNING, 206 Result: &apiv0pb.TryjobResult{ 207 Backend: &apiv0pb.TryjobResult_Buildbucket_{ 208 Buildbucket: &apiv0pb.TryjobResult_Buildbucket{ 209 Host: bbHost, 210 Id: 60000, 211 Builder: builderBar, 212 }, 213 }, 214 }, 215 }, 216 }, 217 }, 218 }) 219 }) 220 221 Convey("Skip if builder is not seen in the config", func() { 222 r.Tryjobs = &run.Tryjobs{ 223 State: &tryjob.ExecutionState{ 224 Requirement: &tryjob.Requirement{ 225 Definitions: []*tryjob.Definition{ 226 { 227 Backend: &tryjob.Definition_Buildbucket_{ 228 Buildbucket: &tryjob.Definition_Buildbucket{ 229 Host: bbHost, 230 Builder: &bbpb.BuilderID{ 231 Project: lProject, 232 Bucket: "try", 233 Builder: "undefined", 234 }, 235 }, 236 }, 237 Critical: true, 238 }, 239 }, 240 }, 241 Executions: []*tryjob.ExecutionState_Execution{ 242 { 243 Attempts: []*tryjob.ExecutionState_Execution_Attempt{ 244 { 245 Status: tryjob.Status_PENDING, 246 Result: &tryjob.Result{ 247 Status: tryjob.Result_UNKNOWN, 248 }, 249 }, 250 }, 251 }, 252 }, 253 }, 254 } 255 ti, err := makeTryjobInvocations(ctx, r) 256 So(err, ShouldBeNil) 257 So(ti, ShouldBeEmpty) 258 }) 259 260 Convey("Omit empty attempt", func() { 261 r.Tryjobs = &run.Tryjobs{ 262 State: &tryjob.ExecutionState{ 263 Requirement: &tryjob.Requirement{ 264 Definitions: []*tryjob.Definition{ 265 { 266 Backend: &tryjob.Definition_Buildbucket_{ 267 Buildbucket: &tryjob.Definition_Buildbucket{ 268 Host: bbHost, 269 Builder: builderFoo, 270 }, 271 }, 272 Critical: true, 273 }, 274 }, 275 }, 276 Executions: []*tryjob.ExecutionState_Execution{ 277 { 278 Attempts: nil, 279 }, 280 }, 281 }, 282 } 283 ti, err := makeTryjobInvocations(ctx, r) 284 So(err, ShouldBeNil) 285 So(ti, ShouldBeEmpty) 286 }) 287 }) 288 }