go.temporal.io/server@v1.23.0/common/archiver/filestore/visibility_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 filestore
    26  
    27  import (
    28  	"context"
    29  	"errors"
    30  	"os"
    31  	"path"
    32  	"testing"
    33  	"time"
    34  
    35  	"github.com/golang/mock/gomock"
    36  	"github.com/stretchr/testify/require"
    37  	"github.com/stretchr/testify/suite"
    38  	commonpb "go.temporal.io/api/common/v1"
    39  	enumspb "go.temporal.io/api/enums/v1"
    40  	"go.temporal.io/api/serviceerror"
    41  	workflowpb "go.temporal.io/api/workflow/v1"
    42  	"google.golang.org/protobuf/types/known/timestamppb"
    43  
    44  	"go.temporal.io/server/common/searchattribute"
    45  
    46  	archiverspb "go.temporal.io/server/api/archiver/v1"
    47  	"go.temporal.io/server/common/archiver"
    48  	"go.temporal.io/server/common/codec"
    49  	"go.temporal.io/server/common/config"
    50  	"go.temporal.io/server/common/convert"
    51  	"go.temporal.io/server/common/log"
    52  	"go.temporal.io/server/common/payload"
    53  	"go.temporal.io/server/common/primitives/timestamp"
    54  	"go.temporal.io/server/tests/testutils"
    55  )
    56  
    57  const (
    58  	testWorkflowTypeName = "test-workflow-type"
    59  )
    60  
    61  type visibilityArchiverSuite struct {
    62  	*require.Assertions
    63  	suite.Suite
    64  
    65  	container          *archiver.VisibilityBootstrapContainer
    66  	testArchivalURI    archiver.URI
    67  	testQueryDirectory string
    68  	visibilityRecords  []*archiverspb.VisibilityRecord
    69  
    70  	controller *gomock.Controller
    71  }
    72  
    73  func TestVisibilityArchiverSuite(t *testing.T) {
    74  	suite.Run(t, new(visibilityArchiverSuite))
    75  }
    76  
    77  func (s *visibilityArchiverSuite) SetupSuite() {
    78  	var err error
    79  	s.testQueryDirectory, err = os.MkdirTemp("", "TestQuery")
    80  	s.Require().NoError(err)
    81  	s.setupVisibilityDirectory()
    82  	s.testArchivalURI, err = archiver.NewURI("file:///a/b/c")
    83  	s.Require().NoError(err)
    84  }
    85  
    86  func (s *visibilityArchiverSuite) TearDownSuite() {
    87  	if err := os.RemoveAll(s.testQueryDirectory); err != nil {
    88  		s.Fail("Failed to remove test query directory %v: %v", s.testQueryDirectory, err)
    89  	}
    90  }
    91  
    92  func (s *visibilityArchiverSuite) SetupTest() {
    93  	s.Assertions = require.New(s.T())
    94  	s.container = &archiver.VisibilityBootstrapContainer{
    95  		Logger: log.NewNoopLogger(),
    96  	}
    97  	s.controller = gomock.NewController(s.T())
    98  }
    99  
   100  func (s *visibilityArchiverSuite) TearDownTest() {
   101  	s.controller.Finish()
   102  }
   103  
   104  func (s *visibilityArchiverSuite) TestValidateURI() {
   105  	testCases := []struct {
   106  		URI         string
   107  		expectedErr error
   108  	}{
   109  		{
   110  			URI:         "wrongscheme:///a/b/c",
   111  			expectedErr: archiver.ErrURISchemeMismatch,
   112  		},
   113  		{
   114  			URI:         "file://",
   115  			expectedErr: errEmptyDirectoryPath,
   116  		},
   117  		{
   118  			URI:         "file:///a/b/c",
   119  			expectedErr: nil,
   120  		},
   121  	}
   122  
   123  	visibilityArchiver := s.newTestVisibilityArchiver()
   124  	for _, tc := range testCases {
   125  		URI, err := archiver.NewURI(tc.URI)
   126  		s.NoError(err)
   127  		s.Equal(tc.expectedErr, visibilityArchiver.ValidateURI(URI))
   128  	}
   129  }
   130  
   131  func (s *visibilityArchiverSuite) TestArchive_Fail_InvalidURI() {
   132  	visibilityArchiver := s.newTestVisibilityArchiver()
   133  	URI, err := archiver.NewURI("wrongscheme://")
   134  	s.NoError(err)
   135  	request := &archiverspb.VisibilityRecord{
   136  		Namespace:        testNamespace,
   137  		NamespaceId:      testNamespaceID,
   138  		WorkflowId:       testWorkflowID,
   139  		RunId:            testRunID,
   140  		WorkflowTypeName: testWorkflowTypeName,
   141  		StartTime:        timestamp.TimeNowPtrUtc(),
   142  		ExecutionTime:    nil, // workflow without backoff
   143  		CloseTime:        timestamp.TimeNowPtrUtc(),
   144  		Status:           enumspb.WORKFLOW_EXECUTION_STATUS_FAILED,
   145  		HistoryLength:    int64(101),
   146  	}
   147  	err = visibilityArchiver.Archive(context.Background(), URI, request)
   148  	s.Error(err)
   149  }
   150  
   151  func (s *visibilityArchiverSuite) TestArchive_Fail_InvalidRequest() {
   152  	visibilityArchiver := s.newTestVisibilityArchiver()
   153  	err := visibilityArchiver.Archive(context.Background(), s.testArchivalURI, &archiverspb.VisibilityRecord{})
   154  	s.Error(err)
   155  }
   156  
   157  func (s *visibilityArchiverSuite) TestArchive_Fail_NonRetryableErrorOption() {
   158  	visibilityArchiver := s.newTestVisibilityArchiver()
   159  	nonRetryableErr := errors.New("some non-retryable error")
   160  	err := visibilityArchiver.Archive(
   161  		context.Background(),
   162  		s.testArchivalURI,
   163  		&archiverspb.VisibilityRecord{},
   164  		archiver.GetNonRetryableErrorOption(nonRetryableErr),
   165  	)
   166  	s.Equal(nonRetryableErr, err)
   167  }
   168  
   169  func (s *visibilityArchiverSuite) TestArchive_Success() {
   170  	dir := testutils.MkdirTemp(s.T(), "", "TestVisibilityArchive")
   171  
   172  	visibilityArchiver := s.newTestVisibilityArchiver()
   173  	closeTimestamp := timestamp.TimeNowPtrUtc()
   174  	request := &archiverspb.VisibilityRecord{
   175  		NamespaceId:      testNamespaceID,
   176  		Namespace:        testNamespace,
   177  		WorkflowId:       testWorkflowID,
   178  		RunId:            testRunID,
   179  		WorkflowTypeName: testWorkflowTypeName,
   180  		StartTime:        timestamppb.New(closeTimestamp.AsTime().Add(-time.Hour)),
   181  		ExecutionTime:    nil, // workflow without backoff
   182  		CloseTime:        closeTimestamp,
   183  		Status:           enumspb.WORKFLOW_EXECUTION_STATUS_FAILED,
   184  		HistoryLength:    int64(101),
   185  		Memo: &commonpb.Memo{
   186  			Fields: map[string]*commonpb.Payload{
   187  				"testFields": payload.EncodeBytes([]byte{1, 2, 3}),
   188  			},
   189  		},
   190  		SearchAttributes: map[string]string{
   191  			"testAttribute": "456",
   192  		},
   193  	}
   194  	URI, err := archiver.NewURI("file://" + dir)
   195  	s.NoError(err)
   196  	err = visibilityArchiver.Archive(context.Background(), URI, request)
   197  	s.NoError(err)
   198  
   199  	expectedFilename := constructVisibilityFilename(closeTimestamp.AsTime(), testRunID)
   200  	filepath := path.Join(dir, testNamespaceID, expectedFilename)
   201  	s.assertFileExists(filepath)
   202  
   203  	data, err := readFile(filepath)
   204  	s.NoError(err)
   205  
   206  	archivedRecord := &archiverspb.VisibilityRecord{}
   207  	encoder := codec.NewJSONPBEncoder()
   208  	err = encoder.Decode(data, archivedRecord)
   209  	s.NoError(err)
   210  	s.Equal(request, archivedRecord)
   211  }
   212  
   213  func (s *visibilityArchiverSuite) TestMatchQuery() {
   214  	testCases := []struct {
   215  		query       *parsedQuery
   216  		record      *archiverspb.VisibilityRecord
   217  		shouldMatch bool
   218  	}{
   219  		{
   220  			query: &parsedQuery{
   221  				earliestCloseTime: time.Unix(0, 1000),
   222  				latestCloseTime:   time.Unix(0, 12345),
   223  			},
   224  			record: &archiverspb.VisibilityRecord{
   225  				CloseTime: timestamp.UnixOrZeroTimePtr(1999),
   226  			},
   227  			shouldMatch: true,
   228  		},
   229  		{
   230  			query: &parsedQuery{
   231  				earliestCloseTime: time.Unix(0, 1000),
   232  				latestCloseTime:   time.Unix(0, 12345),
   233  			},
   234  			record: &archiverspb.VisibilityRecord{
   235  				CloseTime: timestamp.UnixOrZeroTimePtr(999),
   236  			},
   237  			shouldMatch: false,
   238  		},
   239  		{
   240  			query: &parsedQuery{
   241  				earliestCloseTime: time.Unix(0, 1000),
   242  				latestCloseTime:   time.Unix(0, 12345),
   243  				workflowID:        convert.StringPtr("random workflowID"),
   244  			},
   245  			record: &archiverspb.VisibilityRecord{
   246  				CloseTime: timestamp.UnixOrZeroTimePtr(2000),
   247  			},
   248  			shouldMatch: false,
   249  		},
   250  		{
   251  			query: &parsedQuery{
   252  				earliestCloseTime: time.Unix(0, 1000),
   253  				latestCloseTime:   time.Unix(0, 12345),
   254  				workflowID:        convert.StringPtr("random workflowID"),
   255  				runID:             convert.StringPtr("random runID"),
   256  			},
   257  			record: &archiverspb.VisibilityRecord{
   258  				CloseTime:        timestamp.UnixOrZeroTimePtr(12345),
   259  				WorkflowId:       "random workflowID",
   260  				RunId:            "random runID",
   261  				WorkflowTypeName: "random type name",
   262  			},
   263  			shouldMatch: true,
   264  		},
   265  		{
   266  			query: &parsedQuery{
   267  				earliestCloseTime: time.Unix(0, 1000),
   268  				latestCloseTime:   time.Unix(0, 12345),
   269  				workflowTypeName:  convert.StringPtr("some random type name"),
   270  			},
   271  			record: &archiverspb.VisibilityRecord{
   272  				CloseTime: timestamp.UnixOrZeroTimePtr(12345),
   273  			},
   274  			shouldMatch: false,
   275  		},
   276  		{
   277  			query: &parsedQuery{
   278  				earliestCloseTime: time.Unix(0, 1000),
   279  				latestCloseTime:   time.Unix(0, 12345),
   280  				workflowTypeName:  convert.StringPtr("some random type name"),
   281  				status:            toWorkflowExecutionStatusPtr(enumspb.WORKFLOW_EXECUTION_STATUS_CONTINUED_AS_NEW),
   282  			},
   283  			record: &archiverspb.VisibilityRecord{
   284  				CloseTime:        timestamp.UnixOrZeroTimePtr(12345),
   285  				Status:           enumspb.WORKFLOW_EXECUTION_STATUS_CONTINUED_AS_NEW,
   286  				WorkflowTypeName: "some random type name",
   287  			},
   288  			shouldMatch: true,
   289  		},
   290  	}
   291  
   292  	for _, tc := range testCases {
   293  		s.Equal(tc.shouldMatch, matchQuery(tc.record, tc.query))
   294  	}
   295  }
   296  
   297  func (s *visibilityArchiverSuite) TestSortAndFilterFiles() {
   298  	testCases := []struct {
   299  		filenames      []string
   300  		token          *queryVisibilityToken
   301  		expectedResult []string
   302  	}{
   303  		{
   304  			filenames:      []string{"9_12345.vis", "5_0.vis", "9_54321.vis", "1000_654.vis", "1000_78.vis"},
   305  			expectedResult: []string{"1000_78.vis", "1000_654.vis", "9_54321.vis", "9_12345.vis", "5_0.vis"},
   306  		},
   307  		{
   308  			filenames: []string{"9_12345.vis", "5_0.vis", "9_54321.vis", "1000_654.vis", "1000_78.vis"},
   309  			token: &queryVisibilityToken{
   310  				LastCloseTime: time.Unix(0, 3),
   311  			},
   312  			expectedResult: []string{},
   313  		},
   314  		{
   315  			filenames: []string{"9_12345.vis", "5_0.vis", "9_54321.vis", "1000_654.vis", "1000_78.vis"},
   316  			token: &queryVisibilityToken{
   317  				LastCloseTime: time.Unix(0, 999),
   318  			},
   319  			expectedResult: []string{"9_54321.vis", "9_12345.vis", "5_0.vis"},
   320  		},
   321  		{
   322  			filenames: []string{"9_12345.vis", "5_0.vis", "9_54321.vis", "1000_654.vis", "1000_78.vis"},
   323  			token: &queryVisibilityToken{
   324  				LastCloseTime: time.Unix(0, 5).UTC(),
   325  			},
   326  			expectedResult: []string{"5_0.vis"},
   327  		},
   328  	}
   329  
   330  	for i, tc := range testCases {
   331  		result, err := sortAndFilterFiles(tc.filenames, tc.token)
   332  		s.NoError(err, "case %d", i)
   333  		s.Equal(tc.expectedResult, result, "case %d", i)
   334  	}
   335  }
   336  
   337  func (s *visibilityArchiverSuite) TestQuery_Fail_InvalidURI() {
   338  	visibilityArchiver := s.newTestVisibilityArchiver()
   339  	URI, err := archiver.NewURI("wrongscheme://")
   340  	s.NoError(err)
   341  	request := &archiver.QueryVisibilityRequest{
   342  		NamespaceID: testNamespaceID,
   343  		PageSize:    1,
   344  	}
   345  	response, err := visibilityArchiver.Query(context.Background(), URI, request, searchattribute.TestNameTypeMap)
   346  	s.Error(err)
   347  	s.Nil(response)
   348  }
   349  
   350  func (s *visibilityArchiverSuite) TestQuery_Fail_InvalidRequest() {
   351  	visibilityArchiver := s.newTestVisibilityArchiver()
   352  	response, err := visibilityArchiver.Query(context.Background(), s.testArchivalURI, &archiver.QueryVisibilityRequest{}, searchattribute.TestNameTypeMap)
   353  	s.Error(err)
   354  	s.Nil(response)
   355  }
   356  
   357  func (s *visibilityArchiverSuite) TestQuery_Fail_InvalidQuery() {
   358  	visibilityArchiver := s.newTestVisibilityArchiver()
   359  	mockParser := NewMockQueryParser(s.controller)
   360  	mockParser.EXPECT().Parse(gomock.Any()).Return(nil, errors.New("invalid query"))
   361  	visibilityArchiver.queryParser = mockParser
   362  	response, err := visibilityArchiver.Query(context.Background(), s.testArchivalURI, &archiver.QueryVisibilityRequest{
   363  		NamespaceID: "some random namespaceID",
   364  		PageSize:    10,
   365  		Query:       "some invalid query",
   366  	}, searchattribute.TestNameTypeMap)
   367  	s.Error(err)
   368  	s.Nil(response)
   369  }
   370  
   371  func (s *visibilityArchiverSuite) TestQuery_Success_DirectoryNotExist() {
   372  	visibilityArchiver := s.newTestVisibilityArchiver()
   373  	mockParser := NewMockQueryParser(s.controller)
   374  	mockParser.EXPECT().Parse(gomock.Any()).Return(&parsedQuery{
   375  		earliestCloseTime: time.Unix(0, 1),
   376  		latestCloseTime:   time.Unix(0, 101),
   377  	}, nil)
   378  	visibilityArchiver.queryParser = mockParser
   379  	request := &archiver.QueryVisibilityRequest{
   380  		NamespaceID: testNamespaceID,
   381  		Query:       "parsed by mockParser",
   382  		PageSize:    1,
   383  	}
   384  	response, err := visibilityArchiver.Query(context.Background(), s.testArchivalURI, request, searchattribute.TestNameTypeMap)
   385  	s.NoError(err)
   386  	s.NotNil(response)
   387  	s.Empty(response.Executions)
   388  	s.Empty(response.NextPageToken)
   389  }
   390  
   391  func (s *visibilityArchiverSuite) TestQuery_Fail_InvalidToken() {
   392  	visibilityArchiver := s.newTestVisibilityArchiver()
   393  	mockParser := NewMockQueryParser(s.controller)
   394  	mockParser.EXPECT().Parse(gomock.Any()).Return(&parsedQuery{
   395  		earliestCloseTime: time.Unix(0, 1),
   396  		latestCloseTime:   time.Unix(0, 101),
   397  	}, nil)
   398  	visibilityArchiver.queryParser = mockParser
   399  	request := &archiver.QueryVisibilityRequest{
   400  		NamespaceID:   testNamespaceID,
   401  		Query:         "parsed by mockParser",
   402  		PageSize:      1,
   403  		NextPageToken: []byte{1, 2, 3},
   404  	}
   405  	response, err := visibilityArchiver.Query(context.Background(), s.testArchivalURI, request, searchattribute.TestNameTypeMap)
   406  	s.Error(err)
   407  	s.Nil(response)
   408  }
   409  
   410  func (s *visibilityArchiverSuite) TestQuery_Success_NoNextPageToken() {
   411  	visibilityArchiver := s.newTestVisibilityArchiver()
   412  	mockParser := NewMockQueryParser(s.controller)
   413  	mockParser.EXPECT().Parse(gomock.Any()).Return(&parsedQuery{
   414  		earliestCloseTime: time.Unix(0, 1),
   415  		latestCloseTime:   time.Unix(0, 10001),
   416  		workflowID:        convert.StringPtr(testWorkflowID),
   417  	}, nil)
   418  	visibilityArchiver.queryParser = mockParser
   419  	request := &archiver.QueryVisibilityRequest{
   420  		NamespaceID: testNamespaceID,
   421  		PageSize:    10,
   422  		Query:       "parsed by mockParser",
   423  	}
   424  	URI, err := archiver.NewURI("file://" + s.testQueryDirectory)
   425  	s.NoError(err)
   426  	response, err := visibilityArchiver.Query(context.Background(), URI, request, searchattribute.TestNameTypeMap)
   427  	s.NoError(err)
   428  	s.NotNil(response)
   429  	s.Nil(response.NextPageToken)
   430  	s.Len(response.Executions, 1)
   431  	ei, err := convertToExecutionInfo(s.visibilityRecords[0], searchattribute.TestNameTypeMap)
   432  	s.NoError(err)
   433  	s.Equal(ei, response.Executions[0])
   434  }
   435  
   436  func (s *visibilityArchiverSuite) TestQuery_Success_SmallPageSize() {
   437  	visibilityArchiver := s.newTestVisibilityArchiver()
   438  	mockParser := NewMockQueryParser(s.controller)
   439  	mockParser.EXPECT().Parse(gomock.Any()).Return(&parsedQuery{
   440  		earliestCloseTime: time.Unix(0, 1),
   441  		latestCloseTime:   time.Unix(0, 10001),
   442  		status:            toWorkflowExecutionStatusPtr(enumspb.WORKFLOW_EXECUTION_STATUS_FAILED),
   443  	}, nil).AnyTimes()
   444  	visibilityArchiver.queryParser = mockParser
   445  	request := &archiver.QueryVisibilityRequest{
   446  		NamespaceID: testNamespaceID,
   447  		PageSize:    2,
   448  		Query:       "parsed by mockParser",
   449  	}
   450  	URI, err := archiver.NewURI("file://" + s.testQueryDirectory)
   451  	s.NoError(err)
   452  	response, err := visibilityArchiver.Query(context.Background(), URI, request, searchattribute.TestNameTypeMap)
   453  	s.NoError(err)
   454  	s.NotNil(response)
   455  	s.NotNil(response.NextPageToken)
   456  	s.Len(response.Executions, 2)
   457  	ei, err := convertToExecutionInfo(s.visibilityRecords[0], searchattribute.TestNameTypeMap)
   458  	s.NoError(err)
   459  	s.Equal(ei, response.Executions[0])
   460  	ei, err = convertToExecutionInfo(s.visibilityRecords[1], searchattribute.TestNameTypeMap)
   461  	s.NoError(err)
   462  	s.Equal(ei, response.Executions[1])
   463  
   464  	request.NextPageToken = response.NextPageToken
   465  	response, err = visibilityArchiver.Query(context.Background(), URI, request, searchattribute.TestNameTypeMap)
   466  	s.NoError(err)
   467  	s.NotNil(response)
   468  	s.Nil(response.NextPageToken)
   469  	s.Len(response.Executions, 1)
   470  	ei, err = convertToExecutionInfo(s.visibilityRecords[3], searchattribute.TestNameTypeMap)
   471  	s.NoError(err)
   472  	s.Equal(ei, response.Executions[0])
   473  }
   474  
   475  func (s *visibilityArchiverSuite) TestArchiveAndQuery() {
   476  	dir := testutils.MkdirTemp(s.T(), "", "TestArchiveAndQuery")
   477  
   478  	visibilityArchiver := s.newTestVisibilityArchiver()
   479  	mockParser := NewMockQueryParser(s.controller)
   480  	mockParser.EXPECT().Parse(gomock.Any()).Return(&parsedQuery{
   481  		earliestCloseTime: time.Unix(0, 10),
   482  		latestCloseTime:   time.Unix(0, 10001),
   483  		status:            toWorkflowExecutionStatusPtr(enumspb.WORKFLOW_EXECUTION_STATUS_FAILED),
   484  	}, nil).AnyTimes()
   485  	visibilityArchiver.queryParser = mockParser
   486  	URI, err := archiver.NewURI("file://" + dir)
   487  	s.NoError(err)
   488  	for _, record := range s.visibilityRecords {
   489  		err := visibilityArchiver.Archive(context.Background(), URI, (*archiverspb.VisibilityRecord)(record))
   490  		s.NoError(err)
   491  	}
   492  
   493  	request := &archiver.QueryVisibilityRequest{
   494  		NamespaceID: testNamespaceID,
   495  		PageSize:    1,
   496  		Query:       "parsed by mockParser",
   497  	}
   498  	executions := []*workflowpb.WorkflowExecutionInfo{}
   499  	for len(executions) == 0 || request.NextPageToken != nil {
   500  		response, err := visibilityArchiver.Query(context.Background(), URI, request, searchattribute.TestNameTypeMap)
   501  		s.NoError(err)
   502  		s.NotNil(response)
   503  		executions = append(executions, response.Executions...)
   504  		request.NextPageToken = response.NextPageToken
   505  	}
   506  	s.Len(executions, 2)
   507  	ei, err := convertToExecutionInfo(s.visibilityRecords[0], searchattribute.TestNameTypeMap)
   508  	s.NoError(err)
   509  	s.Equal(ei, executions[0])
   510  	ei, err = convertToExecutionInfo(s.visibilityRecords[1], searchattribute.TestNameTypeMap)
   511  	s.NoError(err)
   512  	s.Equal(ei, executions[1])
   513  }
   514  
   515  func (s *visibilityArchiverSuite) TestQuery_EmptyQuery_InvalidNamespace() {
   516  	URI := s.testArchivalURI
   517  
   518  	visibilityArchiver := s.newTestVisibilityArchiver()
   519  	mockParser := NewMockQueryParser(s.controller)
   520  	mockParser.EXPECT().Parse(gomock.Any()).Return(&parsedQuery{
   521  		earliestCloseTime: time.Unix(0, 10),
   522  		latestCloseTime:   time.Unix(0, 10001),
   523  		status:            toWorkflowExecutionStatusPtr(enumspb.WORKFLOW_EXECUTION_STATUS_FAILED),
   524  	}, nil).AnyTimes()
   525  	visibilityArchiver.queryParser = mockParser
   526  	req := &archiver.QueryVisibilityRequest{
   527  		NamespaceID:   "",
   528  		PageSize:      1,
   529  		NextPageToken: nil,
   530  		Query:         "",
   531  	}
   532  	_, err := visibilityArchiver.Query(context.Background(), URI, req, searchattribute.TestNameTypeMap)
   533  
   534  	var svcErr *serviceerror.InvalidArgument
   535  
   536  	s.ErrorAs(err, &svcErr)
   537  }
   538  
   539  func (s *visibilityArchiverSuite) TestQuery_EmptyQuery_ZeroPageSize() {
   540  	visibilityArchiver := s.newTestVisibilityArchiver()
   541  
   542  	req := &archiver.QueryVisibilityRequest{
   543  		NamespaceID:   testNamespaceID,
   544  		PageSize:      0,
   545  		NextPageToken: nil,
   546  		Query:         "",
   547  	}
   548  	_, err := visibilityArchiver.Query(context.Background(), s.testArchivalURI, req, searchattribute.TestNameTypeMap)
   549  
   550  	var svcErr *serviceerror.InvalidArgument
   551  
   552  	s.ErrorAs(err, &svcErr)
   553  }
   554  
   555  func (s *visibilityArchiverSuite) TestQuery_EmptyQuery_Pagination() {
   556  	dir := testutils.MkdirTemp(s.T(), "", "TestQuery_EmptyQuery_Pagination")
   557  
   558  	visibilityArchiver := s.newTestVisibilityArchiver()
   559  	URI, err := archiver.NewURI("file://" + dir)
   560  	s.NoError(err)
   561  	for _, record := range s.visibilityRecords {
   562  		err := visibilityArchiver.Archive(context.Background(), URI, record)
   563  		s.NoError(err)
   564  	}
   565  
   566  	request := &archiver.QueryVisibilityRequest{
   567  		NamespaceID: testNamespaceID,
   568  		PageSize:    1,
   569  		Query:       "",
   570  	}
   571  	var executions []*workflowpb.WorkflowExecutionInfo
   572  	for len(executions) == 0 || request.NextPageToken != nil {
   573  		response, err := visibilityArchiver.Query(context.Background(), URI, request, searchattribute.TestNameTypeMap)
   574  		s.NoError(err)
   575  		s.NotNil(response)
   576  		executions = append(executions, response.Executions...)
   577  		request.NextPageToken = response.NextPageToken
   578  	}
   579  	s.Len(executions, 4)
   580  }
   581  
   582  func (s *visibilityArchiverSuite) newTestVisibilityArchiver() *visibilityArchiver {
   583  	config := &config.FilestoreArchiver{
   584  		FileMode: testFileModeStr,
   585  		DirMode:  testDirModeStr,
   586  	}
   587  	archiver, err := NewVisibilityArchiver(s.container, config)
   588  	s.NoError(err)
   589  	return archiver.(*visibilityArchiver)
   590  }
   591  
   592  func (s *visibilityArchiverSuite) setupVisibilityDirectory() {
   593  	s.visibilityRecords = []*archiverspb.VisibilityRecord{
   594  		{
   595  			NamespaceId:      testNamespaceID,
   596  			Namespace:        testNamespace,
   597  			WorkflowId:       testWorkflowID,
   598  			RunId:            testRunID,
   599  			WorkflowTypeName: testWorkflowTypeName,
   600  			StartTime:        timestamp.UnixOrZeroTimePtr(1),
   601  			CloseTime:        timestamp.UnixOrZeroTimePtr(10000),
   602  			Status:           enumspb.WORKFLOW_EXECUTION_STATUS_FAILED,
   603  			HistoryLength:    101,
   604  		},
   605  		{
   606  			NamespaceId:      testNamespaceID,
   607  			Namespace:        testNamespace,
   608  			WorkflowId:       "some random workflow ID",
   609  			RunId:            "some random run ID",
   610  			WorkflowTypeName: testWorkflowTypeName,
   611  			StartTime:        timestamp.UnixOrZeroTimePtr(2),
   612  			ExecutionTime:    nil,
   613  			CloseTime:        timestamp.UnixOrZeroTimePtr(1000),
   614  			Status:           enumspb.WORKFLOW_EXECUTION_STATUS_FAILED,
   615  			HistoryLength:    123,
   616  		},
   617  		{
   618  			NamespaceId:      testNamespaceID,
   619  			Namespace:        testNamespace,
   620  			WorkflowId:       "another workflow ID",
   621  			RunId:            "another run ID",
   622  			WorkflowTypeName: testWorkflowTypeName,
   623  			StartTime:        timestamp.UnixOrZeroTimePtr(3),
   624  			ExecutionTime:    nil,
   625  			CloseTime:        timestamp.UnixOrZeroTimePtr(10),
   626  			Status:           enumspb.WORKFLOW_EXECUTION_STATUS_CONTINUED_AS_NEW,
   627  			HistoryLength:    456,
   628  		},
   629  		{
   630  			NamespaceId:      testNamespaceID,
   631  			Namespace:        testNamespace,
   632  			WorkflowId:       "and another workflow ID",
   633  			RunId:            "and another run ID",
   634  			WorkflowTypeName: testWorkflowTypeName,
   635  			StartTime:        timestamp.UnixOrZeroTimePtr(3),
   636  			ExecutionTime:    nil,
   637  			CloseTime:        timestamp.UnixOrZeroTimePtr(5),
   638  			Status:           enumspb.WORKFLOW_EXECUTION_STATUS_FAILED,
   639  			HistoryLength:    456,
   640  		},
   641  		{
   642  			NamespaceId:      "some random namespace ID",
   643  			Namespace:        "some random namespace name",
   644  			WorkflowId:       "another workflow ID",
   645  			RunId:            "another run ID",
   646  			WorkflowTypeName: testWorkflowTypeName,
   647  			StartTime:        timestamp.UnixOrZeroTimePtr(3),
   648  			ExecutionTime:    nil,
   649  			CloseTime:        timestamp.UnixOrZeroTimePtr(10000),
   650  			Status:           enumspb.WORKFLOW_EXECUTION_STATUS_CONTINUED_AS_NEW,
   651  			HistoryLength:    456,
   652  		},
   653  	}
   654  
   655  	for _, record := range s.visibilityRecords {
   656  		s.writeVisibilityRecordForQueryTest(record)
   657  	}
   658  }
   659  
   660  func (s *visibilityArchiverSuite) writeVisibilityRecordForQueryTest(record *archiverspb.VisibilityRecord) {
   661  	data, err := encode(record)
   662  	s.Require().NoError(err)
   663  	filename := constructVisibilityFilename(record.CloseTime.AsTime(), record.GetRunId())
   664  	s.Require().NoError(os.MkdirAll(path.Join(s.testQueryDirectory, record.GetNamespaceId()), testDirMode))
   665  	err = writeFile(path.Join(s.testQueryDirectory, record.GetNamespaceId(), filename), data, testFileMode)
   666  	s.Require().NoError(err)
   667  }
   668  
   669  func (s *visibilityArchiverSuite) assertFileExists(filepath string) {
   670  	exists, err := fileExists(filepath)
   671  	s.NoError(err)
   672  	s.True(exists)
   673  }