go.temporal.io/server@v1.23.0/common/archiver/s3store/history_archiver_test.go (about)

     1  // The MIT License
     2  //
     3  // Copyright (c) 2020 Temporal Technologies Inc.  All rights reserved.
     4  //
     5  // Copyright (c) 2020 Uber Technologies, Inc.
     6  //
     7  // Permission is hereby granted, free of charge, to any person obtaining a copy
     8  // of this software and associated documentation files (the "Software"), to deal
     9  // in the Software without restriction, including without limitation the rights
    10  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    11  // copies of the Software, and to permit persons to whom the Software is
    12  // furnished to do so, subject to the following conditions:
    13  //
    14  // The above copyright notice and this permission notice shall be included in
    15  // all copies or substantial portions of the Software.
    16  //
    17  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    18  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    19  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    20  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    21  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    22  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    23  // THE SOFTWARE.
    24  
    25  package s3store
    26  
    27  import (
    28  	"bytes"
    29  	"context"
    30  	"errors"
    31  	"fmt"
    32  	"io"
    33  	"sort"
    34  	"strconv"
    35  	"strings"
    36  	"testing"
    37  	"time"
    38  
    39  	"github.com/aws/aws-sdk-go/aws"
    40  	"github.com/aws/aws-sdk-go/aws/awserr"
    41  	"github.com/aws/aws-sdk-go/aws/request"
    42  	"github.com/aws/aws-sdk-go/service/s3"
    43  	"github.com/golang/mock/gomock"
    44  	"github.com/stretchr/testify/require"
    45  	"github.com/stretchr/testify/suite"
    46  	enumspb "go.temporal.io/api/enums/v1"
    47  	historypb "go.temporal.io/api/history/v1"
    48  	"go.temporal.io/api/serviceerror"
    49  	"google.golang.org/protobuf/types/known/timestamppb"
    50  
    51  	archiverspb "go.temporal.io/server/api/archiver/v1"
    52  	"go.temporal.io/server/common"
    53  	"go.temporal.io/server/common/archiver"
    54  	"go.temporal.io/server/common/archiver/s3store/mocks"
    55  	"go.temporal.io/server/common/codec"
    56  	"go.temporal.io/server/common/convert"
    57  	"go.temporal.io/server/common/log"
    58  	"go.temporal.io/server/common/metrics"
    59  )
    60  
    61  const (
    62  	testNamespaceID          = "test-namespace-id"
    63  	testNamespace            = "test-namespace"
    64  	testWorkflowID           = "test-workflow-id"
    65  	testRunID                = "test-run-id"
    66  	testNextEventID          = 1800
    67  	testCloseFailoverVersion = int64(100)
    68  	testPageSize             = 100
    69  	testBucket               = "test-bucket"
    70  	testBucketURI            = "s3://test-bucket"
    71  )
    72  
    73  var testBranchToken = []byte{1, 2, 3}
    74  
    75  type historyArchiverSuite struct {
    76  	*require.Assertions
    77  	suite.Suite
    78  	s3cli              *mocks.MockS3API
    79  	container          *archiver.HistoryBootstrapContainer
    80  	testArchivalURI    archiver.URI
    81  	historyBatchesV1   []*archiverspb.HistoryBlob
    82  	historyBatchesV100 []*archiverspb.HistoryBlob
    83  	controller         *gomock.Controller
    84  }
    85  
    86  func TestHistoryArchiverSuite(t *testing.T) {
    87  	suite.Run(t, new(historyArchiverSuite))
    88  }
    89  
    90  func (s *historyArchiverSuite) SetupSuite() {
    91  	var err error
    92  	s.testArchivalURI, err = archiver.NewURI(testBucketURI)
    93  	s.Require().NoError(err)
    94  }
    95  
    96  func (s *historyArchiverSuite) TearDownSuite() {
    97  }
    98  
    99  func (s *historyArchiverSuite) SetupTest() {
   100  	s.Assertions = require.New(s.T())
   101  	s.container = &archiver.HistoryBootstrapContainer{
   102  		Logger:         log.NewNoopLogger(),
   103  		MetricsHandler: metrics.NoopMetricsHandler,
   104  	}
   105  
   106  	s.controller = gomock.NewController(s.T())
   107  	s.s3cli = mocks.NewMockS3API(s.controller)
   108  	setupFsEmulation(s.s3cli)
   109  	s.setupHistoryDirectory()
   110  }
   111  
   112  func setupFsEmulation(s3cli *mocks.MockS3API) {
   113  	fs := make(map[string][]byte)
   114  
   115  	putObjectFn := func(_ aws.Context, input *s3.PutObjectInput, _ ...request.Option) (*s3.PutObjectOutput, error) {
   116  		buf := new(bytes.Buffer)
   117  		if _, err := buf.ReadFrom(input.Body); err != nil {
   118  			return nil, err
   119  		}
   120  		fs[*input.Bucket+*input.Key] = buf.Bytes()
   121  		return &s3.PutObjectOutput{}, nil
   122  	}
   123  
   124  	s3cli.EXPECT().ListObjectsV2WithContext(gomock.Any(), gomock.Any()).DoAndReturn(
   125  		func(_ context.Context, input *s3.ListObjectsV2Input, opts ...request.Option) (*s3.ListObjectsV2Output, error) {
   126  			objects := make([]*s3.Object, 0)
   127  			commonPrefixMap := map[string]bool{}
   128  			for k := range fs {
   129  				if strings.HasPrefix(k, *input.Bucket+*input.Prefix) {
   130  					key := k[len(*input.Bucket):]
   131  					keyWithoutPrefix := key[len(*input.Prefix):]
   132  					index := strings.Index(keyWithoutPrefix, "/")
   133  					if index == -1 || input.Delimiter == nil {
   134  						objects = append(objects, &s3.Object{
   135  							Key: aws.String(key),
   136  						})
   137  					} else {
   138  						commonPrefixMap[key[:len(*input.Prefix)+index]] = true
   139  					}
   140  				}
   141  			}
   142  			commonPrefixes := make([]*s3.CommonPrefix, 0)
   143  			for k := range commonPrefixMap {
   144  				commonPrefixes = append(commonPrefixes, &s3.CommonPrefix{
   145  					Prefix: aws.String(k),
   146  				})
   147  			}
   148  
   149  			sort.SliceStable(objects, func(i, j int) bool {
   150  				return *objects[i].Key < *objects[j].Key
   151  			})
   152  			maxKeys := 1000
   153  			if input.MaxKeys != nil {
   154  				maxKeys = int(*input.MaxKeys)
   155  			}
   156  			start := 0
   157  			if input.ContinuationToken != nil {
   158  				start, _ = strconv.Atoi(*input.ContinuationToken)
   159  			}
   160  
   161  			if input.StartAfter != nil {
   162  				for k, v := range objects {
   163  					if *input.StartAfter == *v.Key {
   164  						start = k + 1
   165  					}
   166  				}
   167  			}
   168  
   169  			isTruncated := false
   170  			var nextContinuationToken *string
   171  			if len(objects) > start+maxKeys {
   172  				isTruncated = true
   173  				nextContinuationToken = convert.StringPtr(fmt.Sprintf("%d", start+maxKeys))
   174  				objects = objects[start : start+maxKeys]
   175  			} else {
   176  				objects = objects[start:]
   177  			}
   178  
   179  			if input.StartAfter != nil {
   180  				for k, v := range commonPrefixes {
   181  					if *input.StartAfter == *v.Prefix {
   182  						start = k + 1
   183  					}
   184  				}
   185  			}
   186  
   187  			if len(commonPrefixes) > start+maxKeys {
   188  				isTruncated = true
   189  				nextContinuationToken = convert.StringPtr(fmt.Sprintf("%d", start+maxKeys))
   190  				commonPrefixes = commonPrefixes[start : start+maxKeys]
   191  			} else if len(commonPrefixes) > 0 {
   192  				commonPrefixes = commonPrefixes[start:]
   193  			}
   194  
   195  			return &s3.ListObjectsV2Output{
   196  				CommonPrefixes:        commonPrefixes,
   197  				Contents:              objects,
   198  				IsTruncated:           &isTruncated,
   199  				NextContinuationToken: nextContinuationToken,
   200  			}, nil
   201  		}).AnyTimes()
   202  	s3cli.EXPECT().PutObjectWithContext(gomock.Any(), gomock.Any()).DoAndReturn(putObjectFn).AnyTimes()
   203  
   204  	s3cli.EXPECT().HeadObjectWithContext(gomock.Any(), gomock.Any()).DoAndReturn(
   205  		func(ctx aws.Context, input *s3.HeadObjectInput, options ...request.Option) (*s3.HeadObjectOutput, error) {
   206  			_, ok := fs[*input.Bucket+*input.Key]
   207  			if !ok {
   208  				return nil, awserr.New("NotFound", "", nil)
   209  			}
   210  
   211  			return &s3.HeadObjectOutput{}, nil
   212  		}).AnyTimes()
   213  
   214  	s3cli.EXPECT().GetObjectWithContext(gomock.Any(), gomock.Any()).DoAndReturn(
   215  		func(ctx aws.Context, input *s3.GetObjectInput, options ...request.Option) (*s3.GetObjectOutput, error) {
   216  			_, ok := fs[*input.Bucket+*input.Key]
   217  			if !ok {
   218  				return nil, awserr.New(s3.ErrCodeNoSuchKey, "", nil)
   219  			}
   220  
   221  			return &s3.GetObjectOutput{
   222  				Body: io.NopCloser(bytes.NewReader(fs[*input.Bucket+*input.Key])),
   223  			}, nil
   224  		}).AnyTimes()
   225  }
   226  
   227  func (s *historyArchiverSuite) TestValidateURI() {
   228  	testCases := []struct {
   229  		URI         string
   230  		expectedErr error
   231  	}{
   232  		{
   233  			URI:         "wrongscheme:///a/b/c",
   234  			expectedErr: archiver.ErrURISchemeMismatch,
   235  		},
   236  		{
   237  			URI:         "s3://",
   238  			expectedErr: errNoBucketSpecified,
   239  		},
   240  		{
   241  			URI:         "s3://bucket/a/b/c",
   242  			expectedErr: errBucketNotExists,
   243  		},
   244  		{
   245  			URI:         testBucketURI,
   246  			expectedErr: nil,
   247  		},
   248  	}
   249  
   250  	s.s3cli.EXPECT().HeadBucketWithContext(gomock.Any(), gomock.Any()).DoAndReturn(
   251  		func(ctx aws.Context, input *s3.HeadBucketInput, options ...request.Option) (*s3.HeadBucketOutput, error) {
   252  			if *input.Bucket != s.testArchivalURI.Hostname() {
   253  				return nil, awserr.New("NotFound", "", nil)
   254  			}
   255  
   256  			return &s3.HeadBucketOutput{}, nil
   257  		}).AnyTimes()
   258  
   259  	historyArchiver := s.newTestHistoryArchiver(nil)
   260  	for _, tc := range testCases {
   261  		URI, err := archiver.NewURI(tc.URI)
   262  		s.NoError(err)
   263  		s.Equal(tc.expectedErr, historyArchiver.ValidateURI(URI))
   264  	}
   265  }
   266  
   267  func (s *historyArchiverSuite) TestArchive_Fail_InvalidURI() {
   268  	historyArchiver := s.newTestHistoryArchiver(nil)
   269  	request := &archiver.ArchiveHistoryRequest{
   270  		NamespaceID:          testNamespaceID,
   271  		Namespace:            testNamespace,
   272  		WorkflowID:           testWorkflowID,
   273  		RunID:                testRunID,
   274  		BranchToken:          testBranchToken,
   275  		NextEventID:          testNextEventID,
   276  		CloseFailoverVersion: testCloseFailoverVersion,
   277  	}
   278  	URI, err := archiver.NewURI("wrongscheme://")
   279  	s.NoError(err)
   280  	err = historyArchiver.Archive(context.Background(), URI, request)
   281  	s.Error(err)
   282  }
   283  
   284  func (s *historyArchiverSuite) TestArchive_Fail_InvalidRequest() {
   285  	historyArchiver := s.newTestHistoryArchiver(nil)
   286  	request := &archiver.ArchiveHistoryRequest{
   287  		NamespaceID:          testNamespaceID,
   288  		Namespace:            testNamespace,
   289  		WorkflowID:           "", // an invalid request
   290  		RunID:                testRunID,
   291  		BranchToken:          testBranchToken,
   292  		NextEventID:          testNextEventID,
   293  		CloseFailoverVersion: testCloseFailoverVersion,
   294  	}
   295  	err := historyArchiver.Archive(context.Background(), s.testArchivalURI, request)
   296  	s.Error(err)
   297  }
   298  
   299  func (s *historyArchiverSuite) TestArchive_Fail_ErrorOnReadHistory() {
   300  	historyIterator := archiver.NewMockHistoryIterator(s.controller)
   301  	gomock.InOrder(
   302  		historyIterator.EXPECT().HasNext().Return(true),
   303  		historyIterator.EXPECT().Next(gomock.Any()).Return(nil, errors.New("some random error")),
   304  	)
   305  
   306  	historyArchiver := s.newTestHistoryArchiver(historyIterator)
   307  	request := &archiver.ArchiveHistoryRequest{
   308  		NamespaceID:          testNamespaceID,
   309  		Namespace:            testNamespace,
   310  		WorkflowID:           testWorkflowID,
   311  		RunID:                testRunID,
   312  		BranchToken:          testBranchToken,
   313  		NextEventID:          testNextEventID,
   314  		CloseFailoverVersion: testCloseFailoverVersion,
   315  	}
   316  	err := historyArchiver.Archive(context.Background(), s.testArchivalURI, request)
   317  	s.Error(err)
   318  }
   319  
   320  func (s *historyArchiverSuite) TestArchive_Fail_TimeoutWhenReadingHistory() {
   321  	historyIterator := archiver.NewMockHistoryIterator(s.controller)
   322  	gomock.InOrder(
   323  		historyIterator.EXPECT().HasNext().Return(true),
   324  		historyIterator.EXPECT().Next(gomock.Any()).Return(nil, serviceerror.NewResourceExhausted(enumspb.RESOURCE_EXHAUSTED_CAUSE_RPS_LIMIT, "")),
   325  	)
   326  
   327  	historyArchiver := s.newTestHistoryArchiver(historyIterator)
   328  	request := &archiver.ArchiveHistoryRequest{
   329  		NamespaceID:          testNamespaceID,
   330  		Namespace:            testNamespace,
   331  		WorkflowID:           testWorkflowID,
   332  		RunID:                testRunID,
   333  		BranchToken:          testBranchToken,
   334  		NextEventID:          testNextEventID,
   335  		CloseFailoverVersion: testCloseFailoverVersion,
   336  	}
   337  	err := historyArchiver.Archive(getCanceledContext(), s.testArchivalURI, request)
   338  	s.Error(err)
   339  }
   340  
   341  func (s *historyArchiverSuite) TestArchive_Fail_HistoryMutated() {
   342  	historyIterator := archiver.NewMockHistoryIterator(s.controller)
   343  	historyBatches := []*historypb.History{
   344  		{
   345  			Events: []*historypb.HistoryEvent{
   346  				{
   347  					EventId:   common.FirstEventID + 1,
   348  					EventTime: timestamppb.New(time.Now().UTC()),
   349  					Version:   testCloseFailoverVersion + 1,
   350  				},
   351  			},
   352  		},
   353  	}
   354  	historyBlob := &archiverspb.HistoryBlob{
   355  		Header: &archiverspb.HistoryBlobHeader{
   356  			IsLast: true,
   357  		},
   358  		Body: historyBatches,
   359  	}
   360  	gomock.InOrder(
   361  		historyIterator.EXPECT().HasNext().Return(true),
   362  		historyIterator.EXPECT().Next(gomock.Any()).Return(historyBlob, nil),
   363  	)
   364  
   365  	historyArchiver := s.newTestHistoryArchiver(historyIterator)
   366  	request := &archiver.ArchiveHistoryRequest{
   367  		NamespaceID:          testNamespaceID,
   368  		Namespace:            testNamespace,
   369  		WorkflowID:           testWorkflowID,
   370  		RunID:                testRunID,
   371  		BranchToken:          testBranchToken,
   372  		NextEventID:          testNextEventID,
   373  		CloseFailoverVersion: testCloseFailoverVersion,
   374  	}
   375  	err := historyArchiver.Archive(context.Background(), s.testArchivalURI, request)
   376  	s.Error(err)
   377  }
   378  
   379  func (s *historyArchiverSuite) TestArchive_Fail_NonRetryableErrorOption() {
   380  	historyIterator := archiver.NewMockHistoryIterator(s.controller)
   381  	gomock.InOrder(
   382  		historyIterator.EXPECT().HasNext().Return(true),
   383  		historyIterator.EXPECT().Next(gomock.Any()).Return(nil, errors.New("some random error")),
   384  	)
   385  
   386  	historyArchiver := s.newTestHistoryArchiver(historyIterator)
   387  	request := &archiver.ArchiveHistoryRequest{
   388  		NamespaceID:          testNamespaceID,
   389  		Namespace:            testNamespace,
   390  		WorkflowID:           testWorkflowID,
   391  		RunID:                testRunID,
   392  		BranchToken:          testBranchToken,
   393  		NextEventID:          testNextEventID,
   394  		CloseFailoverVersion: testCloseFailoverVersion,
   395  	}
   396  	nonRetryableErr := errors.New("some non-retryable error")
   397  	err := historyArchiver.Archive(context.Background(), s.testArchivalURI, request, archiver.GetNonRetryableErrorOption(nonRetryableErr))
   398  	s.Equal(nonRetryableErr, err)
   399  }
   400  
   401  func (s *historyArchiverSuite) TestArchive_Skip() {
   402  	historyIterator := archiver.NewMockHistoryIterator(s.controller)
   403  	historyBlob := &archiverspb.HistoryBlob{
   404  		Header: &archiverspb.HistoryBlobHeader{
   405  			IsLast: false,
   406  		},
   407  		Body: []*historypb.History{
   408  			{
   409  				Events: []*historypb.HistoryEvent{
   410  					{
   411  						EventId:   common.FirstEventID,
   412  						EventTime: timestamppb.New(time.Now().UTC()),
   413  						Version:   testCloseFailoverVersion,
   414  					},
   415  				},
   416  			},
   417  		},
   418  	}
   419  	gomock.InOrder(
   420  		historyIterator.EXPECT().HasNext().Return(true),
   421  		historyIterator.EXPECT().Next(gomock.Any()).Return(historyBlob, nil),
   422  		historyIterator.EXPECT().HasNext().Return(true),
   423  		historyIterator.EXPECT().Next(gomock.Any()).Return(nil, serviceerror.NewNotFound("workflow not found")),
   424  	)
   425  
   426  	historyArchiver := s.newTestHistoryArchiver(historyIterator)
   427  	request := &archiver.ArchiveHistoryRequest{
   428  		NamespaceID:          testNamespaceID,
   429  		Namespace:            testNamespace,
   430  		WorkflowID:           testWorkflowID,
   431  		RunID:                testRunID,
   432  		BranchToken:          testBranchToken,
   433  		NextEventID:          testNextEventID,
   434  		CloseFailoverVersion: testCloseFailoverVersion,
   435  	}
   436  	URI, err := archiver.NewURI(testBucketURI + "/TestArchive_Skip")
   437  	s.NoError(err)
   438  	err = historyArchiver.Archive(context.Background(), URI, request)
   439  	s.NoError(err)
   440  
   441  	expectedkey := constructHistoryKey("", testNamespaceID, testWorkflowID, testRunID, testCloseFailoverVersion, 0)
   442  	s.assertKeyExists(expectedkey)
   443  }
   444  
   445  func (s *historyArchiverSuite) TestArchive_Success() {
   446  	historyIterator := archiver.NewMockHistoryIterator(s.controller)
   447  	historyBatches := []*historypb.History{
   448  		{
   449  			Events: []*historypb.HistoryEvent{
   450  				{
   451  					EventId:   common.FirstEventID + 1,
   452  					EventTime: timestamppb.New(time.Now().UTC()),
   453  					Version:   testCloseFailoverVersion,
   454  				},
   455  				{
   456  					EventId:   common.FirstEventID + 2,
   457  					EventTime: timestamppb.New(time.Now().UTC()),
   458  					Version:   testCloseFailoverVersion,
   459  				},
   460  			},
   461  		},
   462  		{
   463  			Events: []*historypb.HistoryEvent{
   464  				{
   465  					EventId:   testNextEventID - 1,
   466  					EventTime: timestamppb.New(time.Now().UTC()),
   467  					Version:   testCloseFailoverVersion,
   468  				},
   469  			},
   470  		},
   471  	}
   472  	historyBlob := &archiverspb.HistoryBlob{
   473  		Header: &archiverspb.HistoryBlobHeader{
   474  			IsLast: true,
   475  		},
   476  		Body: historyBatches,
   477  	}
   478  	gomock.InOrder(
   479  		historyIterator.EXPECT().HasNext().Return(true),
   480  		historyIterator.EXPECT().Next(gomock.Any()).Return(historyBlob, nil),
   481  		historyIterator.EXPECT().HasNext().Return(false),
   482  	)
   483  
   484  	historyArchiver := s.newTestHistoryArchiver(historyIterator)
   485  	request := &archiver.ArchiveHistoryRequest{
   486  		NamespaceID:          testNamespaceID,
   487  		Namespace:            testNamespace,
   488  		WorkflowID:           testWorkflowID,
   489  		RunID:                testRunID,
   490  		BranchToken:          testBranchToken,
   491  		NextEventID:          testNextEventID,
   492  		CloseFailoverVersion: testCloseFailoverVersion,
   493  	}
   494  	URI, err := archiver.NewURI(testBucketURI + "/TestArchive_Success")
   495  	s.NoError(err)
   496  	err = historyArchiver.Archive(context.Background(), URI, request)
   497  	s.NoError(err)
   498  
   499  	expectedkey := constructHistoryKey("", testNamespaceID, testWorkflowID, testRunID, testCloseFailoverVersion, 0)
   500  	s.assertKeyExists(expectedkey)
   501  }
   502  
   503  func (s *historyArchiverSuite) TestGet_Fail_InvalidURI() {
   504  	historyArchiver := s.newTestHistoryArchiver(nil)
   505  	request := &archiver.GetHistoryRequest{
   506  		NamespaceID: testNamespaceID,
   507  		WorkflowID:  testWorkflowID,
   508  		RunID:       testRunID,
   509  		PageSize:    100,
   510  	}
   511  	URI, err := archiver.NewURI("wrongscheme://")
   512  	s.NoError(err)
   513  	response, err := historyArchiver.Get(context.Background(), URI, request)
   514  	s.Nil(response)
   515  	s.Error(err)
   516  }
   517  
   518  func (s *historyArchiverSuite) TestGet_Fail_InvalidRequest() {
   519  	historyArchiver := s.newTestHistoryArchiver(nil)
   520  	request := &archiver.GetHistoryRequest{
   521  		NamespaceID: testNamespaceID,
   522  		WorkflowID:  testWorkflowID,
   523  		RunID:       testRunID,
   524  		PageSize:    0, // pageSize should be greater than 0
   525  	}
   526  	response, err := historyArchiver.Get(context.Background(), s.testArchivalURI, request)
   527  	s.Nil(response)
   528  	s.Error(err)
   529  	s.IsType(&serviceerror.InvalidArgument{}, err)
   530  }
   531  
   532  func (s *historyArchiverSuite) TestGet_Fail_InvalidToken() {
   533  	historyArchiver := s.newTestHistoryArchiver(nil)
   534  	request := &archiver.GetHistoryRequest{
   535  		NamespaceID:   testNamespaceID,
   536  		WorkflowID:    testWorkflowID,
   537  		RunID:         testRunID,
   538  		PageSize:      testPageSize,
   539  		NextPageToken: []byte{'r', 'a', 'n', 'd', 'o', 'm'},
   540  	}
   541  	URI, err := archiver.NewURI(testBucketURI)
   542  	s.NoError(err)
   543  	response, err := historyArchiver.Get(context.Background(), URI, request)
   544  	s.Nil(response)
   545  	s.Error(err)
   546  	s.IsType(&serviceerror.InvalidArgument{}, err)
   547  }
   548  
   549  func (s *historyArchiverSuite) TestGet_Fail_KeyNotExist() {
   550  	historyArchiver := s.newTestHistoryArchiver(nil)
   551  	testCloseFailoverVersion := testCloseFailoverVersion
   552  	request := &archiver.GetHistoryRequest{
   553  		NamespaceID:          testNamespaceID,
   554  		WorkflowID:           testWorkflowID,
   555  		RunID:                testRunID,
   556  		PageSize:             testPageSize,
   557  		CloseFailoverVersion: &testCloseFailoverVersion,
   558  	}
   559  	URI, err := archiver.NewURI("s3://test-bucket/non-existent")
   560  	s.NoError(err)
   561  	response, err := historyArchiver.Get(context.Background(), URI, request)
   562  	s.Nil(response)
   563  	s.Error(err)
   564  	s.IsType(&serviceerror.NotFound{}, err)
   565  }
   566  
   567  func (s *historyArchiverSuite) TestGet_Success_PickHighestVersion() {
   568  	historyArchiver := s.newTestHistoryArchiver(nil)
   569  	request := &archiver.GetHistoryRequest{
   570  		NamespaceID: testNamespaceID,
   571  		WorkflowID:  testWorkflowID,
   572  		RunID:       testRunID,
   573  		PageSize:    testPageSize,
   574  	}
   575  	URI, err := archiver.NewURI(testBucketURI)
   576  	s.NoError(err)
   577  	response, err := historyArchiver.Get(context.Background(), URI, request)
   578  	s.NoError(err)
   579  	s.Nil(response.NextPageToken)
   580  	s.Equal(append(s.historyBatchesV100[0].Body, s.historyBatchesV100[1].Body...), response.HistoryBatches)
   581  }
   582  
   583  func (s *historyArchiverSuite) TestGet_Success_UseProvidedVersion() {
   584  	historyArchiver := s.newTestHistoryArchiver(nil)
   585  	testCloseFailoverVersion := int64(1)
   586  	request := &archiver.GetHistoryRequest{
   587  		NamespaceID:          testNamespaceID,
   588  		WorkflowID:           testWorkflowID,
   589  		RunID:                testRunID,
   590  		PageSize:             testPageSize,
   591  		CloseFailoverVersion: &testCloseFailoverVersion,
   592  	}
   593  	URI, err := archiver.NewURI(testBucketURI)
   594  	s.NoError(err)
   595  	response, err := historyArchiver.Get(context.Background(), URI, request)
   596  	s.NoError(err)
   597  	s.Nil(response.NextPageToken)
   598  	s.Equal(s.historyBatchesV1[0].Body, response.HistoryBatches)
   599  }
   600  
   601  func (s *historyArchiverSuite) TestGet_Success_SmallPageSize() {
   602  	historyArchiver := s.newTestHistoryArchiver(nil)
   603  	testCloseFailoverVersion := testCloseFailoverVersion
   604  	request := &archiver.GetHistoryRequest{
   605  		NamespaceID:          testNamespaceID,
   606  		WorkflowID:           testWorkflowID,
   607  		RunID:                testRunID,
   608  		PageSize:             1,
   609  		CloseFailoverVersion: &testCloseFailoverVersion,
   610  	}
   611  	var combinedHistory []*historypb.History
   612  
   613  	URI, err := archiver.NewURI(testBucketURI)
   614  	s.NoError(err)
   615  	response, err := historyArchiver.Get(context.Background(), URI, request)
   616  	s.NoError(err)
   617  	s.NotNil(response)
   618  	s.NotNil(response.NextPageToken)
   619  	s.NotNil(response.HistoryBatches)
   620  	s.Len(response.HistoryBatches, 1)
   621  	combinedHistory = append(combinedHistory, response.HistoryBatches...)
   622  
   623  	request.NextPageToken = response.NextPageToken
   624  	response, err = historyArchiver.Get(context.Background(), URI, request)
   625  	s.NoError(err)
   626  	s.NotNil(response)
   627  	s.Nil(response.NextPageToken)
   628  	s.NotNil(response.HistoryBatches)
   629  	s.Len(response.HistoryBatches, 1)
   630  	combinedHistory = append(combinedHistory, response.HistoryBatches...)
   631  
   632  	s.Equal(append(s.historyBatchesV100[0].Body, s.historyBatchesV100[1].Body...), combinedHistory)
   633  }
   634  
   635  func (s *historyArchiverSuite) TestGet_EmptyHistory_ReturnsNotFoundError() {
   636  	historyIterator := archiver.NewMockHistoryIterator(s.controller)
   637  	historyArchiver := s.newTestHistoryArchiver(historyIterator)
   638  	URI, err := archiver.NewURI(testBucketURI + "/TestArchiveAndGet")
   639  	s.NoError(err)
   640  	getRequest := &archiver.GetHistoryRequest{
   641  		NamespaceID: testNamespaceID,
   642  		WorkflowID:  testWorkflowID,
   643  		RunID:       testRunID,
   644  		PageSize:    testPageSize,
   645  	}
   646  	response, err := historyArchiver.Get(context.Background(), URI, getRequest)
   647  	s.Error(err)
   648  	s.Nil(response)
   649  	s.IsType(&serviceerror.NotFound{}, err)
   650  }
   651  
   652  func (s *historyArchiverSuite) TestArchiveAndGet() {
   653  	historyIterator := archiver.NewMockHistoryIterator(s.controller)
   654  	gomock.InOrder(
   655  		historyIterator.EXPECT().HasNext().Return(true),
   656  		historyIterator.EXPECT().Next(gomock.Any()).Return(s.historyBatchesV100[0], nil),
   657  		historyIterator.EXPECT().HasNext().Return(true),
   658  		historyIterator.EXPECT().Next(gomock.Any()).Return(s.historyBatchesV100[1], nil),
   659  		historyIterator.EXPECT().HasNext().Return(false),
   660  	)
   661  
   662  	historyArchiver := s.newTestHistoryArchiver(historyIterator)
   663  	archiveRequest := &archiver.ArchiveHistoryRequest{
   664  		NamespaceID:          testNamespaceID,
   665  		Namespace:            testNamespace,
   666  		WorkflowID:           testWorkflowID,
   667  		RunID:                testRunID,
   668  		BranchToken:          testBranchToken,
   669  		NextEventID:          testNextEventID,
   670  		CloseFailoverVersion: testCloseFailoverVersion,
   671  	}
   672  	URI, err := archiver.NewURI(testBucketURI + "/TestArchiveAndGet")
   673  	s.NoError(err)
   674  	err = historyArchiver.Archive(context.Background(), URI, archiveRequest)
   675  	s.NoError(err)
   676  
   677  	getRequest := &archiver.GetHistoryRequest{
   678  		NamespaceID: testNamespaceID,
   679  		WorkflowID:  testWorkflowID,
   680  		RunID:       testRunID,
   681  		PageSize:    testPageSize,
   682  	}
   683  	response, err := historyArchiver.Get(context.Background(), URI, getRequest)
   684  	s.NoError(err)
   685  	s.NotNil(response)
   686  	s.Nil(response.NextPageToken)
   687  	s.Equal(append(s.historyBatchesV100[0].Body, s.historyBatchesV100[1].Body...), response.HistoryBatches)
   688  }
   689  
   690  func (s *historyArchiverSuite) newTestHistoryArchiver(historyIterator archiver.HistoryIterator) *historyArchiver {
   691  	// config := &config.S3Archiver{}
   692  	// archiver, err := newHistoryArchiver(s.container, config, historyIterator)
   693  	archiver := &historyArchiver{
   694  		container:       s.container,
   695  		s3cli:           s.s3cli,
   696  		historyIterator: historyIterator,
   697  	}
   698  	return archiver
   699  }
   700  
   701  func (s *historyArchiverSuite) setupHistoryDirectory() {
   702  	now := time.Date(2020, 8, 22, 1, 2, 3, 4, time.UTC)
   703  
   704  	s.historyBatchesV1 = []*archiverspb.HistoryBlob{
   705  		{
   706  			Header: &archiverspb.HistoryBlobHeader{
   707  				IsLast: true,
   708  			},
   709  			Body: []*historypb.History{
   710  				{
   711  					Events: []*historypb.HistoryEvent{
   712  						{
   713  							EventId:   testNextEventID - 1,
   714  							EventTime: timestamppb.New(now),
   715  							Version:   1,
   716  						},
   717  					},
   718  				},
   719  			},
   720  		},
   721  	}
   722  
   723  	s.historyBatchesV100 = []*archiverspb.HistoryBlob{
   724  		{
   725  			Header: &archiverspb.HistoryBlobHeader{
   726  				IsLast: false,
   727  			},
   728  			Body: []*historypb.History{
   729  				{
   730  					Events: []*historypb.HistoryEvent{
   731  						{
   732  							EventId:   common.FirstEventID + 1,
   733  							EventTime: timestamppb.New(now),
   734  							Version:   testCloseFailoverVersion,
   735  						},
   736  						{
   737  							EventId:   common.FirstEventID + 1,
   738  							EventTime: timestamppb.New(now),
   739  							Version:   testCloseFailoverVersion,
   740  						},
   741  					},
   742  				},
   743  			},
   744  		},
   745  		{
   746  			Header: &archiverspb.HistoryBlobHeader{
   747  				IsLast: true,
   748  			},
   749  			Body: []*historypb.History{
   750  				{
   751  					Events: []*historypb.HistoryEvent{
   752  						{
   753  							EventId:   testNextEventID - 1,
   754  							EventTime: timestamppb.New(now),
   755  							Version:   testCloseFailoverVersion,
   756  						},
   757  					},
   758  				},
   759  			},
   760  		},
   761  	}
   762  
   763  	s.writeHistoryBatchesForGetTest(s.historyBatchesV1, int64(1))
   764  	s.writeHistoryBatchesForGetTest(s.historyBatchesV100, testCloseFailoverVersion)
   765  }
   766  
   767  func (s *historyArchiverSuite) writeHistoryBatchesForGetTest(historyBatches []*archiverspb.HistoryBlob, version int64) {
   768  	for i, batch := range historyBatches {
   769  		encoder := codec.NewJSONPBEncoder()
   770  		data, err := encoder.Encode(batch)
   771  		s.Require().NoError(err)
   772  		key := constructHistoryKey("", testNamespaceID, testWorkflowID, testRunID, version, i)
   773  		_, err = s.s3cli.PutObjectWithContext(context.Background(), &s3.PutObjectInput{
   774  			Bucket: aws.String(testBucket),
   775  			Key:    aws.String(key),
   776  			Body:   bytes.NewReader(data),
   777  		})
   778  		s.Require().NoError(err)
   779  	}
   780  }
   781  
   782  func (s *historyArchiverSuite) assertKeyExists(key string) {
   783  	_, err := s.s3cli.GetObjectWithContext(context.Background(), &s3.GetObjectInput{
   784  		Bucket: aws.String(testBucket),
   785  		Key:    aws.String(key),
   786  	})
   787  	s.NoError(err)
   788  }
   789  
   790  func getCanceledContext() context.Context {
   791  	ctx, cancel := context.WithCancel(context.Background())
   792  	cancel()
   793  	return ctx
   794  }