| // Copyright 2019 The LUCI Authors. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package cli |
| |
| import ( |
| "context" |
| "testing" |
| |
| "github.com/golang/mock/gomock" |
| "google.golang.org/grpc/codes" |
| "google.golang.org/grpc/status" |
| |
| "go.chromium.org/luci/buildbucket" |
| "go.chromium.org/luci/common/proto" |
| |
| pb "go.chromium.org/luci/buildbucket/proto" |
| |
| . "github.com/smartystreets/goconvey/convey" |
| . "go.chromium.org/luci/common/testing/assertions" |
| ) |
| |
| func TestSendBatchReq(t *testing.T) { |
| Convey("SendBatchReq", t, func() { |
| ctl := gomock.NewController(t) |
| defer ctl.Finish() |
| mockBBClient := pb.NewMockBuildsClient(ctl) |
| ctx := context.Background() |
| |
| build := &pb.Build{ |
| Id: 1, |
| Builder: &pb.BuilderID{ |
| Project: "project", |
| Bucket: "bucket", |
| Builder: "builder1", |
| }, |
| Input: &pb.Build_Input{}, |
| } |
| |
| Convey("success", func() { |
| req := &pb.BatchRequest{ |
| Requests: []*pb.BatchRequest_Request{ |
| {Request: &pb.BatchRequest_Request_ScheduleBuild{ |
| ScheduleBuild: &pb.ScheduleBuildRequest{}, |
| }}, |
| }, |
| } |
| expectedRes := &pb.BatchResponse{ |
| Responses: []*pb.BatchResponse_Response{ |
| {Response: &pb.BatchResponse_Response_ScheduleBuild{ |
| ScheduleBuild: build, |
| }}, |
| }, |
| } |
| mockBBClient.EXPECT().Batch(ctx, req).Return(expectedRes, nil) |
| res, err := sendBatchReq(ctx, req, mockBBClient) |
| So(err, ShouldBeNil) |
| So(res, ShouldResembleProto, expectedRes) |
| }) |
| |
| Convey("sub-requests transient errors", func() { |
| req := &pb.BatchRequest{ |
| Requests: []*pb.BatchRequest_Request{ |
| {Request: &pb.BatchRequest_Request_ScheduleBuild{ |
| ScheduleBuild: &pb.ScheduleBuildRequest{}, |
| }}, |
| {Request: &pb.BatchRequest_Request_SearchBuilds{ |
| SearchBuilds: &pb.SearchBuildsRequest{}, |
| }}, |
| {Request: &pb.BatchRequest_Request_GetBuild{ |
| GetBuild: &pb.GetBuildRequest{Id: 1}, |
| }}, |
| }, |
| } |
| expectedRes := &pb.BatchResponse{ |
| Responses: []*pb.BatchResponse_Response{ |
| {Response: &pb.BatchResponse_Response_ScheduleBuild{ |
| ScheduleBuild: build, |
| }}, |
| {Response: &pb.BatchResponse_Response_SearchBuilds{ |
| SearchBuilds: &pb.SearchBuildsResponse{ |
| Builds: []*pb.Build{build}, |
| }, |
| }}, |
| {Response: &pb.BatchResponse_Response_Error{ |
| Error: status.New(codes.InvalidArgument, "bad request").Proto(), |
| }}, |
| }, |
| } |
| |
| // first call |
| mockBBClient.EXPECT().Batch(ctx, req).Times(1).Return(&pb.BatchResponse{ |
| Responses: []*pb.BatchResponse_Response{ |
| {Response: &pb.BatchResponse_Response_ScheduleBuild{ |
| ScheduleBuild: build, |
| }}, |
| {Response: &pb.BatchResponse_Response_Error{ |
| Error: status.New(codes.Internal, "Internal server error").Proto(), |
| }}, |
| {Response: &pb.BatchResponse_Response_Error{ |
| Error: status.New(codes.Internal, "Internal server error").Proto(), |
| }}, |
| }, |
| }, nil) |
| // retry the 2nd and 3rd sub-requests |
| mockBBClient.EXPECT().Batch(ctx, proto.MatcherEqual( |
| &pb.BatchRequest{ |
| Requests: []*pb.BatchRequest_Request{ |
| {Request: &pb.BatchRequest_Request_SearchBuilds{ |
| SearchBuilds: &pb.SearchBuildsRequest{}, |
| }}, |
| {Request: &pb.BatchRequest_Request_GetBuild{ |
| GetBuild: &pb.GetBuildRequest{Id: 1}, |
| }}, |
| }, |
| })).Times(1).Return(&pb.BatchResponse{ |
| Responses: []*pb.BatchResponse_Response{ |
| {Response: &pb.BatchResponse_Response_Error{ |
| Error: status.New(codes.DeadlineExceeded, "timeout").Proto(), |
| }}, |
| {Response: &pb.BatchResponse_Response_Error{ |
| Error: status.New(codes.InvalidArgument, "bad request").Proto(), |
| }}, |
| }, |
| }, nil) |
| // retry the 2nd sub-request again |
| mockBBClient.EXPECT().Batch(ctx, proto.MatcherEqual( |
| &pb.BatchRequest{ |
| Requests: []*pb.BatchRequest_Request{ |
| {Request: &pb.BatchRequest_Request_SearchBuilds{ |
| SearchBuilds: &pb.SearchBuildsRequest{}, |
| }}, |
| }, |
| })).Times(1).Return(&pb.BatchResponse{ |
| Responses: []*pb.BatchResponse_Response{ |
| {Response: &pb.BatchResponse_Response_SearchBuilds{ |
| SearchBuilds: &pb.SearchBuildsResponse{ |
| Builds: []*pb.Build{build}, |
| }, |
| }}, |
| }, |
| }, nil) |
| res, err := sendBatchReq(ctx, req, mockBBClient) |
| So(err, ShouldBeNil) |
| So(res, ShouldResembleProto, expectedRes) |
| }) |
| }) |
| Convey("updateRequest", t, func() { |
| ctx := context.Background() |
| Convey("updateRequest if dummy token", func() { |
| req := &pb.BatchRequest{ |
| Requests: []*pb.BatchRequest_Request{ |
| {Request: &pb.BatchRequest_Request_ScheduleBuild{ |
| ScheduleBuild: &pb.ScheduleBuildRequest{ |
| CanOutliveParent: pb.Trinary_YES, |
| }, |
| }}, |
| }, |
| } |
| updateRequest(ctx, req, buildbucket.DummyBuildbucketToken) |
| So(req.Requests[0].GetScheduleBuild().CanOutliveParent, ShouldEqual, pb.Trinary_UNSET) |
| }) |
| Convey("updateRequest if empty token", func() { |
| req := &pb.BatchRequest{ |
| Requests: []*pb.BatchRequest_Request{ |
| {Request: &pb.BatchRequest_Request_ScheduleBuild{ |
| ScheduleBuild: &pb.ScheduleBuildRequest{ |
| CanOutliveParent: pb.Trinary_YES, |
| }, |
| }}, |
| }, |
| } |
| updateRequest(ctx, req, "") |
| So(req.Requests[0].GetScheduleBuild().CanOutliveParent, ShouldEqual, pb.Trinary_YES) |
| }) |
| Convey("updateRequest if real token", func() { |
| req := &pb.BatchRequest{ |
| Requests: []*pb.BatchRequest_Request{ |
| {Request: &pb.BatchRequest_Request_ScheduleBuild{ |
| ScheduleBuild: &pb.ScheduleBuildRequest{ |
| CanOutliveParent: pb.Trinary_YES, |
| }, |
| }}, |
| }, |
| } |
| updateRequest(ctx, req, "real token") |
| So(req.Requests[0].GetScheduleBuild().CanOutliveParent, ShouldEqual, pb.Trinary_YES) |
| }) |
| }) |
| } |