...

Source file src/cloud.google.com/go/longrunning/longrunning_test.go

Documentation: cloud.google.com/go/longrunning

     1  // Copyright 2016 Google LLC
     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 lro supports Long Running Operations for the Google Cloud Libraries.
    16  //
    17  // This package is still experimental and subject to change.
    18  package longrunning
    19  
    20  import (
    21  	"context"
    22  	"errors"
    23  	"testing"
    24  	"time"
    25  
    26  	pb "cloud.google.com/go/longrunning/autogen/longrunningpb"
    27  	gax "github.com/googleapis/gax-go/v2"
    28  	"github.com/googleapis/gax-go/v2/apierror"
    29  	"google.golang.org/genproto/googleapis/rpc/errdetails"
    30  	rpcstatus "google.golang.org/genproto/googleapis/rpc/status"
    31  	"google.golang.org/grpc"
    32  	"google.golang.org/grpc/codes"
    33  	"google.golang.org/grpc/status"
    34  	"google.golang.org/protobuf/proto"
    35  	"google.golang.org/protobuf/types/known/anypb"
    36  	"google.golang.org/protobuf/types/known/durationpb"
    37  )
    38  
    39  type getterService struct {
    40  	operationsClient
    41  
    42  	// clock represents the fake current time of the service.
    43  	// It is the running sum of the of the duration we have slept.
    44  	clock time.Duration
    45  
    46  	// getTimes records the times at which GetOperation is called.
    47  	getTimes []time.Duration
    48  
    49  	// results are the fake results that GetOperation should return.
    50  	results []*pb.Operation
    51  }
    52  
    53  func (s *getterService) GetOperation(context.Context, *pb.GetOperationRequest, ...gax.CallOption) (*pb.Operation, error) {
    54  	i := len(s.getTimes)
    55  	s.getTimes = append(s.getTimes, s.clock)
    56  	if i >= len(s.results) {
    57  		return nil, errors.New("unexpected call")
    58  	}
    59  	return s.results[i], nil
    60  }
    61  
    62  func (s *getterService) sleeper() sleeper {
    63  	return func(_ context.Context, d time.Duration) error {
    64  		s.clock += d
    65  		return nil
    66  	}
    67  }
    68  
    69  func TestWait(t *testing.T) {
    70  	responseDur := durationpb.New(42 * time.Second)
    71  	responseAny, err := anypb.New(responseDur)
    72  	if err != nil {
    73  		t.Fatal(err)
    74  	}
    75  
    76  	s := &getterService{
    77  		results: []*pb.Operation{
    78  			{Name: "foo"},
    79  			{Name: "foo"},
    80  			{Name: "foo"},
    81  			{Name: "foo"},
    82  			{Name: "foo"},
    83  			{
    84  				Name: "foo",
    85  				Done: true,
    86  				Result: &pb.Operation_Response{
    87  					Response: responseAny,
    88  				},
    89  			},
    90  		},
    91  	}
    92  	op := &Operation{
    93  		c:     s,
    94  		proto: &pb.Operation{Name: "foo"},
    95  	}
    96  	if op.Done() {
    97  		t.Fatal("operation should not have completed yet")
    98  	}
    99  
   100  	var resp durationpb.Duration
   101  	bo := gax.Backoff{
   102  		Initial: 1 * time.Second,
   103  		Max:     3 * time.Second,
   104  	}
   105  	if err := op.wait(context.Background(), &resp, &bo, s.sleeper()); err != nil {
   106  		t.Fatal(err)
   107  	}
   108  	if !proto.Equal(&resp, responseDur) {
   109  		t.Errorf("response, got %v, want %v", resp, responseDur)
   110  	}
   111  	if !op.Done() {
   112  		t.Errorf("operation should have completed")
   113  	}
   114  
   115  	maxWait := []time.Duration{
   116  		1 * time.Second,
   117  		2 * time.Second,
   118  		3 * time.Second,
   119  		3 * time.Second,
   120  		3 * time.Second,
   121  	}
   122  	for i := 0; i < len(s.getTimes)-1; i++ {
   123  		w := s.getTimes[i+1] - s.getTimes[i]
   124  		if mw := maxWait[i]; w > mw {
   125  			t.Errorf("backoff, waited %s, max %s", w, mw)
   126  		}
   127  	}
   128  }
   129  
   130  func TestPollRequestError(t *testing.T) {
   131  	const opName = "foo"
   132  
   133  	// All calls error.
   134  	s := &getterService{}
   135  	op := &Operation{
   136  		c:     s,
   137  		proto: &pb.Operation{Name: opName},
   138  	}
   139  	if err := op.Poll(context.Background(), nil); err == nil {
   140  		t.Fatalf("Poll should error")
   141  	}
   142  	if n := op.Name(); n != opName {
   143  		t.Errorf("operation name, got %q, want %q", n, opName)
   144  	}
   145  	if op.Done() {
   146  		t.Errorf("operation should not have completed; we failed to fetch state")
   147  	}
   148  }
   149  
   150  func TestPollErrorResult(t *testing.T) {
   151  	const (
   152  		errCode = codes.NotFound
   153  		errMsg  = "my error"
   154  	)
   155  	details := &errdetails.ErrorInfo{Reason: "things happen"}
   156  	a, err := anypb.New(details)
   157  	if err != nil {
   158  		t.Fatalf("anypb.New() = %v", err)
   159  	}
   160  	op := &Operation{
   161  		proto: &pb.Operation{
   162  			Name: "foo",
   163  			Done: true,
   164  			Result: &pb.Operation_Error{
   165  				Error: &rpcstatus.Status{
   166  					Code:    int32(errCode),
   167  					Message: errMsg,
   168  					Details: []*anypb.Any{a},
   169  				},
   170  			},
   171  		},
   172  	}
   173  	err = op.Poll(context.Background(), nil)
   174  	if got := status.Code(err); got != errCode {
   175  		t.Errorf("error code, want %s, got %s", errCode, got)
   176  	}
   177  	if got := grpc.ErrorDesc(err); got != errMsg {
   178  		t.Errorf("error code, want %s, got %s", errMsg, got)
   179  	}
   180  	if !op.Done() {
   181  		t.Errorf("operation should have completed")
   182  	}
   183  	var ae *apierror.APIError
   184  	errors.As(err, &ae)
   185  	if got := ae.Details().ErrorInfo.Reason; got != details.Reason {
   186  		t.Errorf("got %q, want %q", got, details.Reason)
   187  	}
   188  }
   189  
   190  type errService struct {
   191  	operationsClient
   192  	errCancel, errDelete error
   193  }
   194  
   195  func (s *errService) CancelOperation(context.Context, *pb.CancelOperationRequest, ...gax.CallOption) error {
   196  	return s.errCancel
   197  }
   198  
   199  func (s *errService) DeleteOperation(context.Context, *pb.DeleteOperationRequest, ...gax.CallOption) error {
   200  	return s.errDelete
   201  }
   202  
   203  func TestCancelReturnsError(t *testing.T) {
   204  	s := &errService{
   205  		errCancel: errors.New("cancel error"),
   206  	}
   207  	op := &Operation{
   208  		c:     s,
   209  		proto: &pb.Operation{Name: "foo"},
   210  	}
   211  	if got, want := op.Cancel(context.Background()), s.errCancel; got != want {
   212  		t.Errorf("cancel, got error %s, want %s", got, want)
   213  	}
   214  }
   215  
   216  func TestDeleteReturnsError(t *testing.T) {
   217  	s := &errService{
   218  		errDelete: errors.New("delete error"),
   219  	}
   220  	op := &Operation{
   221  		c:     s,
   222  		proto: &pb.Operation{Name: "foo"},
   223  	}
   224  	if got, want := op.Delete(context.Background()), s.errDelete; got != want {
   225  		t.Errorf("cancel, got error %s, want %s", got, want)
   226  	}
   227  }
   228  

View as plain text