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  }