go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/buildbucket/cli/batch_test.go (about)

     1  // Copyright 2019 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 cli
    16  
    17  import (
    18  	"context"
    19  	"testing"
    20  
    21  	"github.com/golang/mock/gomock"
    22  	"google.golang.org/grpc/codes"
    23  	"google.golang.org/grpc/status"
    24  
    25  	"go.chromium.org/luci/buildbucket"
    26  	"go.chromium.org/luci/common/proto"
    27  
    28  	pb "go.chromium.org/luci/buildbucket/proto"
    29  
    30  	. "github.com/smartystreets/goconvey/convey"
    31  	. "go.chromium.org/luci/common/testing/assertions"
    32  )
    33  
    34  func TestSendBatchReq(t *testing.T) {
    35  	Convey("SendBatchReq", t, func() {
    36  		ctl := gomock.NewController(t)
    37  		defer ctl.Finish()
    38  		mockBBClient := pb.NewMockBuildsClient(ctl)
    39  		ctx := context.Background()
    40  
    41  		build := &pb.Build{
    42  			Id: 1,
    43  			Builder: &pb.BuilderID{
    44  				Project: "project",
    45  				Bucket:  "bucket",
    46  				Builder: "builder1",
    47  			},
    48  			Input: &pb.Build_Input{},
    49  		}
    50  
    51  		Convey("success", func() {
    52  			req := &pb.BatchRequest{
    53  				Requests: []*pb.BatchRequest_Request{
    54  					{Request: &pb.BatchRequest_Request_ScheduleBuild{
    55  						ScheduleBuild: &pb.ScheduleBuildRequest{},
    56  					}},
    57  				},
    58  			}
    59  			expectedRes := &pb.BatchResponse{
    60  				Responses: []*pb.BatchResponse_Response{
    61  					{Response: &pb.BatchResponse_Response_ScheduleBuild{
    62  						ScheduleBuild: build,
    63  					}},
    64  				},
    65  			}
    66  			mockBBClient.EXPECT().Batch(ctx, req).Return(expectedRes, nil)
    67  			res, err := sendBatchReq(ctx, req, mockBBClient)
    68  			So(err, ShouldBeNil)
    69  			So(res, ShouldResembleProto, expectedRes)
    70  		})
    71  
    72  		Convey("sub-requests transient errors", func() {
    73  			req := &pb.BatchRequest{
    74  				Requests: []*pb.BatchRequest_Request{
    75  					{Request: &pb.BatchRequest_Request_ScheduleBuild{
    76  						ScheduleBuild: &pb.ScheduleBuildRequest{},
    77  					}},
    78  					{Request: &pb.BatchRequest_Request_SearchBuilds{
    79  						SearchBuilds: &pb.SearchBuildsRequest{},
    80  					}},
    81  					{Request: &pb.BatchRequest_Request_GetBuild{
    82  						GetBuild: &pb.GetBuildRequest{Id: 1},
    83  					}},
    84  				},
    85  			}
    86  			expectedRes := &pb.BatchResponse{
    87  				Responses: []*pb.BatchResponse_Response{
    88  					{Response: &pb.BatchResponse_Response_ScheduleBuild{
    89  						ScheduleBuild: build,
    90  					}},
    91  					{Response: &pb.BatchResponse_Response_SearchBuilds{
    92  						SearchBuilds: &pb.SearchBuildsResponse{
    93  							Builds: []*pb.Build{build},
    94  						},
    95  					}},
    96  					{Response: &pb.BatchResponse_Response_Error{
    97  						Error: status.New(codes.InvalidArgument, "bad request").Proto(),
    98  					}},
    99  				},
   100  			}
   101  
   102  			// first call
   103  			mockBBClient.EXPECT().Batch(ctx, req).Times(1).Return(&pb.BatchResponse{
   104  				Responses: []*pb.BatchResponse_Response{
   105  					{Response: &pb.BatchResponse_Response_ScheduleBuild{
   106  						ScheduleBuild: build,
   107  					}},
   108  					{Response: &pb.BatchResponse_Response_Error{
   109  						Error: status.New(codes.Internal, "Internal server error").Proto(),
   110  					}},
   111  					{Response: &pb.BatchResponse_Response_Error{
   112  						Error: status.New(codes.Internal, "Internal server error").Proto(),
   113  					}},
   114  				},
   115  			}, nil)
   116  			// retry the 2nd and 3rd sub-requests
   117  			mockBBClient.EXPECT().Batch(ctx, proto.MatcherEqual(
   118  				&pb.BatchRequest{
   119  					Requests: []*pb.BatchRequest_Request{
   120  						{Request: &pb.BatchRequest_Request_SearchBuilds{
   121  							SearchBuilds: &pb.SearchBuildsRequest{},
   122  						}},
   123  						{Request: &pb.BatchRequest_Request_GetBuild{
   124  							GetBuild: &pb.GetBuildRequest{Id: 1},
   125  						}},
   126  					},
   127  				})).Times(1).Return(&pb.BatchResponse{
   128  				Responses: []*pb.BatchResponse_Response{
   129  					{Response: &pb.BatchResponse_Response_Error{
   130  						Error: status.New(codes.DeadlineExceeded, "timeout").Proto(),
   131  					}},
   132  					{Response: &pb.BatchResponse_Response_Error{
   133  						Error: status.New(codes.InvalidArgument, "bad request").Proto(),
   134  					}},
   135  				},
   136  			}, nil)
   137  			// retry the 2nd sub-request again
   138  			mockBBClient.EXPECT().Batch(ctx, proto.MatcherEqual(
   139  				&pb.BatchRequest{
   140  					Requests: []*pb.BatchRequest_Request{
   141  						{Request: &pb.BatchRequest_Request_SearchBuilds{
   142  							SearchBuilds: &pb.SearchBuildsRequest{},
   143  						}},
   144  					},
   145  				})).Times(1).Return(&pb.BatchResponse{
   146  				Responses: []*pb.BatchResponse_Response{
   147  					{Response: &pb.BatchResponse_Response_SearchBuilds{
   148  						SearchBuilds: &pb.SearchBuildsResponse{
   149  							Builds: []*pb.Build{build},
   150  						},
   151  					}},
   152  				},
   153  			}, nil)
   154  			res, err := sendBatchReq(ctx, req, mockBBClient)
   155  			So(err, ShouldBeNil)
   156  			So(res, ShouldResembleProto, expectedRes)
   157  		})
   158  	})
   159  	Convey("updateRequest", t, func() {
   160  		ctx := context.Background()
   161  		Convey("updateRequest if dummy token", func() {
   162  			req := &pb.BatchRequest{
   163  				Requests: []*pb.BatchRequest_Request{
   164  					{Request: &pb.BatchRequest_Request_ScheduleBuild{
   165  						ScheduleBuild: &pb.ScheduleBuildRequest{
   166  							CanOutliveParent: pb.Trinary_YES,
   167  						},
   168  					}},
   169  				},
   170  			}
   171  			updateRequest(ctx, req, buildbucket.DummyBuildbucketToken)
   172  			So(req.Requests[0].GetScheduleBuild().CanOutliveParent, ShouldEqual, pb.Trinary_UNSET)
   173  		})
   174  		Convey("updateRequest if empty token", func() {
   175  			req := &pb.BatchRequest{
   176  				Requests: []*pb.BatchRequest_Request{
   177  					{Request: &pb.BatchRequest_Request_ScheduleBuild{
   178  						ScheduleBuild: &pb.ScheduleBuildRequest{
   179  							CanOutliveParent: pb.Trinary_YES,
   180  						},
   181  					}},
   182  				},
   183  			}
   184  			updateRequest(ctx, req, "")
   185  			So(req.Requests[0].GetScheduleBuild().CanOutliveParent, ShouldEqual, pb.Trinary_YES)
   186  		})
   187  		Convey("updateRequest if real token", func() {
   188  			req := &pb.BatchRequest{
   189  				Requests: []*pb.BatchRequest_Request{
   190  					{Request: &pb.BatchRequest_Request_ScheduleBuild{
   191  						ScheduleBuild: &pb.ScheduleBuildRequest{
   192  							CanOutliveParent: pb.Trinary_YES,
   193  						},
   194  					}},
   195  				},
   196  			}
   197  			updateRequest(ctx, req, "real token")
   198  			So(req.Requests[0].GetScheduleBuild().CanOutliveParent, ShouldEqual, pb.Trinary_YES)
   199  		})
   200  	})
   201  }