go.temporal.io/server@v1.23.0/common/persistence/sql/sqlplugin/tests/visibility.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 tests
    26  
    27  import (
    28  	"math/rand"
    29  	"sort"
    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  
    37  	"go.temporal.io/server/common/convert"
    38  	"go.temporal.io/server/common/persistence/sql/sqlplugin"
    39  	"go.temporal.io/server/common/primitives"
    40  	"go.temporal.io/server/common/shuffle"
    41  )
    42  
    43  type (
    44  	visibilitySuite struct {
    45  		suite.Suite
    46  		*require.Assertions
    47  
    48  		store sqlplugin.Visibility
    49  	}
    50  )
    51  
    52  const (
    53  	testVisibilityEncoding         = "random encoding"
    54  	testVisibilityWorkflowTypeName = "random workflow type name"
    55  	testVisibilityWorkflowID       = "random workflow ID"
    56  )
    57  
    58  var (
    59  	testVisibilityData = []byte("random history execution activity data")
    60  )
    61  
    62  var testVisibilityCloseStatus = []enumspb.WorkflowExecutionStatus{
    63  	enumspb.WORKFLOW_EXECUTION_STATUS_COMPLETED,
    64  	enumspb.WORKFLOW_EXECUTION_STATUS_FAILED,
    65  	enumspb.WORKFLOW_EXECUTION_STATUS_CANCELED,
    66  	enumspb.WORKFLOW_EXECUTION_STATUS_TERMINATED,
    67  	enumspb.WORKFLOW_EXECUTION_STATUS_CONTINUED_AS_NEW,
    68  	enumspb.WORKFLOW_EXECUTION_STATUS_TIMED_OUT,
    69  }
    70  
    71  func NewVisibilitySuite(
    72  	t *testing.T,
    73  	store sqlplugin.Visibility,
    74  ) *visibilitySuite {
    75  	return &visibilitySuite{
    76  		Assertions: require.New(t),
    77  		store:      store,
    78  	}
    79  }
    80  
    81  func (s *visibilitySuite) SetupSuite() {
    82  
    83  }
    84  
    85  func (s *visibilitySuite) TearDownSuite() {
    86  
    87  }
    88  
    89  func (s *visibilitySuite) SetupTest() {
    90  	s.Assertions = require.New(s.T())
    91  }
    92  
    93  func (s *visibilitySuite) TearDownTest() {
    94  
    95  }
    96  
    97  func (s *visibilitySuite) TestInsertSelect_NonExists() {
    98  	namespaceID := primitives.NewUUID()
    99  	runID := primitives.NewUUID()
   100  	workflowTypeName := shuffle.String(testVisibilityWorkflowTypeName)
   101  	workflowID := shuffle.String(testVisibilityWorkflowID)
   102  	startTime := s.now()
   103  	executionTime := startTime.Add(time.Second)
   104  	status := int32(0)
   105  	closeTime := (*time.Time)(nil)
   106  	historyLength := (*int64)(nil)
   107  
   108  	visibility := s.newRandomVisibilityRow(
   109  		namespaceID,
   110  		runID,
   111  		workflowTypeName,
   112  		workflowID,
   113  		startTime,
   114  		executionTime,
   115  		status,
   116  		closeTime,
   117  		historyLength,
   118  	)
   119  	result, err := s.store.InsertIntoVisibility(newVisibilityContext(), &visibility)
   120  	s.NoError(err)
   121  	rowsAffected, err := result.RowsAffected()
   122  	s.NoError(err)
   123  	s.Equal(1, int(rowsAffected))
   124  
   125  	selectFilter := sqlplugin.VisibilitySelectFilter{
   126  		NamespaceID: namespaceID.String(),
   127  		RunID:       convert.StringPtr(runID.String()),
   128  	}
   129  	rows, err := s.store.SelectFromVisibility(newVisibilityContext(), selectFilter)
   130  	s.NoError(err)
   131  	for index := range rows {
   132  		rows[index].NamespaceID = namespaceID.String()
   133  	}
   134  	s.Equal([]sqlplugin.VisibilityRow{visibility}, rows)
   135  }
   136  
   137  func (s *visibilitySuite) TestInsertSelect_Exists() {
   138  	namespaceID := primitives.NewUUID()
   139  	runID := primitives.NewUUID()
   140  	workflowTypeName := shuffle.String(testVisibilityWorkflowTypeName)
   141  	workflowID := shuffle.String(testVisibilityWorkflowID)
   142  	startTime := s.now()
   143  	executionTime := startTime.Add(time.Second)
   144  	status := int32(0)
   145  	closeTime := (*time.Time)(nil)
   146  	historyLength := (*int64)(nil)
   147  
   148  	visibility1 := s.newRandomVisibilityRow(
   149  		namespaceID,
   150  		runID,
   151  		workflowTypeName,
   152  		workflowID,
   153  		startTime,
   154  		executionTime,
   155  		status,
   156  		closeTime,
   157  		historyLength,
   158  	)
   159  	result, err := s.store.InsertIntoVisibility(newVisibilityContext(), &visibility1)
   160  	s.NoError(err)
   161  	rowsAffected, err := result.RowsAffected()
   162  	s.NoError(err)
   163  	s.Equal(1, int(rowsAffected))
   164  
   165  	visibility2 := s.newRandomVisibilityRow(
   166  		namespaceID,
   167  		runID,
   168  		workflowTypeName,
   169  		workflowID,
   170  		startTime,
   171  		executionTime,
   172  		status,
   173  		closeTime,
   174  		historyLength,
   175  	)
   176  	_, err = s.store.InsertIntoVisibility(newVisibilityContext(), &visibility2)
   177  	s.NoError(err)
   178  	// NOTE: cannot do assertion on affected rows
   179  	//  PostgreSQL will return 0
   180  	//  MySQL will return 1: ref https://dev.mysql.com/doc/c-api/5.7/en/mysql-affected-rows.html
   181  
   182  	selectFilter := sqlplugin.VisibilitySelectFilter{
   183  		NamespaceID: namespaceID.String(),
   184  		RunID:       convert.StringPtr(runID.String()),
   185  	}
   186  	rows, err := s.store.SelectFromVisibility(newVisibilityContext(), selectFilter)
   187  	s.NoError(err)
   188  	for index := range rows {
   189  		rows[index].NamespaceID = namespaceID.String()
   190  	}
   191  	s.Equal([]sqlplugin.VisibilityRow{visibility1}, rows)
   192  }
   193  
   194  func (s *visibilitySuite) TestReplaceSelect_NonExists() {
   195  	namespaceID := primitives.NewUUID()
   196  	runID := primitives.NewUUID()
   197  	workflowTypeName := shuffle.String(testVisibilityWorkflowTypeName)
   198  	workflowID := shuffle.String(testVisibilityWorkflowID)
   199  	startTime := s.now()
   200  	executionTime := startTime.Add(time.Second)
   201  	status := int32(0)
   202  	closeTime := executionTime.Add(time.Second)
   203  	historyLength := rand.Int63()
   204  
   205  	visibility := s.newRandomVisibilityRow(
   206  		namespaceID,
   207  		runID,
   208  		workflowTypeName,
   209  		workflowID,
   210  		startTime,
   211  		executionTime,
   212  		status,
   213  		timePtr(closeTime),
   214  		convert.Int64Ptr(historyLength),
   215  	)
   216  	result, err := s.store.ReplaceIntoVisibility(newVisibilityContext(), &visibility)
   217  	s.NoError(err)
   218  	rowsAffected, err := result.RowsAffected()
   219  	s.NoError(err)
   220  	s.Equal(1, int(rowsAffected))
   221  
   222  	selectFilter := sqlplugin.VisibilitySelectFilter{
   223  		NamespaceID: namespaceID.String(),
   224  		RunID:       convert.StringPtr(runID.String()),
   225  	}
   226  	rows, err := s.store.SelectFromVisibility(newVisibilityContext(), selectFilter)
   227  	s.NoError(err)
   228  	for index := range rows {
   229  		rows[index].NamespaceID = namespaceID.String()
   230  	}
   231  	s.Equal([]sqlplugin.VisibilityRow{visibility}, rows)
   232  }
   233  
   234  func (s *visibilitySuite) TestReplaceSelect_Exists() {
   235  	namespaceID := primitives.NewUUID()
   236  	runID := primitives.NewUUID()
   237  	workflowTypeName := shuffle.String(testVisibilityWorkflowTypeName)
   238  	workflowID := shuffle.String(testVisibilityWorkflowID)
   239  	startTime := s.now()
   240  	executionTime := startTime.Add(time.Second)
   241  	status := int32(0)
   242  	closeTime := executionTime.Add(time.Second)
   243  	historyLength := rand.Int63()
   244  
   245  	visibility := s.newRandomVisibilityRow(
   246  		namespaceID,
   247  		runID,
   248  		workflowTypeName,
   249  		workflowID,
   250  		startTime,
   251  		executionTime,
   252  		status,
   253  		timePtr(closeTime),
   254  		convert.Int64Ptr(historyLength),
   255  	)
   256  	result, err := s.store.ReplaceIntoVisibility(newVisibilityContext(), &visibility)
   257  	s.NoError(err)
   258  	rowsAffected, err := result.RowsAffected()
   259  	s.NoError(err)
   260  	s.Equal(1, int(rowsAffected))
   261  
   262  	visibility = s.newRandomVisibilityRow(
   263  		namespaceID,
   264  		runID,
   265  		workflowTypeName,
   266  		workflowID,
   267  		startTime,
   268  		executionTime,
   269  		status,
   270  		timePtr(closeTime),
   271  		convert.Int64Ptr(historyLength),
   272  	)
   273  	_, err = s.store.ReplaceIntoVisibility(newVisibilityContext(), &visibility)
   274  	s.NoError(err)
   275  	// NOTE: cannot do assertion on affected rows
   276  	//  PostgreSQL will return 1
   277  	//  MySQL will return 2: ref https://dev.mysql.com/doc/c-api/5.7/en/mysql-affected-rows.html
   278  
   279  	selectFilter := sqlplugin.VisibilitySelectFilter{
   280  		NamespaceID: namespaceID.String(),
   281  		RunID:       convert.StringPtr(runID.String()),
   282  	}
   283  	rows, err := s.store.SelectFromVisibility(newVisibilityContext(), selectFilter)
   284  	s.NoError(err)
   285  	for index := range rows {
   286  		rows[index].NamespaceID = namespaceID.String()
   287  	}
   288  	s.Equal([]sqlplugin.VisibilityRow{visibility}, rows)
   289  }
   290  
   291  func (s *visibilitySuite) TestDeleteGet() {
   292  	namespaceID := primitives.NewUUID()
   293  	runID := primitives.NewUUID()
   294  
   295  	deleteFilter := sqlplugin.VisibilityDeleteFilter{
   296  		NamespaceID: namespaceID.String(),
   297  		RunID:       runID.String(),
   298  	}
   299  	result, err := s.store.DeleteFromVisibility(newVisibilityContext(), deleteFilter)
   300  	s.NoError(err)
   301  	rowsAffected, err := result.RowsAffected()
   302  	s.NoError(err)
   303  	s.Equal(0, int(rowsAffected))
   304  
   305  	getFilter := sqlplugin.VisibilityGetFilter{
   306  		NamespaceID: namespaceID.String(),
   307  		RunID:       runID.String(),
   308  	}
   309  	_, err = s.store.GetFromVisibility(newVisibilityContext(), getFilter)
   310  	s.Error(err) // TODO persistence layer should do proper error translation
   311  }
   312  
   313  func (s *visibilitySuite) TestInsertDeleteGet() {
   314  	namespaceID := primitives.NewUUID()
   315  	runID := primitives.NewUUID()
   316  	workflowTypeName := shuffle.String(testVisibilityWorkflowTypeName)
   317  	workflowID := shuffle.String(testVisibilityWorkflowID)
   318  	startTime := s.now()
   319  	executionTime := startTime.Add(time.Second)
   320  	status := int32(0)
   321  	closeTime := (*time.Time)(nil)
   322  	historyLength := (*int64)(nil)
   323  
   324  	visibility := s.newRandomVisibilityRow(
   325  		namespaceID,
   326  		runID,
   327  		workflowTypeName,
   328  		workflowID,
   329  		startTime,
   330  		executionTime,
   331  		status,
   332  		closeTime,
   333  		historyLength,
   334  	)
   335  	result, err := s.store.InsertIntoVisibility(newVisibilityContext(), &visibility)
   336  	s.NoError(err)
   337  	rowsAffected, err := result.RowsAffected()
   338  	s.NoError(err)
   339  	s.Equal(1, int(rowsAffected))
   340  
   341  	deleteFilter := sqlplugin.VisibilityDeleteFilter{
   342  		NamespaceID: namespaceID.String(),
   343  		RunID:       runID.String(),
   344  	}
   345  	result, err = s.store.DeleteFromVisibility(newVisibilityContext(), deleteFilter)
   346  	s.NoError(err)
   347  	rowsAffected, err = result.RowsAffected()
   348  	s.NoError(err)
   349  	s.Equal(1, int(rowsAffected))
   350  
   351  	getFilter := sqlplugin.VisibilityGetFilter{
   352  		NamespaceID: namespaceID.String(),
   353  		RunID:       runID.String(),
   354  	}
   355  	_, err = s.store.GetFromVisibility(newVisibilityContext(), getFilter)
   356  	s.Error(err) // TODO persistence layer should do proper error translation
   357  }
   358  
   359  func (s *visibilitySuite) TestReplaceDeleteGet() {
   360  	namespaceID := primitives.NewUUID()
   361  	runID := primitives.NewUUID()
   362  	workflowTypeName := shuffle.String(testVisibilityWorkflowTypeName)
   363  	workflowID := shuffle.String(testVisibilityWorkflowID)
   364  	startTime := s.now()
   365  	executionTime := startTime.Add(time.Second)
   366  	status := int32(0)
   367  	closeTime := executionTime.Add(time.Second)
   368  	historyLength := rand.Int63()
   369  
   370  	visibility := s.newRandomVisibilityRow(
   371  		namespaceID,
   372  		runID,
   373  		workflowTypeName,
   374  		workflowID,
   375  		startTime,
   376  		executionTime,
   377  		status,
   378  		timePtr(closeTime),
   379  		convert.Int64Ptr(historyLength),
   380  	)
   381  	result, err := s.store.ReplaceIntoVisibility(newVisibilityContext(), &visibility)
   382  	s.NoError(err)
   383  	rowsAffected, err := result.RowsAffected()
   384  	s.NoError(err)
   385  	s.Equal(1, int(rowsAffected))
   386  
   387  	deleteFilter := sqlplugin.VisibilityDeleteFilter{
   388  		NamespaceID: namespaceID.String(),
   389  		RunID:       runID.String(),
   390  	}
   391  	result, err = s.store.DeleteFromVisibility(newVisibilityContext(), deleteFilter)
   392  	s.NoError(err)
   393  	rowsAffected, err = result.RowsAffected()
   394  	s.NoError(err)
   395  	s.Equal(1, int(rowsAffected))
   396  
   397  	getFilter := sqlplugin.VisibilityGetFilter{
   398  		NamespaceID: namespaceID.String(),
   399  		RunID:       runID.String(),
   400  	}
   401  	_, err = s.store.GetFromVisibility(newVisibilityContext(), getFilter)
   402  	s.Error(err) // TODO persistence layer should do proper error translation
   403  }
   404  
   405  func (s *visibilitySuite) TestSelect_MinStartTime_MaxStartTime_WorkflowID_StatusOpen_Single() {
   406  	pageSize := 1
   407  
   408  	namespaceID := primitives.NewUUID()
   409  	runID := primitives.NewUUID()
   410  	workflowTypeName := shuffle.String(testVisibilityWorkflowTypeName)
   411  	workflowID := shuffle.String(testVisibilityWorkflowID)
   412  	startTime := s.now()
   413  	executionTime := startTime.Add(time.Second)
   414  	status := int32(enumspb.WORKFLOW_EXECUTION_STATUS_RUNNING)
   415  	closeTime := (*time.Time)(nil)
   416  	historyLength := (*int64)(nil)
   417  
   418  	visibility := s.newRandomVisibilityRow(
   419  		namespaceID,
   420  		runID,
   421  		workflowTypeName,
   422  		workflowID,
   423  		startTime,
   424  		executionTime,
   425  		status,
   426  		closeTime,
   427  		historyLength,
   428  	)
   429  	result, err := s.store.InsertIntoVisibility(newVisibilityContext(), &visibility)
   430  	s.NoError(err)
   431  	rowsAffected, err := result.RowsAffected()
   432  	s.NoError(err)
   433  	s.Equal(1, int(rowsAffected))
   434  
   435  	minStartTime := startTime
   436  	maxStartTime := startTime
   437  	selectFilter := sqlplugin.VisibilitySelectFilter{
   438  		NamespaceID:      namespaceID.String(),
   439  		WorkflowID:       convert.StringPtr(workflowID),
   440  		RunID:            convert.StringPtr(""),
   441  		WorkflowTypeName: nil,
   442  		MinTime:          timePtr(minStartTime),
   443  		MaxTime:          timePtr(maxStartTime),
   444  		Status:           int32(enumspb.WORKFLOW_EXECUTION_STATUS_RUNNING),
   445  		PageSize:         convert.IntPtr(pageSize),
   446  	}
   447  	rows, err := s.store.SelectFromVisibility(newVisibilityContext(), selectFilter)
   448  	s.NoError(err)
   449  	for index := range rows {
   450  		rows[index].NamespaceID = namespaceID.String()
   451  	}
   452  	s.Equal([]sqlplugin.VisibilityRow{visibility}, rows)
   453  }
   454  
   455  func (s *visibilitySuite) TestSelect_MinStartTime_MaxStartTime_WorkflowID_StatusOpen_Multiple() {
   456  	numStartTime := 20
   457  	visibilityPerStartTime := 4
   458  	pageSize := 5
   459  
   460  	var visibilities []sqlplugin.VisibilityRow
   461  
   462  	namespaceID := primitives.NewUUID()
   463  	workflowID := shuffle.String(testVisibilityWorkflowID)
   464  	startTime := s.now()
   465  	minStartTime := startTime
   466  	maxStartTime := startTime.Add(time.Duration(numStartTime) * time.Second)
   467  	executionTime := startTime.Add(time.Second)
   468  	status := int32(enumspb.WORKFLOW_EXECUTION_STATUS_RUNNING)
   469  	closeTime := (*time.Time)(nil)
   470  	historyLength := (*int64)(nil)
   471  	for i := 0; i < numStartTime; i++ {
   472  		for j := 0; j < visibilityPerStartTime; j++ {
   473  			runID := primitives.NewUUID()
   474  			workflowTypeName := shuffle.String(testVisibilityWorkflowTypeName)
   475  			visibility := s.newRandomVisibilityRow(
   476  				namespaceID,
   477  				runID,
   478  				workflowTypeName,
   479  				workflowID,
   480  				startTime,
   481  				executionTime,
   482  				status,
   483  				closeTime,
   484  				historyLength,
   485  			)
   486  			result, err := s.store.InsertIntoVisibility(newVisibilityContext(), &visibility)
   487  			s.NoError(err)
   488  			rowsAffected, err := result.RowsAffected()
   489  			s.NoError(err)
   490  			s.Equal(1, int(rowsAffected))
   491  
   492  			visibilities = append(visibilities, visibility)
   493  		}
   494  
   495  		startTime = startTime.Add(time.Second)
   496  	}
   497  
   498  	selectFilter := sqlplugin.VisibilitySelectFilter{
   499  		NamespaceID:      namespaceID.String(),
   500  		WorkflowID:       convert.StringPtr(workflowID),
   501  		RunID:            convert.StringPtr(""),
   502  		WorkflowTypeName: nil,
   503  		MinTime:          timePtr(minStartTime),
   504  		MaxTime:          timePtr(maxStartTime),
   505  		Status:           int32(enumspb.WORKFLOW_EXECUTION_STATUS_RUNNING),
   506  		PageSize:         convert.IntPtr(pageSize),
   507  	}
   508  	var rows []sqlplugin.VisibilityRow
   509  	for {
   510  		rowsPerPage, err := s.store.SelectFromVisibility(newVisibilityContext(), selectFilter)
   511  		s.NoError(err)
   512  		rows = append(rows, rowsPerPage...)
   513  
   514  		if len(rowsPerPage) > 0 {
   515  			lastVisibility := rowsPerPage[len(rowsPerPage)-1]
   516  			selectFilter.MaxTime = timePtr(lastVisibility.StartTime)
   517  			selectFilter.RunID = convert.StringPtr(lastVisibility.RunID)
   518  		} else {
   519  			break
   520  		}
   521  	}
   522  	s.Len(rows, len(visibilities))
   523  	s.sortByStartTimeDescRunIDAsc(visibilities)
   524  	for index := range rows {
   525  		rows[index].NamespaceID = namespaceID.String()
   526  	}
   527  	s.Equal(visibilities, rows)
   528  }
   529  
   530  func (s *visibilitySuite) TestSelect_MinStartTime_MaxStartTime_WorkflowID_StatusClose_Single() {
   531  	pageSize := 1
   532  
   533  	namespaceID := primitives.NewUUID()
   534  	runID := primitives.NewUUID()
   535  	workflowTypeName := shuffle.String(testVisibilityWorkflowTypeName)
   536  	workflowID := shuffle.String(testVisibilityWorkflowID)
   537  	startTime := s.now()
   538  	executionTime := startTime.Add(time.Second)
   539  	status := int32(enumspb.WORKFLOW_EXECUTION_STATUS_COMPLETED)
   540  	closeTime := executionTime.Add(time.Second)
   541  	historyLength := rand.Int63()
   542  
   543  	visibility := s.newRandomVisibilityRow(
   544  		namespaceID,
   545  		runID,
   546  		workflowTypeName,
   547  		workflowID,
   548  		startTime,
   549  		executionTime,
   550  		status,
   551  		timePtr(closeTime),
   552  		convert.Int64Ptr(historyLength),
   553  	)
   554  	result, err := s.store.ReplaceIntoVisibility(newVisibilityContext(), &visibility)
   555  	s.NoError(err)
   556  	rowsAffected, err := result.RowsAffected()
   557  	s.NoError(err)
   558  	s.Equal(1, int(rowsAffected))
   559  
   560  	minStartTime := closeTime
   561  	maxStartTime := closeTime
   562  	selectFilter := sqlplugin.VisibilitySelectFilter{
   563  		NamespaceID:      namespaceID.String(),
   564  		WorkflowID:       convert.StringPtr(workflowID),
   565  		RunID:            convert.StringPtr(""),
   566  		WorkflowTypeName: nil,
   567  		MinTime:          timePtr(minStartTime),
   568  		MaxTime:          timePtr(maxStartTime),
   569  		Status:           int32(enumspb.WORKFLOW_EXECUTION_STATUS_UNSPECIFIED),
   570  		PageSize:         convert.IntPtr(pageSize),
   571  	}
   572  	rows, err := s.store.SelectFromVisibility(newVisibilityContext(), selectFilter)
   573  	s.NoError(err)
   574  	for index := range rows {
   575  		rows[index].NamespaceID = namespaceID.String()
   576  	}
   577  	s.Equal([]sqlplugin.VisibilityRow{visibility}, rows)
   578  }
   579  
   580  func (s *visibilitySuite) TestSelect_MinStartTime_MaxStartTime_WorkflowID_StatusClose_Multiple() {
   581  	numStartTime := 20
   582  	visibilityPerStartTime := 4
   583  	pageSize := 5
   584  
   585  	var visibilities []sqlplugin.VisibilityRow
   586  
   587  	namespaceID := primitives.NewUUID()
   588  	workflowID := shuffle.String(testVisibilityWorkflowID)
   589  	startTime := s.now()
   590  	executionTime := startTime.Add(time.Second)
   591  	status := int32(enumspb.WORKFLOW_EXECUTION_STATUS_COMPLETED)
   592  	closeTime := executionTime.Add(time.Second)
   593  	historyLength := rand.Int63()
   594  	minStartTime := closeTime
   595  	maxStartTime := closeTime.Add(time.Duration(numStartTime) * time.Second)
   596  	for i := 0; i < numStartTime; i++ {
   597  		for j := 0; j < visibilityPerStartTime; j++ {
   598  			runID := primitives.NewUUID()
   599  			workflowTypeName := shuffle.String(testVisibilityWorkflowTypeName)
   600  			visibility := s.newRandomVisibilityRow(
   601  				namespaceID,
   602  				runID,
   603  				workflowTypeName,
   604  				workflowID,
   605  				startTime,
   606  				executionTime,
   607  				status,
   608  				timePtr(closeTime),
   609  				convert.Int64Ptr(historyLength),
   610  			)
   611  			result, err := s.store.ReplaceIntoVisibility(newVisibilityContext(), &visibility)
   612  			s.NoError(err)
   613  			rowsAffected, err := result.RowsAffected()
   614  			s.NoError(err)
   615  			s.Equal(1, int(rowsAffected))
   616  
   617  			visibilities = append(visibilities, visibility)
   618  		}
   619  		closeTime = closeTime.Add(time.Second)
   620  	}
   621  
   622  	selectFilter := sqlplugin.VisibilitySelectFilter{
   623  		NamespaceID:      namespaceID.String(),
   624  		WorkflowID:       convert.StringPtr(workflowID),
   625  		RunID:            convert.StringPtr(""),
   626  		WorkflowTypeName: nil,
   627  		MinTime:          timePtr(minStartTime),
   628  		MaxTime:          timePtr(maxStartTime),
   629  		Status:           int32(enumspb.WORKFLOW_EXECUTION_STATUS_UNSPECIFIED),
   630  		PageSize:         convert.IntPtr(pageSize),
   631  	}
   632  	var rows []sqlplugin.VisibilityRow
   633  	for {
   634  		rowsPerPage, err := s.store.SelectFromVisibility(newVisibilityContext(), selectFilter)
   635  		s.NoError(err)
   636  		rows = append(rows, rowsPerPage...)
   637  
   638  		if len(rowsPerPage) > 0 {
   639  			lastVisibility := rowsPerPage[len(rowsPerPage)-1]
   640  			selectFilter.MaxTime = lastVisibility.CloseTime
   641  			selectFilter.RunID = convert.StringPtr(lastVisibility.RunID)
   642  		} else {
   643  			break
   644  		}
   645  	}
   646  	s.Len(rows, len(visibilities))
   647  	s.sortByCloseTimeDescRunIDAsc(visibilities)
   648  	for index := range rows {
   649  		rows[index].NamespaceID = namespaceID.String()
   650  	}
   651  	s.Equal(visibilities, rows)
   652  }
   653  
   654  func (s *visibilitySuite) TestSelect_MinStartTime_MaxStartTime_WorkflowTypeName_StatusOpen_Single() {
   655  	pageSize := 1
   656  
   657  	namespaceID := primitives.NewUUID()
   658  	runID := primitives.NewUUID()
   659  	workflowTypeName := shuffle.String(testVisibilityWorkflowTypeName)
   660  	workflowID := shuffle.String(testVisibilityWorkflowID)
   661  	startTime := s.now()
   662  	executionTime := startTime.Add(time.Second)
   663  	status := int32(enumspb.WORKFLOW_EXECUTION_STATUS_RUNNING)
   664  	closeTime := (*time.Time)(nil)
   665  	historyLength := (*int64)(nil)
   666  
   667  	visibility := s.newRandomVisibilityRow(
   668  		namespaceID,
   669  		runID,
   670  		workflowTypeName,
   671  		workflowID,
   672  		startTime,
   673  		executionTime,
   674  		status,
   675  		closeTime,
   676  		historyLength,
   677  	)
   678  	result, err := s.store.InsertIntoVisibility(newVisibilityContext(), &visibility)
   679  	s.NoError(err)
   680  	rowsAffected, err := result.RowsAffected()
   681  	s.NoError(err)
   682  	s.Equal(1, int(rowsAffected))
   683  
   684  	minStartTime := startTime
   685  	maxStartTime := startTime
   686  	selectFilter := sqlplugin.VisibilitySelectFilter{
   687  		NamespaceID:      namespaceID.String(),
   688  		WorkflowID:       nil,
   689  		RunID:            convert.StringPtr(""),
   690  		WorkflowTypeName: convert.StringPtr(workflowTypeName),
   691  		MinTime:          timePtr(minStartTime),
   692  		MaxTime:          timePtr(maxStartTime),
   693  		Status:           int32(enumspb.WORKFLOW_EXECUTION_STATUS_RUNNING),
   694  		PageSize:         convert.IntPtr(pageSize),
   695  	}
   696  	rows, err := s.store.SelectFromVisibility(newVisibilityContext(), selectFilter)
   697  	s.NoError(err)
   698  	for index := range rows {
   699  		rows[index].NamespaceID = namespaceID.String()
   700  	}
   701  	s.Equal([]sqlplugin.VisibilityRow{visibility}, rows)
   702  }
   703  
   704  func (s *visibilitySuite) TestSelect_MinStartTime_MaxStartTime_WorkflowTypeName_StatusOpen_Multiple() {
   705  	numStartTime := 20
   706  	visibilityPerStartTime := 4
   707  	pageSize := 5
   708  
   709  	var visibilities []sqlplugin.VisibilityRow
   710  
   711  	namespaceID := primitives.NewUUID()
   712  	workflowTypeName := shuffle.String(testVisibilityWorkflowTypeName)
   713  	startTime := s.now()
   714  	minStartTime := startTime
   715  	maxStartTime := startTime.Add(time.Duration(numStartTime) * time.Second)
   716  	executionTime := startTime.Add(time.Second)
   717  	status := int32(enumspb.WORKFLOW_EXECUTION_STATUS_RUNNING)
   718  	closeTime := (*time.Time)(nil)
   719  	historyLength := (*int64)(nil)
   720  	for i := 0; i < numStartTime; i++ {
   721  		for j := 0; j < visibilityPerStartTime; j++ {
   722  			workflowID := shuffle.String(testVisibilityWorkflowID)
   723  			runID := primitives.NewUUID()
   724  			visibility := s.newRandomVisibilityRow(
   725  				namespaceID,
   726  				runID,
   727  				workflowTypeName,
   728  				workflowID,
   729  				startTime,
   730  				executionTime,
   731  				status,
   732  				closeTime,
   733  				historyLength,
   734  			)
   735  			result, err := s.store.InsertIntoVisibility(newVisibilityContext(), &visibility)
   736  			s.NoError(err)
   737  			rowsAffected, err := result.RowsAffected()
   738  			s.NoError(err)
   739  			s.Equal(1, int(rowsAffected))
   740  
   741  			visibilities = append(visibilities, visibility)
   742  		}
   743  
   744  		startTime = startTime.Add(time.Second)
   745  	}
   746  
   747  	selectFilter := sqlplugin.VisibilitySelectFilter{
   748  		NamespaceID:      namespaceID.String(),
   749  		WorkflowID:       nil,
   750  		RunID:            convert.StringPtr(""),
   751  		WorkflowTypeName: convert.StringPtr(workflowTypeName),
   752  		MinTime:          timePtr(minStartTime),
   753  		MaxTime:          timePtr(maxStartTime),
   754  		Status:           int32(enumspb.WORKFLOW_EXECUTION_STATUS_RUNNING),
   755  		PageSize:         convert.IntPtr(pageSize),
   756  	}
   757  	var rows []sqlplugin.VisibilityRow
   758  	for {
   759  		rowsPerPage, err := s.store.SelectFromVisibility(newVisibilityContext(), selectFilter)
   760  		s.NoError(err)
   761  		rows = append(rows, rowsPerPage...)
   762  
   763  		if len(rowsPerPage) > 0 {
   764  			lastVisibility := rowsPerPage[len(rowsPerPage)-1]
   765  			selectFilter.MaxTime = timePtr(lastVisibility.StartTime)
   766  			selectFilter.RunID = convert.StringPtr(lastVisibility.RunID)
   767  		} else {
   768  			break
   769  		}
   770  	}
   771  	s.Len(rows, len(visibilities))
   772  	s.sortByStartTimeDescRunIDAsc(visibilities)
   773  	for index := range rows {
   774  		rows[index].NamespaceID = namespaceID.String()
   775  	}
   776  	s.Equal(visibilities, rows)
   777  }
   778  
   779  func (s *visibilitySuite) TestSelect_MinStartTime_MaxStartTime_WorkflowTypeName_StatusClose_Single() {
   780  	pageSize := 1
   781  
   782  	namespaceID := primitives.NewUUID()
   783  	runID := primitives.NewUUID()
   784  	workflowTypeName := shuffle.String(testVisibilityWorkflowTypeName)
   785  	workflowID := shuffle.String(testVisibilityWorkflowID)
   786  	startTime := s.now()
   787  	executionTime := startTime.Add(time.Second)
   788  	status := int32(enumspb.WORKFLOW_EXECUTION_STATUS_COMPLETED)
   789  	closeTime := executionTime.Add(time.Second)
   790  	historyLength := rand.Int63()
   791  
   792  	visibility := s.newRandomVisibilityRow(
   793  		namespaceID,
   794  		runID,
   795  		workflowTypeName,
   796  		workflowID,
   797  		startTime,
   798  		executionTime,
   799  		status,
   800  		timePtr(closeTime),
   801  		convert.Int64Ptr(historyLength),
   802  	)
   803  	result, err := s.store.ReplaceIntoVisibility(newVisibilityContext(), &visibility)
   804  	s.NoError(err)
   805  	rowsAffected, err := result.RowsAffected()
   806  	s.NoError(err)
   807  	s.Equal(1, int(rowsAffected))
   808  
   809  	minStartTime := closeTime
   810  	maxStartTime := closeTime
   811  	selectFilter := sqlplugin.VisibilitySelectFilter{
   812  		NamespaceID:      namespaceID.String(),
   813  		WorkflowID:       nil,
   814  		RunID:            convert.StringPtr(""),
   815  		WorkflowTypeName: convert.StringPtr(workflowTypeName),
   816  		MinTime:          timePtr(minStartTime),
   817  		MaxTime:          timePtr(maxStartTime),
   818  		Status:           int32(enumspb.WORKFLOW_EXECUTION_STATUS_UNSPECIFIED),
   819  		PageSize:         convert.IntPtr(pageSize),
   820  	}
   821  	rows, err := s.store.SelectFromVisibility(newVisibilityContext(), selectFilter)
   822  	s.NoError(err)
   823  	for index := range rows {
   824  		rows[index].NamespaceID = namespaceID.String()
   825  	}
   826  	s.Equal([]sqlplugin.VisibilityRow{visibility}, rows)
   827  }
   828  
   829  func (s *visibilitySuite) TestSelect_MinStartTime_MaxStartTime_WorkflowTypeName_StatusClose_Multiple() {
   830  	numStartTime := 20
   831  	visibilityPerStartTime := 4
   832  	pageSize := 5
   833  
   834  	var visibilities []sqlplugin.VisibilityRow
   835  
   836  	namespaceID := primitives.NewUUID()
   837  	workflowTypeName := shuffle.String(testVisibilityWorkflowTypeName)
   838  	startTime := s.now()
   839  	executionTime := startTime.Add(time.Second)
   840  	status := int32(enumspb.WORKFLOW_EXECUTION_STATUS_COMPLETED)
   841  	closeTime := executionTime.Add(time.Second)
   842  	historyLength := rand.Int63()
   843  	minStartTime := closeTime
   844  	maxStartTime := closeTime.Add(time.Duration(numStartTime) * time.Second)
   845  	for i := 0; i < numStartTime; i++ {
   846  		for j := 0; j < visibilityPerStartTime; j++ {
   847  			workflowID := shuffle.String(testVisibilityWorkflowID)
   848  			runID := primitives.NewUUID()
   849  			visibility := s.newRandomVisibilityRow(
   850  				namespaceID,
   851  				runID,
   852  				workflowTypeName,
   853  				workflowID,
   854  				startTime,
   855  				executionTime,
   856  				status,
   857  				timePtr(closeTime),
   858  				convert.Int64Ptr(historyLength),
   859  			)
   860  			result, err := s.store.ReplaceIntoVisibility(newVisibilityContext(), &visibility)
   861  			s.NoError(err)
   862  			rowsAffected, err := result.RowsAffected()
   863  			s.NoError(err)
   864  			s.Equal(1, int(rowsAffected))
   865  
   866  			visibilities = append(visibilities, visibility)
   867  		}
   868  		closeTime = closeTime.Add(time.Second)
   869  	}
   870  
   871  	selectFilter := sqlplugin.VisibilitySelectFilter{
   872  		NamespaceID:      namespaceID.String(),
   873  		WorkflowID:       nil,
   874  		RunID:            convert.StringPtr(""),
   875  		WorkflowTypeName: convert.StringPtr(workflowTypeName),
   876  		MinTime:          timePtr(minStartTime),
   877  		MaxTime:          timePtr(maxStartTime),
   878  		Status:           int32(enumspb.WORKFLOW_EXECUTION_STATUS_UNSPECIFIED),
   879  		PageSize:         convert.IntPtr(pageSize),
   880  	}
   881  	var rows []sqlplugin.VisibilityRow
   882  	for {
   883  		rowsPerPage, err := s.store.SelectFromVisibility(newVisibilityContext(), selectFilter)
   884  		s.NoError(err)
   885  		rows = append(rows, rowsPerPage...)
   886  
   887  		if len(rowsPerPage) > 0 {
   888  			lastVisibility := rowsPerPage[len(rowsPerPage)-1]
   889  			selectFilter.MaxTime = lastVisibility.CloseTime
   890  			selectFilter.RunID = convert.StringPtr(lastVisibility.RunID)
   891  		} else {
   892  			break
   893  		}
   894  	}
   895  	s.Len(rows, len(visibilities))
   896  	s.sortByCloseTimeDescRunIDAsc(visibilities)
   897  	for index := range rows {
   898  		rows[index].NamespaceID = namespaceID.String()
   899  	}
   900  	s.Equal(visibilities, rows)
   901  }
   902  
   903  func (s *visibilitySuite) TestSelect_MinStartTime_MaxStartTime_StatusOpen_Single() {
   904  	pageSize := 1
   905  
   906  	namespaceID := primitives.NewUUID()
   907  	runID := primitives.NewUUID()
   908  	workflowTypeName := shuffle.String(testVisibilityWorkflowTypeName)
   909  	workflowID := shuffle.String(testVisibilityWorkflowID)
   910  	startTime := s.now()
   911  	executionTime := startTime.Add(time.Second)
   912  	status := int32(enumspb.WORKFLOW_EXECUTION_STATUS_RUNNING)
   913  	closeTime := (*time.Time)(nil)
   914  	historyLength := (*int64)(nil)
   915  
   916  	visibility := s.newRandomVisibilityRow(
   917  		namespaceID,
   918  		runID,
   919  		workflowTypeName,
   920  		workflowID,
   921  		startTime,
   922  		executionTime,
   923  		status,
   924  		closeTime,
   925  		historyLength,
   926  	)
   927  	result, err := s.store.InsertIntoVisibility(newVisibilityContext(), &visibility)
   928  	s.NoError(err)
   929  	rowsAffected, err := result.RowsAffected()
   930  	s.NoError(err)
   931  	s.Equal(1, int(rowsAffected))
   932  
   933  	minStartTime := startTime
   934  	maxStartTime := startTime
   935  	selectFilter := sqlplugin.VisibilitySelectFilter{
   936  		NamespaceID:      namespaceID.String(),
   937  		WorkflowID:       nil,
   938  		RunID:            convert.StringPtr(""),
   939  		WorkflowTypeName: nil,
   940  		MinTime:          timePtr(minStartTime),
   941  		MaxTime:          timePtr(maxStartTime),
   942  		Status:           int32(enumspb.WORKFLOW_EXECUTION_STATUS_RUNNING),
   943  		PageSize:         convert.IntPtr(pageSize),
   944  	}
   945  	rows, err := s.store.SelectFromVisibility(newVisibilityContext(), selectFilter)
   946  	s.NoError(err)
   947  	for index := range rows {
   948  		rows[index].NamespaceID = namespaceID.String()
   949  	}
   950  	s.Equal([]sqlplugin.VisibilityRow{visibility}, rows)
   951  }
   952  
   953  func (s *visibilitySuite) TestSelect_MinStartTime_MaxStartTime_StatusOpen_Multiple() {
   954  	numStartTime := 20
   955  	visibilityPerStartTime := 4
   956  	pageSize := 5
   957  
   958  	var visibilities []sqlplugin.VisibilityRow
   959  
   960  	namespaceID := primitives.NewUUID()
   961  	startTime := s.now()
   962  	minStartTime := startTime
   963  	maxStartTime := startTime.Add(time.Duration(numStartTime) * time.Second)
   964  	executionTime := startTime.Add(time.Second)
   965  	status := int32(enumspb.WORKFLOW_EXECUTION_STATUS_RUNNING)
   966  	closeTime := (*time.Time)(nil)
   967  	historyLength := (*int64)(nil)
   968  	for i := 0; i < numStartTime; i++ {
   969  		for j := 0; j < visibilityPerStartTime; j++ {
   970  			workflowID := shuffle.String(testVisibilityWorkflowID)
   971  			runID := primitives.NewUUID()
   972  			workflowTypeName := shuffle.String(testVisibilityWorkflowTypeName)
   973  			visibility := s.newRandomVisibilityRow(
   974  				namespaceID,
   975  				runID,
   976  				workflowTypeName,
   977  				workflowID,
   978  				startTime,
   979  				executionTime,
   980  				status,
   981  				closeTime,
   982  				historyLength,
   983  			)
   984  			result, err := s.store.InsertIntoVisibility(newVisibilityContext(), &visibility)
   985  			s.NoError(err)
   986  			rowsAffected, err := result.RowsAffected()
   987  			s.NoError(err)
   988  			s.Equal(1, int(rowsAffected))
   989  
   990  			visibilities = append(visibilities, visibility)
   991  		}
   992  
   993  		startTime = startTime.Add(time.Second)
   994  	}
   995  
   996  	selectFilter := sqlplugin.VisibilitySelectFilter{
   997  		NamespaceID:      namespaceID.String(),
   998  		WorkflowID:       nil,
   999  		RunID:            convert.StringPtr(""),
  1000  		WorkflowTypeName: nil,
  1001  		MinTime:          timePtr(minStartTime),
  1002  		MaxTime:          timePtr(maxStartTime),
  1003  		Status:           int32(enumspb.WORKFLOW_EXECUTION_STATUS_RUNNING),
  1004  		PageSize:         convert.IntPtr(pageSize),
  1005  	}
  1006  	var rows []sqlplugin.VisibilityRow
  1007  	for {
  1008  		rowsPerPage, err := s.store.SelectFromVisibility(newVisibilityContext(), selectFilter)
  1009  		s.NoError(err)
  1010  		rows = append(rows, rowsPerPage...)
  1011  
  1012  		if len(rowsPerPage) > 0 {
  1013  			lastVisibility := rowsPerPage[len(rowsPerPage)-1]
  1014  			selectFilter.MaxTime = timePtr(lastVisibility.StartTime)
  1015  			selectFilter.RunID = convert.StringPtr(lastVisibility.RunID)
  1016  		} else {
  1017  			break
  1018  		}
  1019  	}
  1020  	s.Len(rows, len(visibilities))
  1021  	s.sortByStartTimeDescRunIDAsc(visibilities)
  1022  	for index := range rows {
  1023  		rows[index].NamespaceID = namespaceID.String()
  1024  	}
  1025  	s.Equal(visibilities, rows)
  1026  }
  1027  
  1028  func (s *visibilitySuite) TestSelect_MinStartTime_MaxStartTime_StatusClose_Single() {
  1029  	for _, status := range testVisibilityCloseStatus {
  1030  		s.testSelectMinStartTimeMaxStartTimeStatusCloseSingle(status)
  1031  	}
  1032  }
  1033  
  1034  func (s *visibilitySuite) testSelectMinStartTimeMaxStartTimeStatusCloseSingle(
  1035  	status enumspb.WorkflowExecutionStatus,
  1036  ) {
  1037  	pageSize := 1
  1038  
  1039  	namespaceID := primitives.NewUUID()
  1040  	runID := primitives.NewUUID()
  1041  	workflowTypeName := shuffle.String(testVisibilityWorkflowTypeName)
  1042  	workflowID := shuffle.String(testVisibilityWorkflowID)
  1043  	startTime := s.now()
  1044  	executionTime := startTime.Add(time.Second)
  1045  	closeTime := executionTime.Add(time.Second)
  1046  	historyLength := rand.Int63()
  1047  
  1048  	visibility := s.newRandomVisibilityRow(
  1049  		namespaceID,
  1050  		runID,
  1051  		workflowTypeName,
  1052  		workflowID,
  1053  		startTime,
  1054  		executionTime,
  1055  		int32(status),
  1056  		timePtr(closeTime),
  1057  		convert.Int64Ptr(historyLength),
  1058  	)
  1059  	result, err := s.store.ReplaceIntoVisibility(newVisibilityContext(), &visibility)
  1060  	s.NoError(err)
  1061  	rowsAffected, err := result.RowsAffected()
  1062  	s.NoError(err)
  1063  	s.Equal(1, int(rowsAffected))
  1064  
  1065  	minStartTime := closeTime
  1066  	maxStartTime := closeTime
  1067  	selectFilter := sqlplugin.VisibilitySelectFilter{
  1068  		NamespaceID:      namespaceID.String(),
  1069  		WorkflowID:       nil,
  1070  		RunID:            convert.StringPtr(""),
  1071  		WorkflowTypeName: nil,
  1072  		MinTime:          timePtr(minStartTime),
  1073  		MaxTime:          timePtr(maxStartTime),
  1074  		Status:           int32(enumspb.WORKFLOW_EXECUTION_STATUS_UNSPECIFIED),
  1075  		PageSize:         convert.IntPtr(pageSize),
  1076  	}
  1077  	rows, err := s.store.SelectFromVisibility(newVisibilityContext(), selectFilter)
  1078  	s.NoError(err)
  1079  	for index := range rows {
  1080  		rows[index].NamespaceID = namespaceID.String()
  1081  	}
  1082  	s.Equal([]sqlplugin.VisibilityRow{visibility}, rows)
  1083  }
  1084  
  1085  func (s *visibilitySuite) TestSelect_MinStartTime_MaxStartTime_StatusClose_Multiple() {
  1086  	numStartTime := 20
  1087  	visibilityPerStartTime := 4
  1088  	pageSize := 5
  1089  
  1090  	var visibilities []sqlplugin.VisibilityRow
  1091  
  1092  	namespaceID := primitives.NewUUID()
  1093  	startTime := s.now()
  1094  	executionTime := startTime.Add(time.Second)
  1095  
  1096  	closeTime := executionTime.Add(time.Second)
  1097  	historyLength := rand.Int63()
  1098  	minStartTime := closeTime
  1099  	maxStartTime := closeTime.Add(time.Duration(numStartTime) * time.Second)
  1100  	for i := 0; i < numStartTime; i++ {
  1101  		for j := 0; j < visibilityPerStartTime; j++ {
  1102  			workflowID := shuffle.String(testVisibilityWorkflowID)
  1103  			runID := primitives.NewUUID()
  1104  			workflowTypeName := shuffle.String(testVisibilityWorkflowTypeName)
  1105  			status := int32(testVisibilityCloseStatus[rand.Intn(len(testVisibilityCloseStatus))])
  1106  			visibility := s.newRandomVisibilityRow(
  1107  				namespaceID,
  1108  				runID,
  1109  				workflowTypeName,
  1110  				workflowID,
  1111  				startTime,
  1112  				executionTime,
  1113  				status,
  1114  				timePtr(closeTime),
  1115  				convert.Int64Ptr(historyLength),
  1116  			)
  1117  			result, err := s.store.ReplaceIntoVisibility(newVisibilityContext(), &visibility)
  1118  			s.NoError(err)
  1119  			rowsAffected, err := result.RowsAffected()
  1120  			s.NoError(err)
  1121  			s.Equal(1, int(rowsAffected))
  1122  
  1123  			visibilities = append(visibilities, visibility)
  1124  		}
  1125  		closeTime = closeTime.Add(time.Second)
  1126  	}
  1127  
  1128  	selectFilter := sqlplugin.VisibilitySelectFilter{
  1129  		NamespaceID:      namespaceID.String(),
  1130  		WorkflowID:       nil,
  1131  		RunID:            convert.StringPtr(""),
  1132  		WorkflowTypeName: nil,
  1133  		MinTime:          timePtr(minStartTime),
  1134  		MaxTime:          timePtr(maxStartTime),
  1135  		Status:           int32(enumspb.WORKFLOW_EXECUTION_STATUS_UNSPECIFIED),
  1136  		PageSize:         convert.IntPtr(pageSize),
  1137  	}
  1138  	var rows []sqlplugin.VisibilityRow
  1139  	for {
  1140  		rowsPerPage, err := s.store.SelectFromVisibility(newVisibilityContext(), selectFilter)
  1141  		s.NoError(err)
  1142  		rows = append(rows, rowsPerPage...)
  1143  
  1144  		if len(rowsPerPage) > 0 {
  1145  			lastVisibility := rowsPerPage[len(rowsPerPage)-1]
  1146  			selectFilter.MaxTime = lastVisibility.CloseTime
  1147  			selectFilter.RunID = convert.StringPtr(lastVisibility.RunID)
  1148  		} else {
  1149  			break
  1150  		}
  1151  	}
  1152  	s.Len(rows, len(visibilities))
  1153  	s.sortByCloseTimeDescRunIDAsc(visibilities)
  1154  	for index := range rows {
  1155  		rows[index].NamespaceID = namespaceID.String()
  1156  	}
  1157  	s.Equal(visibilities, rows)
  1158  }
  1159  
  1160  func (s *visibilitySuite) TestSelect_MinStartTime_MaxStartTime_StatusCloseByType_Single() {
  1161  	for _, status := range testVisibilityCloseStatus {
  1162  		s.testSelectMinStartTimeMaxStartTimeStatusCloseByTypeSingle(status)
  1163  	}
  1164  }
  1165  
  1166  func (s *visibilitySuite) testSelectMinStartTimeMaxStartTimeStatusCloseByTypeSingle(
  1167  	status enumspb.WorkflowExecutionStatus,
  1168  ) {
  1169  	pageSize := 1
  1170  
  1171  	namespaceID := primitives.NewUUID()
  1172  	runID := primitives.NewUUID()
  1173  	workflowTypeName := shuffle.String(testVisibilityWorkflowTypeName)
  1174  	workflowID := shuffle.String(testVisibilityWorkflowID)
  1175  	startTime := s.now()
  1176  	executionTime := startTime.Add(time.Second)
  1177  	closeTime := executionTime.Add(time.Second)
  1178  	historyLength := rand.Int63()
  1179  
  1180  	visibility := s.newRandomVisibilityRow(
  1181  		namespaceID,
  1182  		runID,
  1183  		workflowTypeName,
  1184  		workflowID,
  1185  		startTime,
  1186  		executionTime,
  1187  		int32(status),
  1188  		timePtr(closeTime),
  1189  		convert.Int64Ptr(historyLength),
  1190  	)
  1191  	result, err := s.store.ReplaceIntoVisibility(newVisibilityContext(), &visibility)
  1192  	s.NoError(err)
  1193  	rowsAffected, err := result.RowsAffected()
  1194  	s.NoError(err)
  1195  	s.Equal(1, int(rowsAffected))
  1196  
  1197  	minStartTime := closeTime
  1198  	maxStartTime := closeTime
  1199  	selectFilter := sqlplugin.VisibilitySelectFilter{
  1200  		NamespaceID:      namespaceID.String(),
  1201  		WorkflowID:       nil,
  1202  		RunID:            convert.StringPtr(""),
  1203  		WorkflowTypeName: convert.StringPtr(workflowTypeName),
  1204  		MinTime:          timePtr(minStartTime),
  1205  		MaxTime:          timePtr(maxStartTime),
  1206  		Status:           int32(status),
  1207  		PageSize:         convert.IntPtr(pageSize),
  1208  	}
  1209  	rows, err := s.store.SelectFromVisibility(newVisibilityContext(), selectFilter)
  1210  	s.NoError(err)
  1211  	for index := range rows {
  1212  		rows[index].NamespaceID = namespaceID.String()
  1213  	}
  1214  	s.Equal([]sqlplugin.VisibilityRow{visibility}, rows)
  1215  }
  1216  
  1217  func (s *visibilitySuite) TestSelect_MinStartTime_MaxStartTime_StatusCloseByType_Multiple() {
  1218  	for _, status := range testVisibilityCloseStatus {
  1219  		s.testSelectMinStartTimeMaxStartTimeStatusCloseByTypeMultiple(status)
  1220  	}
  1221  }
  1222  
  1223  func (s *visibilitySuite) testSelectMinStartTimeMaxStartTimeStatusCloseByTypeMultiple(
  1224  	status enumspb.WorkflowExecutionStatus,
  1225  ) {
  1226  	numStartTime := 20
  1227  	visibilityPerStartTime := 4
  1228  	pageSize := 5
  1229  
  1230  	var visibilities []sqlplugin.VisibilityRow
  1231  
  1232  	namespaceID := primitives.NewUUID()
  1233  	startTime := s.now()
  1234  	executionTime := startTime.Add(time.Second)
  1235  	closeTime := executionTime.Add(time.Second)
  1236  	historyLength := rand.Int63()
  1237  	minStartTime := closeTime
  1238  	maxStartTime := closeTime.Add(time.Duration(numStartTime) * time.Second)
  1239  	for i := 0; i < numStartTime; i++ {
  1240  		for j := 0; j < visibilityPerStartTime; j++ {
  1241  			workflowID := shuffle.String(testVisibilityWorkflowID)
  1242  			runID := primitives.NewUUID()
  1243  			workflowTypeName := shuffle.String(testVisibilityWorkflowTypeName)
  1244  			visibility := s.newRandomVisibilityRow(
  1245  				namespaceID,
  1246  				runID,
  1247  				workflowTypeName,
  1248  				workflowID,
  1249  				startTime,
  1250  				executionTime,
  1251  				int32(status),
  1252  				timePtr(closeTime),
  1253  				convert.Int64Ptr(historyLength),
  1254  			)
  1255  			result, err := s.store.ReplaceIntoVisibility(newVisibilityContext(), &visibility)
  1256  			s.NoError(err)
  1257  			rowsAffected, err := result.RowsAffected()
  1258  			s.NoError(err)
  1259  			s.Equal(1, int(rowsAffected))
  1260  
  1261  			visibilities = append(visibilities, visibility)
  1262  		}
  1263  		closeTime = closeTime.Add(time.Second)
  1264  	}
  1265  
  1266  	selectFilter := sqlplugin.VisibilitySelectFilter{
  1267  		NamespaceID:      namespaceID.String(),
  1268  		WorkflowID:       nil,
  1269  		RunID:            convert.StringPtr(""),
  1270  		WorkflowTypeName: nil,
  1271  		MinTime:          timePtr(minStartTime),
  1272  		MaxTime:          timePtr(maxStartTime),
  1273  		Status:           int32(status),
  1274  		PageSize:         convert.IntPtr(pageSize),
  1275  	}
  1276  	var rows []sqlplugin.VisibilityRow
  1277  	for {
  1278  		rowsPerPage, err := s.store.SelectFromVisibility(newVisibilityContext(), selectFilter)
  1279  		s.NoError(err)
  1280  		rows = append(rows, rowsPerPage...)
  1281  
  1282  		if len(rowsPerPage) > 0 {
  1283  			lastVisibility := rowsPerPage[len(rowsPerPage)-1]
  1284  			selectFilter.MaxTime = lastVisibility.CloseTime
  1285  			selectFilter.RunID = convert.StringPtr(lastVisibility.RunID)
  1286  		} else {
  1287  			break
  1288  		}
  1289  	}
  1290  
  1291  	s.Len(rows, len(visibilities))
  1292  	s.sortByCloseTimeDescRunIDAsc(visibilities)
  1293  	for index := range rows {
  1294  		rows[index].NamespaceID = namespaceID.String()
  1295  	}
  1296  	s.Equal(visibilities, rows)
  1297  }
  1298  
  1299  func (s *visibilitySuite) sortByStartTimeDescRunIDAsc(
  1300  	visibilities []sqlplugin.VisibilityRow,
  1301  ) {
  1302  	sort.Slice(visibilities, func(i, j int) bool {
  1303  		this := visibilities[i]
  1304  		that := visibilities[j]
  1305  
  1306  		// start time desc, run ID asc
  1307  
  1308  		if this.StartTime.Before(that.StartTime) {
  1309  			return false
  1310  		} else if that.StartTime.Before(this.StartTime) {
  1311  			return true
  1312  		}
  1313  
  1314  		if this.RunID < that.RunID {
  1315  			return true
  1316  		} else if this.RunID > that.RunID {
  1317  			return false
  1318  		}
  1319  
  1320  		// same
  1321  		return true
  1322  	})
  1323  }
  1324  
  1325  func (s *visibilitySuite) sortByCloseTimeDescRunIDAsc(
  1326  	visibilities []sqlplugin.VisibilityRow,
  1327  ) {
  1328  	sort.Slice(visibilities, func(i, j int) bool {
  1329  		this := visibilities[i]
  1330  		that := visibilities[j]
  1331  
  1332  		// close time desc, run ID asc
  1333  
  1334  		if this.CloseTime.Before(*that.CloseTime) {
  1335  			return false
  1336  		} else if that.CloseTime.Before(*this.CloseTime) {
  1337  			return true
  1338  		}
  1339  
  1340  		if this.RunID < that.RunID {
  1341  			return true
  1342  		} else if this.RunID > that.RunID {
  1343  			return false
  1344  		}
  1345  
  1346  		// same
  1347  		return true
  1348  	})
  1349  }
  1350  
  1351  func (s *visibilitySuite) now() time.Time {
  1352  	return time.Now().UTC().Truncate(time.Millisecond)
  1353  }
  1354  
  1355  func (s *visibilitySuite) newRandomVisibilityRow(
  1356  	namespaceID primitives.UUID,
  1357  	runID primitives.UUID,
  1358  	workflowTypeName string,
  1359  	workflowID string,
  1360  	startTime time.Time,
  1361  	executionTime time.Time,
  1362  	status int32,
  1363  	closeTime *time.Time,
  1364  	historyLength *int64,
  1365  ) sqlplugin.VisibilityRow {
  1366  	return sqlplugin.VisibilityRow{
  1367  		NamespaceID:      namespaceID.String(),
  1368  		RunID:            runID.String(),
  1369  		WorkflowTypeName: workflowTypeName,
  1370  		WorkflowID:       workflowID,
  1371  		StartTime:        startTime,
  1372  		ExecutionTime:    executionTime,
  1373  		Status:           status,
  1374  		CloseTime:        closeTime,
  1375  		HistoryLength:    historyLength,
  1376  		Memo:             shuffle.Bytes(testVisibilityData),
  1377  		Encoding:         testVisibilityEncoding,
  1378  	}
  1379  }
  1380  
  1381  func timePtr(t time.Time) *time.Time {
  1382  	return &t
  1383  }