go.temporal.io/server@v1.23.0/common/archiver/filestore/query_parser_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  	"testing"
    29  	"time"
    30  
    31  	"github.com/stretchr/testify/require"
    32  	"github.com/stretchr/testify/suite"
    33  	enumspb "go.temporal.io/api/enums/v1"
    34  
    35  	"go.temporal.io/server/common/convert"
    36  )
    37  
    38  type queryParserSuite struct {
    39  	*require.Assertions
    40  	suite.Suite
    41  
    42  	parser QueryParser
    43  }
    44  
    45  func TestQueryParserSuite(t *testing.T) {
    46  	suite.Run(t, new(queryParserSuite))
    47  }
    48  
    49  func (s *queryParserSuite) SetupTest() {
    50  	s.Assertions = require.New(s.T())
    51  	s.parser = NewQueryParser()
    52  }
    53  
    54  func (s *queryParserSuite) TestParseWorkflowID_RunID_WorkflowType() {
    55  	testCases := []struct {
    56  		query       string
    57  		expectErr   bool
    58  		parsedQuery *parsedQuery
    59  	}{
    60  		{
    61  			query:     "WorkflowId = \"random workflowID\"",
    62  			expectErr: false,
    63  			parsedQuery: &parsedQuery{
    64  				workflowID: convert.StringPtr("random workflowID"),
    65  			},
    66  		},
    67  		{
    68  			query:     "WorkflowId = \"random workflowID\" and WorkflowId = \"random workflowID\"",
    69  			expectErr: false,
    70  			parsedQuery: &parsedQuery{
    71  				workflowID: convert.StringPtr("random workflowID"),
    72  			},
    73  		},
    74  		{
    75  			query:     "RunId = \"random runID\"",
    76  			expectErr: false,
    77  			parsedQuery: &parsedQuery{
    78  				runID: convert.StringPtr("random runID"),
    79  			},
    80  		},
    81  		{
    82  			query:     "WorkflowType = \"random typeName\"",
    83  			expectErr: false,
    84  			parsedQuery: &parsedQuery{
    85  				workflowTypeName: convert.StringPtr("random typeName"),
    86  			},
    87  		},
    88  		{
    89  			query:     "WorkflowId = 'random workflowID'",
    90  			expectErr: false,
    91  			parsedQuery: &parsedQuery{
    92  				workflowID: convert.StringPtr("random workflowID"),
    93  			},
    94  		},
    95  		{
    96  			query:     "WorkflowType = 'random typeName' and WorkflowType = \"another typeName\"",
    97  			expectErr: false,
    98  			parsedQuery: &parsedQuery{
    99  				emptyResult: true,
   100  			},
   101  		},
   102  		{
   103  			query:     "WorkflowType = 'random typeName' and (WorkflowId = \"random workflowID\" and RunId='random runID')",
   104  			expectErr: false,
   105  			parsedQuery: &parsedQuery{
   106  				workflowID:       convert.StringPtr("random workflowID"),
   107  				runID:            convert.StringPtr("random runID"),
   108  				workflowTypeName: convert.StringPtr("random typeName"),
   109  			},
   110  		},
   111  		{
   112  			query:     "runId = random workflowID",
   113  			expectErr: true,
   114  		},
   115  		{
   116  			query:     "WorkflowId = \"random workflowID\" or WorkflowId = \"another workflowID\"",
   117  			expectErr: true,
   118  		},
   119  		{
   120  			query:     "WorkflowId = \"random workflowID\" or runId = \"random runID\"",
   121  			expectErr: true,
   122  		},
   123  		{
   124  			query:     "workflowid = \"random workflowID\"",
   125  			expectErr: true,
   126  		},
   127  		{
   128  			query:     "runId > \"random workflowID\"",
   129  			expectErr: true,
   130  		},
   131  	}
   132  
   133  	for _, tc := range testCases {
   134  		parsedQuery, err := s.parser.Parse(tc.query)
   135  		if tc.expectErr {
   136  			s.Error(err)
   137  			continue
   138  		}
   139  		s.NoError(err)
   140  		s.Equal(tc.parsedQuery.emptyResult, parsedQuery.emptyResult)
   141  		if !tc.parsedQuery.emptyResult {
   142  			s.Equal(tc.parsedQuery.workflowID, parsedQuery.workflowID)
   143  			s.Equal(tc.parsedQuery.runID, parsedQuery.runID)
   144  			s.Equal(tc.parsedQuery.workflowTypeName, parsedQuery.workflowTypeName)
   145  		}
   146  	}
   147  }
   148  
   149  func (s *queryParserSuite) TestParseCloseStatus() {
   150  	testCases := []struct {
   151  		query       string
   152  		expectErr   bool
   153  		parsedQuery *parsedQuery
   154  	}{
   155  		{
   156  			query:     "ExecutionStatus = \"Completed\"",
   157  			expectErr: false,
   158  			parsedQuery: &parsedQuery{
   159  				status: toWorkflowExecutionStatusPtr(enumspb.WORKFLOW_EXECUTION_STATUS_COMPLETED),
   160  			},
   161  		},
   162  		{
   163  			query:     "ExecutionStatus = \"failed\"",
   164  			expectErr: false,
   165  			parsedQuery: &parsedQuery{
   166  				status: toWorkflowExecutionStatusPtr(enumspb.WORKFLOW_EXECUTION_STATUS_FAILED),
   167  			},
   168  		},
   169  		{
   170  			query:     "ExecutionStatus = \"canceled\"",
   171  			expectErr: false,
   172  			parsedQuery: &parsedQuery{
   173  				status: toWorkflowExecutionStatusPtr(enumspb.WORKFLOW_EXECUTION_STATUS_CANCELED),
   174  			},
   175  		},
   176  		{
   177  			query:     "ExecutionStatus = \"terminated\"",
   178  			expectErr: false,
   179  			parsedQuery: &parsedQuery{
   180  				status: toWorkflowExecutionStatusPtr(enumspb.WORKFLOW_EXECUTION_STATUS_TERMINATED),
   181  			},
   182  		},
   183  		{
   184  			query:     "ExecutionStatus = 'continuedasnew'",
   185  			expectErr: false,
   186  			parsedQuery: &parsedQuery{
   187  				status: toWorkflowExecutionStatusPtr(enumspb.WORKFLOW_EXECUTION_STATUS_CONTINUED_AS_NEW),
   188  			},
   189  		},
   190  		{
   191  			query:     "ExecutionStatus = 'TIMED_OUT'",
   192  			expectErr: false,
   193  			parsedQuery: &parsedQuery{
   194  				status: toWorkflowExecutionStatusPtr(enumspb.WORKFLOW_EXECUTION_STATUS_TIMED_OUT),
   195  			},
   196  		},
   197  		{
   198  			query:     "ExecutionStatus = 'Failed' and ExecutionStatus = \"Failed\"",
   199  			expectErr: false,
   200  			parsedQuery: &parsedQuery{
   201  				status: toWorkflowExecutionStatusPtr(enumspb.WORKFLOW_EXECUTION_STATUS_FAILED),
   202  			},
   203  		},
   204  		{
   205  			query:     "(ExecutionStatus = 'Timedout' and ExecutionStatus = \"canceled\")",
   206  			expectErr: false,
   207  			parsedQuery: &parsedQuery{
   208  				emptyResult: true,
   209  			},
   210  		},
   211  		{
   212  			query:     "status = \"Failed\"",
   213  			expectErr: true,
   214  		},
   215  		{
   216  			query:     "ExecutionStatus = \"Failed\" or ExecutionStatus = \"Failed\"",
   217  			expectErr: true,
   218  		},
   219  		{
   220  			query:     "ExecutionStatus = \"unknown\"",
   221  			expectErr: true,
   222  		},
   223  		{
   224  			query:     "ExecutionStatus > \"Failed\"",
   225  			expectErr: true,
   226  		},
   227  		{
   228  			query:     "ExecutionStatus = 3",
   229  			expectErr: false,
   230  			parsedQuery: &parsedQuery{
   231  				status: toWorkflowExecutionStatusPtr(enumspb.WORKFLOW_EXECUTION_STATUS_FAILED),
   232  			},
   233  		},
   234  		{
   235  			query:     "CloseStatus = 10",
   236  			expectErr: true,
   237  		},
   238  	}
   239  
   240  	for _, tc := range testCases {
   241  		parsedQuery, err := s.parser.Parse(tc.query)
   242  		if tc.expectErr {
   243  			s.Error(err)
   244  			continue
   245  		}
   246  		s.NoError(err)
   247  		s.Equal(tc.parsedQuery.emptyResult, parsedQuery.emptyResult)
   248  		if !tc.parsedQuery.emptyResult {
   249  			s.EqualValues(tc.parsedQuery.status, parsedQuery.status)
   250  		}
   251  	}
   252  }
   253  
   254  func (s *queryParserSuite) TestParseCloseTime() {
   255  	testCases := []struct {
   256  		query       string
   257  		expectErr   bool
   258  		parsedQuery *parsedQuery
   259  	}{
   260  		{
   261  			query:     "CloseTime <= 1000",
   262  			expectErr: false,
   263  			parsedQuery: &parsedQuery{
   264  				earliestCloseTime: time.Time{},
   265  				latestCloseTime:   time.Unix(0, 1000),
   266  			},
   267  		},
   268  		{
   269  			query:     "CloseTime < 2000 and CloseTime <= 1000 and CloseTime > 300",
   270  			expectErr: false,
   271  			parsedQuery: &parsedQuery{
   272  				earliestCloseTime: time.Unix(0, 301),
   273  				latestCloseTime:   time.Unix(0, 1000),
   274  			},
   275  		},
   276  		{
   277  			query:     "CloseTime = 2000 and (CloseTime > 1000 and CloseTime <= 9999)",
   278  			expectErr: false,
   279  			parsedQuery: &parsedQuery{
   280  				earliestCloseTime: time.Unix(0, 2000),
   281  				latestCloseTime:   time.Unix(0, 2000),
   282  			},
   283  		},
   284  		{
   285  			query:     "CloseTime <= \"2019-01-01T11:11:11Z\" and CloseTime >= 1000000",
   286  			expectErr: false,
   287  			parsedQuery: &parsedQuery{
   288  				earliestCloseTime: time.Unix(0, 1000000),
   289  				latestCloseTime:   time.Date(2019, 01, 01, 11, 11, 11, 0, time.UTC),
   290  			},
   291  		},
   292  		{
   293  			query:     "closeTime = 2000",
   294  			expectErr: true,
   295  		},
   296  		{
   297  			query:     "CloseTime > \"2019-01-01 00:00:00\"",
   298  			expectErr: true,
   299  		},
   300  		{
   301  			query:     "ExecutionStatus > 2000 or ExecutionStatus < 1000",
   302  			expectErr: true,
   303  		},
   304  	}
   305  
   306  	for i, tc := range testCases {
   307  		parsedQuery, err := s.parser.Parse(tc.query)
   308  		if tc.expectErr {
   309  			s.Error(err)
   310  			continue
   311  		}
   312  		s.NoError(err, "case %d", i)
   313  		s.Equal(tc.parsedQuery.emptyResult, parsedQuery.emptyResult, "case %d", i)
   314  		if !tc.parsedQuery.emptyResult {
   315  			s.True(tc.parsedQuery.earliestCloseTime.Equal(parsedQuery.earliestCloseTime), "case %d", i)
   316  			s.True(tc.parsedQuery.latestCloseTime.Equal(parsedQuery.latestCloseTime), "case %d", i)
   317  		}
   318  	}
   319  }
   320  
   321  func (s *queryParserSuite) TestParse() {
   322  	testCases := []struct {
   323  		query       string
   324  		expectErr   bool
   325  		parsedQuery *parsedQuery
   326  	}{
   327  		{
   328  			query:     "CloseTime <= \"2019-01-01T11:11:11Z\" and WorkflowId = 'random workflowID'",
   329  			expectErr: false,
   330  			parsedQuery: &parsedQuery{
   331  				earliestCloseTime: time.Time{},
   332  				latestCloseTime:   time.Date(2019, 01, 01, 11, 11, 11, 0, time.UTC),
   333  				workflowID:        convert.StringPtr("random workflowID"),
   334  			},
   335  		},
   336  		{
   337  			query:     "CloseTime > 1999 and CloseTime < 10000 and RunId = 'random runID' and ExecutionStatus = 'Failed'",
   338  			expectErr: false,
   339  			parsedQuery: &parsedQuery{
   340  				earliestCloseTime: time.Unix(0, 2000).UTC(),
   341  				latestCloseTime:   time.Unix(0, 9999).UTC(),
   342  				runID:             convert.StringPtr("random runID"),
   343  				status:            toWorkflowExecutionStatusPtr(enumspb.WORKFLOW_EXECUTION_STATUS_FAILED),
   344  			},
   345  		},
   346  		{
   347  			query:     "CloseTime > 2001 and CloseTime < 10000 and (RunId = 'random runID') and ExecutionStatus = 'Failed' and (RunId = 'another ID')",
   348  			expectErr: false,
   349  			parsedQuery: &parsedQuery{
   350  				emptyResult: true,
   351  			},
   352  		},
   353  	}
   354  
   355  	for i, tc := range testCases {
   356  		parsedQuery, err := s.parser.Parse(tc.query)
   357  		if tc.expectErr {
   358  			s.Error(err)
   359  			continue
   360  		}
   361  		s.NoError(err, "case %d", i)
   362  		s.Equal(tc.parsedQuery.emptyResult, parsedQuery.emptyResult, "case %d", i)
   363  		if !tc.parsedQuery.emptyResult {
   364  			s.Equal(tc.parsedQuery, parsedQuery, "case %d", i)
   365  		}
   366  	}
   367  }