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 }