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 }