go.temporal.io/server@v1.23.0/common/persistence/sql/sqlplugin/tests/namespace.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  	"database/sql"
    29  	"testing"
    30  
    31  	"github.com/stretchr/testify/require"
    32  	"github.com/stretchr/testify/suite"
    33  
    34  	"go.temporal.io/server/common/convert"
    35  	"go.temporal.io/server/common/persistence/sql/sqlplugin"
    36  	"go.temporal.io/server/common/primitives"
    37  	"go.temporal.io/server/common/shuffle"
    38  )
    39  
    40  const (
    41  	testNamespaceEncoding = "random encoding"
    42  )
    43  
    44  var (
    45  	testNamespaceName = "random namespace"
    46  	testNamespaceData = []byte("random namespace data")
    47  )
    48  
    49  type (
    50  	namespaceSuite struct {
    51  		suite.Suite
    52  		*require.Assertions
    53  
    54  		store sqlplugin.Namespace
    55  	}
    56  )
    57  
    58  func NewNamespaceSuite(
    59  	t *testing.T,
    60  	store sqlplugin.Namespace,
    61  ) *namespaceSuite {
    62  	return &namespaceSuite{
    63  		Assertions: require.New(t),
    64  		store:      store,
    65  	}
    66  }
    67  
    68  func (s *namespaceSuite) SetupSuite() {
    69  
    70  }
    71  
    72  func (s *namespaceSuite) TearDownSuite() {
    73  
    74  }
    75  
    76  func (s *namespaceSuite) SetupTest() {
    77  	s.Assertions = require.New(s.T())
    78  }
    79  
    80  func (s *namespaceSuite) TearDownTest() {
    81  
    82  }
    83  
    84  func (s *namespaceSuite) TestInsert_Success() {
    85  	id := primitives.NewUUID()
    86  	name := shuffle.String(testNamespaceName)
    87  	notificationVersion := int64(1)
    88  
    89  	namespace := s.newRandomNamespaceRow(id, name, notificationVersion)
    90  	result, err := s.store.InsertIntoNamespace(newExecutionContext(), &namespace)
    91  	s.NoError(err)
    92  	rowsAffected, err := result.RowsAffected()
    93  	s.NoError(err)
    94  	s.Equal(1, int(rowsAffected))
    95  }
    96  
    97  func (s *namespaceSuite) TestInsert_Fail_Duplicate() {
    98  	id := primitives.NewUUID()
    99  	name := shuffle.String(testNamespaceName)
   100  	notificationVersion := int64(1)
   101  
   102  	namespace := s.newRandomNamespaceRow(id, name, notificationVersion)
   103  	result, err := s.store.InsertIntoNamespace(newExecutionContext(), &namespace)
   104  	s.NoError(err)
   105  	rowsAffected, err := result.RowsAffected()
   106  	s.NoError(err)
   107  	s.Equal(1, int(rowsAffected))
   108  
   109  	namespace = s.newRandomNamespaceRow(id, name, notificationVersion)
   110  	_, err = s.store.InsertIntoNamespace(newExecutionContext(), &namespace)
   111  	s.Error(err) // TODO persistence layer should do proper error translation
   112  
   113  	namespace = s.newRandomNamespaceRow(id, shuffle.String(testNamespaceName), notificationVersion)
   114  	_, err = s.store.InsertIntoNamespace(newExecutionContext(), &namespace)
   115  	s.Error(err) // TODO persistence layer should do proper error translation
   116  
   117  	namespace = s.newRandomNamespaceRow(primitives.NewUUID(), name, notificationVersion)
   118  	_, err = s.store.InsertIntoNamespace(newExecutionContext(), &namespace)
   119  	s.Error(err) // TODO persistence layer should do proper error translation
   120  }
   121  
   122  func (s *namespaceSuite) TestInsertSelect() {
   123  	id := primitives.NewUUID()
   124  	name := shuffle.String(testNamespaceName)
   125  	notificationVersion := int64(1)
   126  
   127  	namespace := s.newRandomNamespaceRow(id, name, notificationVersion)
   128  	result, err := s.store.InsertIntoNamespace(newExecutionContext(), &namespace)
   129  	s.NoError(err)
   130  	rowsAffected, err := result.RowsAffected()
   131  	s.NoError(err)
   132  	s.Equal(1, int(rowsAffected))
   133  
   134  	filter := sqlplugin.NamespaceFilter{
   135  		ID: &id,
   136  	}
   137  	rows, err := s.store.SelectFromNamespace(newExecutionContext(), filter)
   138  	s.NoError(err)
   139  	s.Equal([]sqlplugin.NamespaceRow{namespace}, rows)
   140  
   141  	filter = sqlplugin.NamespaceFilter{
   142  		Name: &name,
   143  	}
   144  	rows, err = s.store.SelectFromNamespace(newExecutionContext(), filter)
   145  	s.NoError(err)
   146  	s.Equal([]sqlplugin.NamespaceRow{namespace}, rows)
   147  }
   148  
   149  func (s *namespaceSuite) TestInsertUpdate_Success() {
   150  	id := primitives.NewUUID()
   151  	name := shuffle.String(testNamespaceName)
   152  	notificationVersion := int64(1)
   153  
   154  	namespace := s.newRandomNamespaceRow(id, name, notificationVersion)
   155  	result, err := s.store.InsertIntoNamespace(newExecutionContext(), &namespace)
   156  	s.NoError(err)
   157  	rowsAffected, err := result.RowsAffected()
   158  	s.NoError(err)
   159  	s.Equal(1, int(rowsAffected))
   160  
   161  	namespace = s.newRandomNamespaceRow(id, name, notificationVersion)
   162  	result, err = s.store.UpdateNamespace(newExecutionContext(), &namespace)
   163  	s.NoError(err)
   164  	rowsAffected, err = result.RowsAffected()
   165  	s.NoError(err)
   166  	s.Equal(1, int(rowsAffected))
   167  }
   168  
   169  func (s *namespaceSuite) TestUpdate_Fail() {
   170  	id := primitives.NewUUID()
   171  	name := shuffle.String(testNamespaceName)
   172  	notificationVersion := int64(1)
   173  
   174  	namespace := s.newRandomNamespaceRow(id, name, notificationVersion)
   175  	result, err := s.store.UpdateNamespace(newExecutionContext(), &namespace)
   176  	s.NoError(err)
   177  	rowsAffected, err := result.RowsAffected()
   178  	s.NoError(err)
   179  	s.Equal(0, int(rowsAffected))
   180  }
   181  
   182  func (s *namespaceSuite) TestInsertUpdateSelect() {
   183  	id := primitives.NewUUID()
   184  	name := shuffle.String(testNamespaceName)
   185  	notificationVersion := int64(1)
   186  
   187  	namespace := s.newRandomNamespaceRow(id, name, notificationVersion)
   188  	result, err := s.store.InsertIntoNamespace(newExecutionContext(), &namespace)
   189  	s.NoError(err)
   190  	rowsAffected, err := result.RowsAffected()
   191  	s.NoError(err)
   192  	s.Equal(1, int(rowsAffected))
   193  
   194  	namespace = s.newRandomNamespaceRow(id, name, notificationVersion)
   195  	result, err = s.store.UpdateNamespace(newExecutionContext(), &namespace)
   196  	s.NoError(err)
   197  	rowsAffected, err = result.RowsAffected()
   198  	s.NoError(err)
   199  	s.Equal(1, int(rowsAffected))
   200  
   201  	filter := sqlplugin.NamespaceFilter{
   202  		ID: &id,
   203  	}
   204  	rows, err := s.store.SelectFromNamespace(newExecutionContext(), filter)
   205  	s.NoError(err)
   206  	s.Equal([]sqlplugin.NamespaceRow{namespace}, rows)
   207  
   208  	filter = sqlplugin.NamespaceFilter{
   209  		Name: &name,
   210  	}
   211  	rows, err = s.store.SelectFromNamespace(newExecutionContext(), filter)
   212  	s.NoError(err)
   213  	s.Equal([]sqlplugin.NamespaceRow{namespace}, rows)
   214  }
   215  
   216  func (s *namespaceSuite) TestInsertDeleteSelect_ID() {
   217  	id := primitives.NewUUID()
   218  	name := shuffle.String(testNamespaceName)
   219  	notificationVersion := int64(1)
   220  
   221  	namespace := s.newRandomNamespaceRow(id, name, notificationVersion)
   222  	result, err := s.store.InsertIntoNamespace(newExecutionContext(), &namespace)
   223  	s.NoError(err)
   224  	rowsAffected, err := result.RowsAffected()
   225  	s.NoError(err)
   226  	s.Equal(1, int(rowsAffected))
   227  
   228  	filter := sqlplugin.NamespaceFilter{
   229  		ID: &id,
   230  	}
   231  	result, err = s.store.DeleteFromNamespace(newExecutionContext(), filter)
   232  	s.NoError(err)
   233  	rowsAffected, err = result.RowsAffected()
   234  	s.NoError(err)
   235  	s.Equal(1, int(rowsAffected))
   236  
   237  	filter = sqlplugin.NamespaceFilter{
   238  		ID: &id,
   239  	}
   240  	_, err = s.store.SelectFromNamespace(newExecutionContext(), filter)
   241  	s.Error(err) // TODO persistence layer should do proper error translation
   242  
   243  	filter = sqlplugin.NamespaceFilter{
   244  		Name: &name,
   245  	}
   246  	_, err = s.store.SelectFromNamespace(newExecutionContext(), filter)
   247  	s.Error(err) // TODO persistence layer should do proper error translation
   248  }
   249  
   250  func (s *namespaceSuite) TestInsertDeleteSelect_Name() {
   251  	id := primitives.NewUUID()
   252  	name := shuffle.String(testNamespaceName)
   253  	notificationVersion := int64(1)
   254  
   255  	namespace := s.newRandomNamespaceRow(id, name, notificationVersion)
   256  	result, err := s.store.InsertIntoNamespace(newExecutionContext(), &namespace)
   257  	s.NoError(err)
   258  	rowsAffected, err := result.RowsAffected()
   259  	s.NoError(err)
   260  	s.Equal(1, int(rowsAffected))
   261  
   262  	filter := sqlplugin.NamespaceFilter{
   263  		Name: &name,
   264  	}
   265  	result, err = s.store.DeleteFromNamespace(newExecutionContext(), filter)
   266  	s.NoError(err)
   267  	rowsAffected, err = result.RowsAffected()
   268  	s.NoError(err)
   269  	s.Equal(1, int(rowsAffected))
   270  
   271  	filter = sqlplugin.NamespaceFilter{
   272  		ID: &id,
   273  	}
   274  	_, err = s.store.SelectFromNamespace(newExecutionContext(), filter)
   275  	s.Error(err) // TODO persistence layer should do proper error translation
   276  
   277  	filter = sqlplugin.NamespaceFilter{
   278  		Name: &name,
   279  	}
   280  	_, err = s.store.SelectFromNamespace(newExecutionContext(), filter)
   281  	s.Error(err) // TODO persistence layer should do proper error translation
   282  }
   283  
   284  func (s *namespaceSuite) TestInsertSelect_Pagination() {
   285  	// cleanup the namespace for pagination test
   286  	rowsPerPage, err := s.store.SelectFromNamespace(newExecutionContext(), sqlplugin.NamespaceFilter{
   287  		GreaterThanID: nil,
   288  		PageSize:      convert.IntPtr(1000000),
   289  	})
   290  	switch err {
   291  	case nil:
   292  		for _, row := range rowsPerPage {
   293  			_, err := s.store.DeleteFromNamespace(newExecutionContext(), sqlplugin.NamespaceFilter{
   294  				ID: &row.ID,
   295  			})
   296  			s.NoError(err)
   297  		}
   298  	case sql.ErrNoRows:
   299  		// noop
   300  	default:
   301  		s.NoError(err)
   302  	}
   303  
   304  	namespaces := map[string]*sqlplugin.NamespaceRow{}
   305  	numNamespace := 2
   306  	numNamespacePerPage := 1
   307  
   308  	for i := 0; i < numNamespace; i++ {
   309  		id := primitives.NewUUID()
   310  		name := shuffle.String(testNamespaceName)
   311  		notificationVersion := int64(1)
   312  
   313  		namespace := s.newRandomNamespaceRow(id, name, notificationVersion)
   314  		result, err := s.store.InsertIntoNamespace(newExecutionContext(), &namespace)
   315  		s.NoError(err)
   316  		rowsAffected, err := result.RowsAffected()
   317  		s.NoError(err)
   318  		s.Equal(1, int(rowsAffected))
   319  
   320  		namespaces[namespace.ID.String()] = &namespace
   321  	}
   322  
   323  	rows := map[string]*sqlplugin.NamespaceRow{}
   324  	filter := sqlplugin.NamespaceFilter{
   325  		GreaterThanID: nil,
   326  		PageSize:      convert.IntPtr(numNamespacePerPage),
   327  	}
   328  	for doContinue := true; doContinue; doContinue = filter.GreaterThanID != nil {
   329  		rowsPerPage, err := s.store.SelectFromNamespace(newExecutionContext(), filter)
   330  		switch err {
   331  		case nil:
   332  			for _, row := range rowsPerPage {
   333  				rows[row.ID.String()] = &row
   334  			}
   335  			length := len(rowsPerPage)
   336  			if length == 0 {
   337  				filter.GreaterThanID = nil
   338  			} else {
   339  				filter.GreaterThanID = &rowsPerPage[len(rowsPerPage)-1].ID
   340  			}
   341  
   342  		case sql.ErrNoRows:
   343  			filter.GreaterThanID = nil
   344  
   345  		default:
   346  			s.NoError(err)
   347  		}
   348  	}
   349  	s.Equal(namespaces, rows)
   350  }
   351  
   352  func (s *namespaceSuite) TestSelectLockMetadata() {
   353  	row, err := s.store.SelectFromNamespaceMetadata(newExecutionContext())
   354  	s.NoError(err)
   355  
   356  	// NOTE: lock without transaction is equivalent to select
   357  	//  this test only test the select functionality
   358  	metadata, err := s.store.LockNamespaceMetadata(newExecutionContext())
   359  	s.NoError(err)
   360  	s.Equal(row, metadata)
   361  }
   362  
   363  func (s *namespaceSuite) TestSelectUpdateSelectMetadata_Success() {
   364  	row, err := s.store.SelectFromNamespaceMetadata(newExecutionContext())
   365  	s.NoError(err)
   366  	originalVersion := row.NotificationVersion
   367  
   368  	result, err := s.store.UpdateNamespaceMetadata(newExecutionContext(), row)
   369  	s.NoError(err)
   370  	rowsAffected, err := result.RowsAffected()
   371  	s.NoError(err)
   372  	s.Equal(1, int(rowsAffected))
   373  
   374  	row, err = s.store.SelectFromNamespaceMetadata(newExecutionContext())
   375  	s.NoError(err)
   376  	s.Equal(originalVersion+1, row.NotificationVersion)
   377  }
   378  
   379  func (s *namespaceSuite) TestSelectUpdateSelectMetadata_Fail() {
   380  	row, err := s.store.SelectFromNamespaceMetadata(newExecutionContext())
   381  	s.NoError(err)
   382  	originalVersion := row.NotificationVersion
   383  
   384  	namespaceMetadata := s.newRandomNamespaceMetadataRow(row.NotificationVersion + 1000)
   385  	result, err := s.store.UpdateNamespaceMetadata(newExecutionContext(), &namespaceMetadata)
   386  	s.NoError(err) // TODO persistence layer should do proper error translation
   387  	rowsAffected, err := result.RowsAffected()
   388  	s.NoError(err)
   389  	s.Equal(0, int(rowsAffected))
   390  
   391  	row, err = s.store.SelectFromNamespaceMetadata(newExecutionContext())
   392  	s.NoError(err)
   393  	s.Equal(originalVersion, row.NotificationVersion)
   394  }
   395  
   396  func (s *namespaceSuite) newRandomNamespaceRow(
   397  	id primitives.UUID,
   398  	name string,
   399  	notificationVersion int64,
   400  ) sqlplugin.NamespaceRow {
   401  	return sqlplugin.NamespaceRow{
   402  		ID:                  id,
   403  		Name:                name,
   404  		Data:                shuffle.Bytes(testNamespaceData),
   405  		DataEncoding:        testNamespaceEncoding,
   406  		IsGlobal:            true,
   407  		NotificationVersion: notificationVersion,
   408  	}
   409  }
   410  
   411  func (s *namespaceSuite) newRandomNamespaceMetadataRow(
   412  	notificationVersion int64,
   413  ) sqlplugin.NamespaceMetadataRow {
   414  	return sqlplugin.NamespaceMetadataRow{
   415  		NotificationVersion: notificationVersion,
   416  	}
   417  }