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 }