go.temporal.io/server@v1.23.0/common/archiver/filestore/util_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 filestore
    26  
    27  import (
    28  	"os"
    29  	"path/filepath"
    30  	"testing"
    31  	"time"
    32  
    33  	"github.com/stretchr/testify/require"
    34  	"github.com/stretchr/testify/suite"
    35  	enumspb "go.temporal.io/api/enums/v1"
    36  	historypb "go.temporal.io/api/history/v1"
    37  	"google.golang.org/protobuf/types/known/timestamppb"
    38  
    39  	"go.temporal.io/server/common"
    40  	"go.temporal.io/server/common/archiver"
    41  	"go.temporal.io/server/common/codec"
    42  	"go.temporal.io/server/tests/testutils"
    43  )
    44  
    45  const (
    46  	testDirMode  = os.FileMode(0700)
    47  	testFileMode = os.FileMode(0600)
    48  )
    49  
    50  type UtilSuite struct {
    51  	*require.Assertions
    52  	suite.Suite
    53  }
    54  
    55  func TestUtilSuite(t *testing.T) {
    56  	suite.Run(t, new(UtilSuite))
    57  }
    58  
    59  func (s *UtilSuite) SetupTest() {
    60  	s.Assertions = require.New(s.T())
    61  }
    62  
    63  func (s *UtilSuite) TestFileExists() {
    64  	dir := testutils.MkdirTemp(s.T(), "", "TestFileExists")
    65  	s.assertDirectoryExists(dir)
    66  
    67  	exists, err := fileExists(dir)
    68  	s.Error(err)
    69  	s.False(exists)
    70  
    71  	filename := "test-file-name"
    72  	exists, err = fileExists(filepath.Join(dir, filename))
    73  	s.NoError(err)
    74  	s.False(exists)
    75  
    76  	s.createFile(dir, filename)
    77  	exists, err = fileExists(filepath.Join(dir, filename))
    78  	s.NoError(err)
    79  	s.True(exists)
    80  }
    81  
    82  func (s *UtilSuite) TestDirectoryExists() {
    83  	dir := testutils.MkdirTemp(s.T(), "", "TestDirectoryExists")
    84  	s.assertDirectoryExists(dir)
    85  
    86  	subdir := "subdir"
    87  	exists, err := directoryExists(filepath.Join(dir, subdir))
    88  	s.NoError(err)
    89  	s.False(exists)
    90  
    91  	filename := "test-file-name"
    92  	s.createFile(dir, filename)
    93  	fpath := filepath.Join(dir, filename)
    94  	exists, err = directoryExists(fpath)
    95  	s.Error(err)
    96  	s.False(exists)
    97  }
    98  
    99  func (s *UtilSuite) TestMkdirAll() {
   100  	dir := testutils.MkdirTemp(s.T(), "", "TestMkdirAll")
   101  	s.assertDirectoryExists(dir)
   102  
   103  	s.NoError(mkdirAll(dir, testDirMode))
   104  	s.assertDirectoryExists(dir)
   105  
   106  	subDirPath := filepath.Join(dir, "subdir_1", "subdir_2", "subdir_3")
   107  	s.assertDirectoryNotExists(subDirPath)
   108  	s.NoError(mkdirAll(subDirPath, testDirMode))
   109  	s.assertDirectoryExists(subDirPath)
   110  	s.assertCorrectFileMode(subDirPath)
   111  
   112  	filename := "test-file-name"
   113  	s.createFile(dir, filename)
   114  	fpath := filepath.Join(dir, filename)
   115  	s.Error(mkdirAll(fpath, testDirMode))
   116  }
   117  
   118  func (s *UtilSuite) TestWriteFile() {
   119  	dir := testutils.MkdirTemp(s.T(), "", "TestWriteFile")
   120  	s.assertDirectoryExists(dir)
   121  
   122  	filename := "test-file-name"
   123  	fpath := filepath.Join(dir, filename)
   124  	s.NoError(writeFile(fpath, []byte("file body 1"), testFileMode))
   125  	s.assertFileExists(fpath)
   126  	s.assertCorrectFileMode(fpath)
   127  
   128  	s.NoError(writeFile(fpath, []byte("file body 2"), testFileMode))
   129  	s.assertFileExists(fpath)
   130  	s.assertCorrectFileMode(fpath)
   131  
   132  	s.Error(writeFile(dir, []byte(""), testFileMode))
   133  	s.assertFileExists(fpath)
   134  }
   135  
   136  func (s *UtilSuite) TestReadFile() {
   137  	dir := testutils.MkdirTemp(s.T(), "", "TestReadFile")
   138  	s.assertDirectoryExists(dir)
   139  
   140  	filename := "test-file-name"
   141  	fpath := filepath.Join(dir, filename)
   142  	data, err := readFile(fpath)
   143  	s.Error(err)
   144  	s.Empty(data)
   145  
   146  	err = writeFile(fpath, []byte("file contents"), testFileMode)
   147  	s.NoError(err)
   148  	data, err = readFile(fpath)
   149  	s.NoError(err)
   150  	s.Equal("file contents", string(data))
   151  }
   152  
   153  func (s *UtilSuite) TestListFilesByPrefix() {
   154  	dir := testutils.MkdirTemp(s.T(), "", "TestListFiles")
   155  	s.assertDirectoryExists(dir)
   156  
   157  	filename := "test-file-name"
   158  	fpath := filepath.Join(dir, filename)
   159  	files, err := listFilesByPrefix(fpath, "test-")
   160  	s.Error(err)
   161  	s.Nil(files)
   162  
   163  	subDirPath := filepath.Join(dir, "subdir")
   164  	s.NoError(mkdirAll(subDirPath, testDirMode))
   165  	s.assertDirectoryExists(subDirPath)
   166  	expectedFileNames := []string{"file_1", "file_2", "file_3"}
   167  	for _, f := range expectedFileNames {
   168  		s.createFile(dir, f)
   169  	}
   170  	for _, f := range []string{"randomFile", "fileWithOtherPrefix"} {
   171  		s.createFile(dir, f)
   172  	}
   173  	actualFileNames, err := listFilesByPrefix(dir, "file_")
   174  	s.NoError(err)
   175  	s.Equal(len(expectedFileNames), len(actualFileNames))
   176  }
   177  
   178  func (s *UtilSuite) TestEncodeDecodeHistoryBatches() {
   179  	now := time.Date(2020, 8, 22, 1, 2, 3, 4, time.UTC)
   180  	historyBatches := []*historypb.History{
   181  		{
   182  			Events: []*historypb.HistoryEvent{
   183  				{
   184  					EventId: common.FirstEventID,
   185  					Version: 1,
   186  				},
   187  			},
   188  		},
   189  		{
   190  			Events: []*historypb.HistoryEvent{
   191  				{
   192  					EventId:   common.FirstEventID + 1,
   193  					EventTime: timestamppb.New(now),
   194  					Version:   1,
   195  				},
   196  				{
   197  					EventId: common.FirstEventID + 2,
   198  					Version: 2,
   199  					Attributes: &historypb.HistoryEvent_WorkflowTaskStartedEventAttributes{WorkflowTaskStartedEventAttributes: &historypb.WorkflowTaskStartedEventAttributes{
   200  						Identity: "some random identity",
   201  					}},
   202  				},
   203  			},
   204  		},
   205  	}
   206  
   207  	encoder := codec.NewJSONPBEncoder()
   208  	encodedHistoryBatches, err := encoder.EncodeHistories(historyBatches)
   209  	s.NoError(err)
   210  
   211  	decodedHistoryBatches, err := encoder.DecodeHistories(encodedHistoryBatches)
   212  	s.NoError(err)
   213  	s.Equal(historyBatches, decodedHistoryBatches)
   214  }
   215  
   216  func (s *UtilSuite) TestValidateDirPath() {
   217  	dir := testutils.MkdirTemp(s.T(), "", "TestValidateDirPath")
   218  	s.assertDirectoryExists(dir)
   219  	filename := "test-file-name"
   220  	s.createFile(dir, filename)
   221  	fpath := filepath.Join(dir, filename)
   222  
   223  	testCases := []struct {
   224  		dirPath     string
   225  		expectedErr error
   226  	}{
   227  		{
   228  			dirPath:     "",
   229  			expectedErr: errEmptyDirectoryPath,
   230  		},
   231  		{
   232  			dirPath:     "/absolute/path",
   233  			expectedErr: nil,
   234  		},
   235  		{
   236  			dirPath:     "relative/path",
   237  			expectedErr: nil,
   238  		},
   239  		{
   240  			dirPath:     dir,
   241  			expectedErr: nil,
   242  		},
   243  		{
   244  			dirPath:     fpath,
   245  			expectedErr: errDirectoryExpected,
   246  		},
   247  	}
   248  
   249  	for _, tc := range testCases {
   250  		s.Equal(tc.expectedErr, validateDirPath(tc.dirPath))
   251  	}
   252  }
   253  
   254  func (s *UtilSuite) TestconstructHistoryFilename() {
   255  	testCases := []struct {
   256  		namespaceID          string
   257  		workflowID           string
   258  		runID                string
   259  		closeFailoverVersion int64
   260  		expectBuiltName      string
   261  	}{
   262  		{
   263  			namespaceID:          "testNamespaceID",
   264  			workflowID:           "testWorkflowID",
   265  			runID:                "testRunID",
   266  			closeFailoverVersion: 5,
   267  			expectBuiltName:      "11936904199538907273367046253745284795510285995943906173973_5.history",
   268  		},
   269  	}
   270  
   271  	for _, tc := range testCases {
   272  		filename := constructHistoryFilename(tc.namespaceID, tc.workflowID, tc.runID, tc.closeFailoverVersion)
   273  		s.Equal(tc.expectBuiltName, filename)
   274  	}
   275  }
   276  
   277  func (s *UtilSuite) TestExtractCloseFailoverVersion() {
   278  	testCases := []struct {
   279  		filename        string
   280  		expectedVersion int64
   281  		expectedErr     bool
   282  	}{
   283  		{
   284  			filename:        "11936904199538907273367046253745284795510285995943906173973_5.history",
   285  			expectedVersion: 5,
   286  			expectedErr:     false,
   287  		},
   288  		{
   289  			filename:    "history",
   290  			expectedErr: true,
   291  		},
   292  		{
   293  			filename:    "some.random.filename",
   294  			expectedErr: true,
   295  		},
   296  		{
   297  			filename:        "some-random_101.filename",
   298  			expectedVersion: 101,
   299  			expectedErr:     false,
   300  		},
   301  		{
   302  			filename:        "random_-100.filename",
   303  			expectedVersion: -100,
   304  			expectedErr:     false,
   305  		},
   306  	}
   307  
   308  	for _, tc := range testCases {
   309  		version, err := extractCloseFailoverVersion(tc.filename)
   310  		if tc.expectedErr {
   311  			s.Error(err)
   312  		} else {
   313  			s.NoError(err)
   314  			s.Equal(tc.expectedVersion, version)
   315  		}
   316  	}
   317  }
   318  
   319  func (s *UtilSuite) TestHistoryMutated() {
   320  	testCases := []struct {
   321  		historyBatches []*historypb.History
   322  		request        *archiver.ArchiveHistoryRequest
   323  		isLast         bool
   324  		isMutated      bool
   325  	}{
   326  		{
   327  			historyBatches: []*historypb.History{
   328  				{
   329  					Events: []*historypb.HistoryEvent{
   330  						{
   331  							Version: 15,
   332  						},
   333  					},
   334  				},
   335  			},
   336  			request: &archiver.ArchiveHistoryRequest{
   337  				CloseFailoverVersion: 3,
   338  			},
   339  			isMutated: true,
   340  		},
   341  		{
   342  			historyBatches: []*historypb.History{
   343  				{
   344  					Events: []*historypb.HistoryEvent{
   345  						{
   346  							EventId: 33,
   347  							Version: 10,
   348  						},
   349  					},
   350  				},
   351  				{
   352  					Events: []*historypb.HistoryEvent{
   353  						{
   354  							EventId: 49,
   355  							Version: 10,
   356  						},
   357  						{
   358  							EventId: 50,
   359  							Version: 10,
   360  						},
   361  					},
   362  				},
   363  			},
   364  			request: &archiver.ArchiveHistoryRequest{
   365  				CloseFailoverVersion: 10,
   366  				NextEventID:          34,
   367  			},
   368  			isLast:    true,
   369  			isMutated: true,
   370  		},
   371  		{
   372  			historyBatches: []*historypb.History{
   373  				{
   374  					Events: []*historypb.HistoryEvent{
   375  						{
   376  							Version: 9,
   377  						},
   378  					},
   379  				},
   380  			},
   381  			request: &archiver.ArchiveHistoryRequest{
   382  				CloseFailoverVersion: 10,
   383  			},
   384  			isLast:    true,
   385  			isMutated: true,
   386  		},
   387  		{
   388  			historyBatches: []*historypb.History{
   389  				{
   390  					Events: []*historypb.HistoryEvent{
   391  						{
   392  							EventId: 20,
   393  							Version: 10,
   394  						},
   395  					},
   396  				},
   397  				{
   398  					Events: []*historypb.HistoryEvent{
   399  						{
   400  							EventId: 33,
   401  							Version: 10,
   402  						},
   403  					},
   404  				},
   405  			},
   406  			request: &archiver.ArchiveHistoryRequest{
   407  				CloseFailoverVersion: 10,
   408  				NextEventID:          34,
   409  			},
   410  			isLast:    true,
   411  			isMutated: false,
   412  		},
   413  	}
   414  	for _, tc := range testCases {
   415  		s.Equal(tc.isMutated, historyMutated(tc.request, tc.historyBatches, tc.isLast))
   416  	}
   417  }
   418  
   419  func (s *UtilSuite) TestSerializeDeserializeGetHistoryToken() {
   420  	token := &getHistoryToken{
   421  		CloseFailoverVersion: 101,
   422  		NextBatchIdx:         20,
   423  	}
   424  
   425  	serializedToken, err := serializeToken(token)
   426  	s.Nil(err)
   427  
   428  	deserializedToken, err := deserializeGetHistoryToken(serializedToken)
   429  	s.Nil(err)
   430  	s.Equal(token, deserializedToken)
   431  }
   432  
   433  func (s *UtilSuite) createFile(dir string, filename string) {
   434  	err := os.WriteFile(filepath.Join(dir, filename), []byte("file contents"), testFileMode)
   435  	s.Nil(err)
   436  }
   437  
   438  func (s *UtilSuite) assertFileExists(filepath string) {
   439  	exists, err := fileExists(filepath)
   440  	s.NoError(err)
   441  	s.True(exists)
   442  }
   443  
   444  func (s *UtilSuite) assertDirectoryExists(path string) {
   445  	exists, err := directoryExists(path)
   446  	s.NoError(err)
   447  	s.True(exists)
   448  }
   449  
   450  func (s *UtilSuite) assertDirectoryNotExists(path string) {
   451  	exists, err := directoryExists(path)
   452  	s.NoError(err)
   453  	s.False(exists)
   454  }
   455  
   456  func (s *UtilSuite) assertCorrectFileMode(path string) {
   457  	info, err := os.Stat(path)
   458  	s.NoError(err)
   459  	mode := testFileMode
   460  	if info.IsDir() {
   461  		mode = testDirMode | os.ModeDir
   462  	}
   463  	s.Equal(mode, info.Mode())
   464  }
   465  
   466  func toWorkflowExecutionStatusPtr(in enumspb.WorkflowExecutionStatus) *enumspb.WorkflowExecutionStatus {
   467  	return &in
   468  }