github.com/haalcala/mattermost-server-change-repo@v0.0.0-20210713015153-16753fbeee5f/store/storetest/plugin_store.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package storetest 5 6 import ( 7 "sort" 8 "testing" 9 10 "github.com/stretchr/testify/assert" 11 "github.com/stretchr/testify/require" 12 13 "github.com/mattermost/mattermost-server/v5/model" 14 "github.com/mattermost/mattermost-server/v5/store" 15 ) 16 17 func TestPluginStore(t *testing.T, ss store.Store, s SqlStore) { 18 t.Run("SaveOrUpdate", func(t *testing.T) { testPluginSaveOrUpdate(t, ss) }) 19 t.Run("CompareAndSet", func(t *testing.T) { testPluginCompareAndSet(t, ss) }) 20 t.Run("CompareAndDelete", func(t *testing.T) { testPluginCompareAndDelete(t, ss) }) 21 t.Run("SetWithOptions", func(t *testing.T) { testPluginSetWithOptions(t, ss) }) 22 t.Run("Get", func(t *testing.T) { testPluginGet(t, ss) }) 23 t.Run("Delete", func(t *testing.T) { testPluginDelete(t, ss) }) 24 t.Run("DeleteAllForPlugin", func(t *testing.T) { testPluginDeleteAllForPlugin(t, ss) }) 25 t.Run("DeleteAllExpired", func(t *testing.T) { testPluginDeleteAllExpired(t, ss) }) 26 t.Run("List", func(t *testing.T) { testPluginList(t, ss) }) 27 } 28 29 func setupKVs(t *testing.T, ss store.Store) (string, func()) { 30 pluginId := model.NewId() 31 otherPluginId := model.NewId() 32 33 // otherKV is another key value for the current plugin, and used to verify other keys 34 // aren't modified unintentionally. 35 otherKV := &model.PluginKeyValue{ 36 PluginId: pluginId, 37 Key: model.NewId(), 38 Value: []byte(model.NewId()), 39 ExpireAt: 0, 40 } 41 _, err := ss.Plugin().SaveOrUpdate(otherKV) 42 require.NoError(t, err) 43 44 // otherPluginKV is a key value for another plugin, and used to verify other plugins' keys 45 // aren't modified unintentionally. 46 otherPluginKV := &model.PluginKeyValue{ 47 PluginId: otherPluginId, 48 Key: model.NewId(), 49 Value: []byte(model.NewId()), 50 ExpireAt: 0, 51 } 52 _, err = ss.Plugin().SaveOrUpdate(otherPluginKV) 53 require.NoError(t, err) 54 55 return pluginId, func() { 56 actualOtherKV, err := ss.Plugin().Get(otherKV.PluginId, otherKV.Key) 57 require.NoError(t, err, "failed to find other key value for same plugin") 58 assert.Equal(t, otherKV, actualOtherKV) 59 60 actualOtherPluginKV, err := ss.Plugin().Get(otherPluginKV.PluginId, otherPluginKV.Key) 61 require.NoError(t, err, "failed to find other key value from different plugin") 62 assert.Equal(t, otherPluginKV, actualOtherPluginKV) 63 } 64 } 65 66 func doTestPluginSaveOrUpdate(t *testing.T, ss store.Store, doer func(kv *model.PluginKeyValue) (*model.PluginKeyValue, error)) { 67 t.Run("invalid kv", func(t *testing.T) { 68 _, tearDown := setupKVs(t, ss) 69 defer tearDown() 70 71 kv := &model.PluginKeyValue{ 72 PluginId: "", 73 Key: model.NewId(), 74 Value: []byte(model.NewId()), 75 ExpireAt: 0, 76 } 77 78 kv, err := doer(kv) 79 require.Error(t, err) 80 appErr, ok := err.(*model.AppError) 81 require.True(t, ok) 82 require.Equal(t, "model.plugin_key_value.is_valid.plugin_id.app_error", appErr.Id) 83 assert.Nil(t, kv) 84 }) 85 86 t.Run("new key", func(t *testing.T) { 87 pluginId, tearDown := setupKVs(t, ss) 88 defer tearDown() 89 90 key := model.NewId() 91 value := model.NewId() 92 expireAt := int64(0) 93 94 kv := &model.PluginKeyValue{ 95 PluginId: pluginId, 96 Key: key, 97 Value: []byte(value), 98 ExpireAt: expireAt, 99 } 100 101 retKV, err := doer(kv) 102 require.NoError(t, err) 103 assert.Equal(t, kv, retKV) 104 // SaveOrUpdate returns the kv passed in, so test each field individually for 105 // completeness. It should probably be changed to not bother doing that. 106 assert.Equal(t, pluginId, kv.PluginId) 107 assert.Equal(t, key, kv.Key) 108 assert.Equal(t, []byte(value), kv.Value) 109 assert.Equal(t, expireAt, kv.ExpireAt) 110 111 actualKV, nErr := ss.Plugin().Get(pluginId, key) 112 require.NoError(t, nErr) 113 assert.Equal(t, kv, actualKV) 114 }) 115 116 t.Run("nil value for new key", func(t *testing.T) { 117 pluginId, tearDown := setupKVs(t, ss) 118 defer tearDown() 119 120 key := model.NewId() 121 var value []byte 122 expireAt := int64(0) 123 124 kv := &model.PluginKeyValue{ 125 PluginId: pluginId, 126 Key: key, 127 Value: value, 128 ExpireAt: expireAt, 129 } 130 131 retKV, err := doer(kv) 132 require.NoError(t, err) 133 assert.Equal(t, kv, retKV) 134 // SaveOrUpdate returns the kv passed in, so test each field individually for 135 // completeness. It should probably be changed to not bother doing that. 136 assert.Equal(t, pluginId, kv.PluginId) 137 assert.Equal(t, key, kv.Key) 138 assert.Nil(t, kv.Value) 139 assert.Equal(t, expireAt, kv.ExpireAt) 140 141 actualKV, nErr := ss.Plugin().Get(pluginId, key) 142 _, ok := nErr.(*store.ErrNotFound) 143 require.Error(t, nErr) 144 assert.True(t, ok) 145 assert.Nil(t, actualKV) 146 }) 147 148 t.Run("existing key", func(t *testing.T) { 149 pluginId, tearDown := setupKVs(t, ss) 150 defer tearDown() 151 152 key := model.NewId() 153 value := model.NewId() 154 expireAt := int64(0) 155 156 kv := &model.PluginKeyValue{ 157 PluginId: pluginId, 158 Key: key, 159 Value: []byte(value), 160 ExpireAt: expireAt, 161 } 162 163 _, err := doer(kv) 164 require.NoError(t, err) 165 166 newValue := model.NewId() 167 kv.Value = []byte(newValue) 168 169 retKV, err := doer(kv) 170 require.NoError(t, err) 171 assert.Equal(t, kv, retKV) 172 // SaveOrUpdate returns the kv passed in, so test each field individually for 173 // completeness. It should probably be changed to not bother doing that. 174 assert.Equal(t, pluginId, kv.PluginId) 175 assert.Equal(t, key, kv.Key) 176 assert.Equal(t, []byte(newValue), kv.Value) 177 assert.Equal(t, expireAt, kv.ExpireAt) 178 179 actualKV, nErr := ss.Plugin().Get(pluginId, key) 180 require.NoError(t, nErr) 181 assert.Equal(t, kv, actualKV) 182 }) 183 184 t.Run("nil value for existing key", func(t *testing.T) { 185 pluginId, tearDown := setupKVs(t, ss) 186 defer tearDown() 187 188 key := model.NewId() 189 value := model.NewId() 190 expireAt := int64(0) 191 192 kv := &model.PluginKeyValue{ 193 PluginId: pluginId, 194 Key: key, 195 Value: []byte(value), 196 ExpireAt: expireAt, 197 } 198 199 _, err := doer(kv) 200 require.NoError(t, err) 201 202 kv.Value = nil 203 retKV, err := doer(kv) 204 205 require.NoError(t, err) 206 assert.Equal(t, kv, retKV) 207 // SaveOrUpdate returns the kv passed in, so test each field individually for 208 // completeness. It should probably be changed to not bother doing that. 209 assert.Equal(t, pluginId, kv.PluginId) 210 assert.Equal(t, key, kv.Key) 211 assert.Nil(t, kv.Value) 212 assert.Equal(t, expireAt, kv.ExpireAt) 213 214 actualKV, nErr := ss.Plugin().Get(pluginId, key) 215 _, ok := nErr.(*store.ErrNotFound) 216 require.Error(t, nErr) 217 assert.True(t, ok) 218 assert.Nil(t, actualKV) 219 }) 220 } 221 222 func testPluginSaveOrUpdate(t *testing.T, ss store.Store) { 223 doTestPluginSaveOrUpdate(t, ss, func(kv *model.PluginKeyValue) (*model.PluginKeyValue, error) { 224 return ss.Plugin().SaveOrUpdate(kv) 225 }) 226 } 227 228 // doTestPluginCompareAndSet exercises the CompareAndSet functionality, but abstracts the actual 229 // call to same to allow reuse with SetWithOptions 230 func doTestPluginCompareAndSet(t *testing.T, ss store.Store, compareAndSet func(kv *model.PluginKeyValue, oldValue []byte) (bool, error)) { 231 t.Run("invalid kv", func(t *testing.T) { 232 _, tearDown := setupKVs(t, ss) 233 defer tearDown() 234 235 kv := &model.PluginKeyValue{ 236 PluginId: "", 237 Key: model.NewId(), 238 Value: []byte(model.NewId()), 239 ExpireAt: 0, 240 } 241 242 ok, err := compareAndSet(kv, nil) 243 require.Error(t, err) 244 assert.False(t, ok) 245 appErr, ok := err.(*model.AppError) 246 require.True(t, ok) 247 assert.Equal(t, "model.plugin_key_value.is_valid.plugin_id.app_error", appErr.Id) 248 }) 249 250 // assertChanged verifies that CompareAndSet successfully changes to the given value. 251 assertChanged := func(t *testing.T, kv *model.PluginKeyValue, oldValue []byte) { 252 t.Helper() 253 254 ok, err := compareAndSet(kv, oldValue) 255 require.NoError(t, err) 256 require.True(t, ok, "should have succeeded to CompareAndSet") 257 258 actualKV, nErr := ss.Plugin().Get(kv.PluginId, kv.Key) 259 require.NoError(t, nErr) 260 261 // When tested with KVSetWithOptions, a strict comparison can fail because that 262 // function accepts a relative time and makes its own call to model.GetMillis(), 263 // leading to off-by-one issues. All these tests are written with 15+ second 264 // differences, so allow for an off-by-1000ms in either direction. 265 require.NotNil(t, actualKV) 266 267 expiryDelta := actualKV.ExpireAt - kv.ExpireAt 268 if expiryDelta > -1000 && expiryDelta < 1000 { 269 actualKV.ExpireAt = kv.ExpireAt 270 } 271 272 assert.Equal(t, kv, actualKV) 273 } 274 275 // assertUnchanged verifies that CompareAndSet fails, leaving the existing value. 276 assertUnchanged := func(t *testing.T, kv, existingKV *model.PluginKeyValue, oldValue []byte) { 277 t.Helper() 278 279 ok, err := compareAndSet(kv, oldValue) 280 require.NoError(t, err) 281 require.False(t, ok, "should have failed to CompareAndSet") 282 283 actualKV, nErr := ss.Plugin().Get(kv.PluginId, kv.Key) 284 if existingKV == nil { 285 require.Error(t, nErr) 286 _, ok := nErr.(*store.ErrNotFound) 287 assert.True(t, ok) 288 assert.Nil(t, actualKV) 289 } else { 290 require.NoError(t, nErr) 291 assert.Equal(t, existingKV, actualKV) 292 } 293 } 294 295 // assertRemoved verifies that CompareAndSet successfully removes the given value. 296 assertRemoved := func(t *testing.T, kv *model.PluginKeyValue, oldValue []byte) { 297 t.Helper() 298 299 ok, err := compareAndSet(kv, oldValue) 300 require.NoError(t, err) 301 require.True(t, ok, "should have succeeded to CompareAndSet") 302 303 actualKV, nErr := ss.Plugin().Get(kv.PluginId, kv.Key) 304 _, ok = nErr.(*store.ErrNotFound) 305 require.Error(t, nErr) 306 assert.True(t, ok) 307 assert.Nil(t, actualKV) 308 } 309 310 // Non-existent keys and expired keys should behave identically. 311 for description, setup := range map[string]func(t *testing.T) (*model.PluginKeyValue, func()){ 312 "non-existent key": func(t *testing.T) (*model.PluginKeyValue, func()) { 313 pluginId, tearDown := setupKVs(t, ss) 314 315 kv := &model.PluginKeyValue{ 316 PluginId: pluginId, 317 Key: model.NewId(), 318 Value: []byte(model.NewId()), 319 ExpireAt: 0, 320 } 321 322 return kv, tearDown 323 }, 324 "expired key": func(t *testing.T) (*model.PluginKeyValue, func()) { 325 pluginId, tearDown := setupKVs(t, ss) 326 327 expiredKV := &model.PluginKeyValue{ 328 PluginId: pluginId, 329 Key: model.NewId(), 330 Value: []byte(model.NewId()), 331 ExpireAt: 1, 332 } 333 _, err := ss.Plugin().SaveOrUpdate(expiredKV) 334 require.NoError(t, err) 335 336 return expiredKV, tearDown 337 }, 338 } { 339 t.Run(description, func(t *testing.T) { 340 t.Run("setting a nil value should fail", func(t *testing.T) { 341 testCases := map[string][]byte{ 342 "given nil old value": nil, 343 "given non-nil old value": []byte(model.NewId()), 344 } 345 346 for description, oldValue := range testCases { 347 t.Run(description, func(t *testing.T) { 348 kv, tearDown := setup(t) 349 defer tearDown() 350 351 kv.Value = nil 352 assertUnchanged(t, kv, nil, oldValue) 353 }) 354 } 355 }) 356 357 t.Run("setting a non-nil value", func(t *testing.T) { 358 t.Run("should succeed given non-expiring, nil old value", func(t *testing.T) { 359 kv, tearDown := setup(t) 360 defer tearDown() 361 362 kv.ExpireAt = 0 363 assertChanged(t, kv, []byte(nil)) 364 }) 365 366 t.Run("should succeed given not-yet-expired, nil old value", func(t *testing.T) { 367 kv, tearDown := setup(t) 368 defer tearDown() 369 370 kv.ExpireAt = model.GetMillis() + 15*1000 371 assertChanged(t, kv, []byte(nil)) 372 }) 373 374 t.Run("should fail given expired, nil old value", func(t *testing.T) { 375 kv, tearDown := setup(t) 376 defer tearDown() 377 378 kv.ExpireAt = 1 379 assertRemoved(t, kv, []byte(nil)) 380 }) 381 382 t.Run("should fail given 'different' old value", func(t *testing.T) { 383 kv, tearDown := setup(t) 384 defer tearDown() 385 386 assertUnchanged(t, kv, nil, []byte(model.NewId())) 387 }) 388 389 t.Run("should fail given 'same' old value", func(t *testing.T) { 390 kv, tearDown := setup(t) 391 defer tearDown() 392 393 assertUnchanged(t, kv, nil, kv.Value) 394 }) 395 }) 396 }) 397 } 398 399 t.Run("existing key", func(t *testing.T) { 400 setup := func(t *testing.T) (*model.PluginKeyValue, func()) { 401 pluginId, tearDown := setupKVs(t, ss) 402 403 existingKV := &model.PluginKeyValue{ 404 PluginId: pluginId, 405 Key: model.NewId(), 406 Value: []byte(model.NewId()), 407 ExpireAt: 0, 408 } 409 _, err := ss.Plugin().SaveOrUpdate(existingKV) 410 require.NoError(t, err) 411 412 return existingKV, tearDown 413 } 414 415 testCases := map[string]bool{ 416 // CompareAndSet should succeed even if the value isn't changing. 417 "setting the same value": true, 418 "setting a different value": false, 419 } 420 421 for description, setToSameValue := range testCases { 422 makeKV := func(existingKV *model.PluginKeyValue) *model.PluginKeyValue { 423 kv := &model.PluginKeyValue{ 424 PluginId: existingKV.PluginId, 425 Key: existingKV.Key, 426 ExpireAt: existingKV.ExpireAt, 427 } 428 if setToSameValue { 429 kv.Value = existingKV.Value 430 } else { 431 kv.Value = []byte(model.NewId()) 432 } 433 434 return kv 435 } 436 437 t.Run(description, func(t *testing.T) { 438 t.Run("should fail", func(t *testing.T) { 439 testCases := map[string][]byte{ 440 "given nil old value": nil, 441 "given different old value": []byte(model.NewId()), 442 } 443 444 for description, oldValue := range testCases { 445 t.Run(description, func(t *testing.T) { 446 existingKV, tearDown := setup(t) 447 defer tearDown() 448 449 kv := makeKV(existingKV) 450 assertUnchanged(t, kv, existingKV, oldValue) 451 }) 452 } 453 }) 454 455 t.Run("should succeed given same old value", func(t *testing.T) { 456 existingKV, tearDown := setup(t) 457 defer tearDown() 458 459 kv := makeKV(existingKV) 460 461 assertChanged(t, kv, existingKV.Value) 462 }) 463 464 t.Run("and future expiry should succeed given same old value", func(t *testing.T) { 465 existingKV, tearDown := setup(t) 466 defer tearDown() 467 468 kv := makeKV(existingKV) 469 kv.ExpireAt = model.GetMillis() + 15*1000 470 471 assertChanged(t, kv, existingKV.Value) 472 }) 473 474 t.Run("and past expiry should succeed given same old value", func(t *testing.T) { 475 existingKV, tearDown := setup(t) 476 defer tearDown() 477 478 kv := makeKV(existingKV) 479 kv.ExpireAt = model.GetMillis() - 15*1000 480 481 assertRemoved(t, kv, existingKV.Value) 482 }) 483 }) 484 } 485 486 t.Run("setting a nil value", func(t *testing.T) { 487 makeKV := func(existingKV *model.PluginKeyValue) *model.PluginKeyValue { 488 kv := &model.PluginKeyValue{ 489 PluginId: existingKV.PluginId, 490 Key: existingKV.Key, 491 Value: existingKV.Value, 492 ExpireAt: existingKV.ExpireAt, 493 } 494 kv.Value = nil 495 496 return kv 497 } 498 499 t.Run("should fail", func(t *testing.T) { 500 testCases := map[string][]byte{ 501 "given nil old value": nil, 502 "given different old value": []byte(model.NewId()), 503 } 504 505 for description, oldValue := range testCases { 506 t.Run(description, func(t *testing.T) { 507 existingKV, tearDown := setup(t) 508 defer tearDown() 509 510 kv := makeKV(existingKV) 511 assertUnchanged(t, kv, existingKV, oldValue) 512 }) 513 } 514 }) 515 516 t.Run("should succeed, deleting, given same old value", func(t *testing.T) { 517 existingKV, tearDown := setup(t) 518 defer tearDown() 519 520 kv := makeKV(existingKV) 521 assertRemoved(t, kv, existingKV.Value) 522 }) 523 }) 524 }) 525 } 526 527 func testPluginCompareAndSet(t *testing.T, ss store.Store) { 528 doTestPluginCompareAndSet(t, ss, func(kv *model.PluginKeyValue, oldValue []byte) (bool, error) { 529 return ss.Plugin().CompareAndSet(kv, oldValue) 530 }) 531 } 532 533 func testPluginCompareAndDelete(t *testing.T, ss store.Store) { 534 t.Run("invalid kv", func(t *testing.T) { 535 _, tearDown := setupKVs(t, ss) 536 defer tearDown() 537 538 kv := &model.PluginKeyValue{ 539 PluginId: "", 540 Key: model.NewId(), 541 Value: []byte(model.NewId()), 542 ExpireAt: 0, 543 } 544 545 ok, err := ss.Plugin().CompareAndDelete(kv, nil) 546 require.Error(t, err) 547 assert.False(t, ok) 548 appErr, ok := err.(*model.AppError) 549 require.True(t, ok) 550 assert.Equal(t, "model.plugin_key_value.is_valid.plugin_id.app_error", appErr.Id) 551 }) 552 553 t.Run("non-existent key should fail", func(t *testing.T) { 554 pluginId, tearDown := setupKVs(t, ss) 555 defer tearDown() 556 557 key := model.NewId() 558 value := model.NewId() 559 expireAt := int64(0) 560 561 kv := &model.PluginKeyValue{ 562 PluginId: pluginId, 563 Key: key, 564 Value: []byte(value), 565 ExpireAt: expireAt, 566 } 567 568 testCases := map[string][]byte{ 569 "given nil old value": nil, 570 "given non-nil old value": []byte(model.NewId()), 571 } 572 573 for description, oldValue := range testCases { 574 t.Run(description, func(t *testing.T) { 575 ok, err := ss.Plugin().CompareAndDelete(kv, oldValue) 576 require.NoError(t, err) 577 assert.False(t, ok) 578 }) 579 } 580 }) 581 582 t.Run("expired key should fail", func(t *testing.T) { 583 pluginId, tearDown := setupKVs(t, ss) 584 defer tearDown() 585 586 key := model.NewId() 587 value := model.NewId() 588 expireAt := int64(1) 589 590 kv := &model.PluginKeyValue{ 591 PluginId: pluginId, 592 Key: key, 593 Value: []byte(value), 594 ExpireAt: expireAt, 595 } 596 _, err := ss.Plugin().SaveOrUpdate(kv) 597 require.NoError(t, err) 598 599 testCases := map[string][]byte{ 600 "given nil old value": nil, 601 "given different old value": []byte(model.NewId()), 602 "given same old value": []byte(value), 603 } 604 605 for description, oldValue := range testCases { 606 t.Run(description, func(t *testing.T) { 607 ok, err := ss.Plugin().CompareAndDelete(kv, oldValue) 608 require.NoError(t, err) 609 assert.False(t, ok) 610 }) 611 } 612 }) 613 614 t.Run("existing key should fail given different old value", func(t *testing.T) { 615 pluginId, tearDown := setupKVs(t, ss) 616 defer tearDown() 617 618 key := model.NewId() 619 value := model.NewId() 620 expireAt := int64(0) 621 622 kv := &model.PluginKeyValue{ 623 PluginId: pluginId, 624 Key: key, 625 Value: []byte(value), 626 ExpireAt: expireAt, 627 } 628 _, err := ss.Plugin().SaveOrUpdate(kv) 629 require.NoError(t, err) 630 631 oldValue := []byte(model.NewId()) 632 633 ok, err := ss.Plugin().CompareAndDelete(kv, oldValue) 634 require.NoError(t, err) 635 assert.False(t, ok) 636 }) 637 638 t.Run("existing key should succeed given same old value", func(t *testing.T) { 639 pluginId, tearDown := setupKVs(t, ss) 640 defer tearDown() 641 642 key := model.NewId() 643 value := model.NewId() 644 expireAt := int64(0) 645 646 kv := &model.PluginKeyValue{ 647 PluginId: pluginId, 648 Key: key, 649 Value: []byte(value), 650 ExpireAt: expireAt, 651 } 652 _, err := ss.Plugin().SaveOrUpdate(kv) 653 require.NoError(t, err) 654 655 oldValue := []byte(value) 656 657 ok, err := ss.Plugin().CompareAndDelete(kv, oldValue) 658 require.NoError(t, err) 659 assert.True(t, ok) 660 }) 661 } 662 663 func testPluginSetWithOptions(t *testing.T, ss store.Store) { 664 t.Run("invalid options", func(t *testing.T) { 665 _, tearDown := setupKVs(t, ss) 666 defer tearDown() 667 668 pluginId := "" 669 key := model.NewId() 670 value := model.NewId() 671 options := model.PluginKVSetOptions{ 672 Atomic: false, 673 OldValue: []byte("not-nil"), 674 } 675 676 ok, err := ss.Plugin().SetWithOptions(pluginId, key, []byte(value), options) 677 require.Error(t, err) 678 assert.False(t, ok) 679 appErr, ok := err.(*model.AppError) 680 require.True(t, ok) 681 require.Equal(t, "model.plugin_kvset_options.is_valid.old_value.app_error", appErr.Id) 682 }) 683 684 t.Run("invalid kv", func(t *testing.T) { 685 _, tearDown := setupKVs(t, ss) 686 defer tearDown() 687 688 pluginId := "" 689 key := model.NewId() 690 value := model.NewId() 691 options := model.PluginKVSetOptions{} 692 693 ok, err := ss.Plugin().SetWithOptions(pluginId, key, []byte(value), options) 694 require.Error(t, err) 695 assert.False(t, ok) 696 appErr, ok := err.(*model.AppError) 697 require.True(t, ok) 698 require.Equal(t, "model.plugin_key_value.is_valid.plugin_id.app_error", appErr.Id) 699 }) 700 701 t.Run("atomic", func(t *testing.T) { 702 doTestPluginCompareAndSet(t, ss, func(kv *model.PluginKeyValue, oldValue []byte) (bool, error) { 703 now := model.GetMillis() 704 options := model.PluginKVSetOptions{ 705 Atomic: true, 706 OldValue: oldValue, 707 } 708 709 if kv.ExpireAt != 0 { 710 options.ExpireInSeconds = (kv.ExpireAt - now) / 1000 711 } 712 713 return ss.Plugin().SetWithOptions(kv.PluginId, kv.Key, kv.Value, options) 714 }) 715 }) 716 717 t.Run("non-atomic", func(t *testing.T) { 718 doTestPluginSaveOrUpdate(t, ss, func(kv *model.PluginKeyValue) (*model.PluginKeyValue, error) { 719 now := model.GetMillis() 720 options := model.PluginKVSetOptions{ 721 Atomic: false, 722 } 723 724 if kv.ExpireAt != 0 { 725 options.ExpireInSeconds = (kv.ExpireAt - now) / 1000 726 } 727 728 ok, err := ss.Plugin().SetWithOptions(kv.PluginId, kv.Key, kv.Value, options) 729 if !ok { 730 return nil, err 731 } 732 return kv, err 733 }) 734 }) 735 } 736 737 func testPluginGet(t *testing.T, ss store.Store) { 738 t.Run("no matching key value", func(t *testing.T) { 739 pluginId := model.NewId() 740 key := model.NewId() 741 742 kv, nErr := ss.Plugin().Get(pluginId, key) 743 _, ok := nErr.(*store.ErrNotFound) 744 require.Error(t, nErr) 745 assert.True(t, ok) 746 assert.Nil(t, kv) 747 }) 748 749 t.Run("no-matching key value for plugin id", func(t *testing.T) { 750 pluginId := model.NewId() 751 key := model.NewId() 752 value := model.NewId() 753 expireAt := int64(0) 754 755 kv := &model.PluginKeyValue{ 756 PluginId: pluginId, 757 Key: key, 758 Value: []byte(value), 759 ExpireAt: expireAt, 760 } 761 762 _, err := ss.Plugin().SaveOrUpdate(kv) 763 require.NoError(t, err) 764 765 kv, err = ss.Plugin().Get(model.NewId(), key) 766 _, ok := err.(*store.ErrNotFound) 767 require.Error(t, err) 768 assert.True(t, ok) 769 assert.Nil(t, kv) 770 }) 771 772 t.Run("no-matching key value for key", func(t *testing.T) { 773 pluginId := model.NewId() 774 key := model.NewId() 775 value := model.NewId() 776 expireAt := int64(0) 777 778 kv := &model.PluginKeyValue{ 779 PluginId: pluginId, 780 Key: key, 781 Value: []byte(value), 782 ExpireAt: expireAt, 783 } 784 785 _, err := ss.Plugin().SaveOrUpdate(kv) 786 require.NoError(t, err) 787 788 kv, err = ss.Plugin().Get(pluginId, model.NewId()) 789 _, ok := err.(*store.ErrNotFound) 790 require.Error(t, err) 791 assert.True(t, ok) 792 assert.Nil(t, kv) 793 }) 794 795 t.Run("old expired key value", func(t *testing.T) { 796 pluginId := model.NewId() 797 key := model.NewId() 798 value := model.NewId() 799 expireAt := int64(1) 800 801 kv := &model.PluginKeyValue{ 802 PluginId: pluginId, 803 Key: key, 804 Value: []byte(value), 805 ExpireAt: expireAt, 806 } 807 808 _, err := ss.Plugin().SaveOrUpdate(kv) 809 require.NoError(t, err) 810 811 kv, err = ss.Plugin().Get(pluginId, model.NewId()) 812 _, ok := err.(*store.ErrNotFound) 813 require.Error(t, err) 814 assert.True(t, ok) 815 assert.Nil(t, kv) 816 }) 817 818 t.Run("recently expired key value", func(t *testing.T) { 819 pluginId := model.NewId() 820 key := model.NewId() 821 value := model.NewId() 822 expireAt := model.GetMillis() - 15*1000 823 824 kv := &model.PluginKeyValue{ 825 PluginId: pluginId, 826 Key: key, 827 Value: []byte(value), 828 ExpireAt: expireAt, 829 } 830 831 _, err := ss.Plugin().SaveOrUpdate(kv) 832 require.NoError(t, err) 833 834 kv, err = ss.Plugin().Get(pluginId, model.NewId()) 835 _, ok := err.(*store.ErrNotFound) 836 require.Error(t, err) 837 assert.True(t, ok) 838 assert.Nil(t, kv) 839 }) 840 841 t.Run("matching key value, non-expiring", func(t *testing.T) { 842 pluginId := model.NewId() 843 key := model.NewId() 844 value := model.NewId() 845 expireAt := int64(0) 846 847 kv := &model.PluginKeyValue{ 848 PluginId: pluginId, 849 Key: key, 850 Value: []byte(value), 851 ExpireAt: expireAt, 852 } 853 854 _, err := ss.Plugin().SaveOrUpdate(kv) 855 require.NoError(t, err) 856 857 actualKV, err := ss.Plugin().Get(pluginId, key) 858 require.NoError(t, err) 859 require.Equal(t, kv, actualKV) 860 }) 861 862 t.Run("matching key value, not yet expired", func(t *testing.T) { 863 pluginId := model.NewId() 864 key := model.NewId() 865 value := model.NewId() 866 expireAt := model.GetMillis() + 15*1000 867 868 kv := &model.PluginKeyValue{ 869 PluginId: pluginId, 870 Key: key, 871 Value: []byte(value), 872 ExpireAt: expireAt, 873 } 874 875 _, err := ss.Plugin().SaveOrUpdate(kv) 876 require.NoError(t, err) 877 878 actualKV, err := ss.Plugin().Get(pluginId, key) 879 require.NoError(t, err) 880 require.Equal(t, kv, actualKV) 881 }) 882 } 883 884 func testPluginDelete(t *testing.T, ss store.Store) { 885 t.Run("no matching key value", func(t *testing.T) { 886 pluginId, tearDown := setupKVs(t, ss) 887 defer tearDown() 888 889 key := model.NewId() 890 891 err := ss.Plugin().Delete(pluginId, key) 892 require.NoError(t, err) 893 894 kv, err := ss.Plugin().Get(pluginId, key) 895 _, ok := err.(*store.ErrNotFound) 896 require.Error(t, err) 897 assert.True(t, ok) 898 assert.Nil(t, kv) 899 }) 900 901 testCases := []struct { 902 description string 903 expireAt int64 904 }{ 905 { 906 "expired key value", 907 model.GetMillis() - 15*1000, 908 }, 909 { 910 "never expiring value", 911 0, 912 }, 913 { 914 "not yet expired value", 915 model.GetMillis() + 15*1000, 916 }, 917 } 918 919 for _, testCase := range testCases { 920 t.Run(testCase.description, func(t *testing.T) { 921 pluginId, tearDown := setupKVs(t, ss) 922 defer tearDown() 923 924 key := model.NewId() 925 value := model.NewId() 926 expireAt := testCase.expireAt 927 928 kv := &model.PluginKeyValue{ 929 PluginId: pluginId, 930 Key: key, 931 Value: []byte(value), 932 ExpireAt: expireAt, 933 } 934 935 _, err := ss.Plugin().SaveOrUpdate(kv) 936 require.NoError(t, err) 937 938 err = ss.Plugin().Delete(pluginId, key) 939 require.NoError(t, err) 940 941 kv, err = ss.Plugin().Get(pluginId, key) 942 _, ok := err.(*store.ErrNotFound) 943 require.Error(t, err) 944 assert.True(t, ok) 945 assert.Nil(t, kv) 946 }) 947 } 948 } 949 950 func testPluginDeleteAllForPlugin(t *testing.T, ss store.Store) { 951 setupKVsForDeleteAll := func(t *testing.T) (string, func()) { 952 pluginId := model.NewId() 953 otherPluginId := model.NewId() 954 955 // otherPluginKV is another key value for another plugin, and used to verify other 956 // keys aren't modified unintentionally. 957 otherPluginKV := &model.PluginKeyValue{ 958 PluginId: otherPluginId, 959 Key: model.NewId(), 960 Value: []byte(model.NewId()), 961 ExpireAt: 0, 962 } 963 _, err := ss.Plugin().SaveOrUpdate(otherPluginKV) 964 require.NoError(t, err) 965 966 return pluginId, func() { 967 actualOtherPluginKV, err := ss.Plugin().Get(otherPluginKV.PluginId, otherPluginKV.Key) 968 require.NoError(t, err, "failed to find other key value from different plugin") 969 assert.Equal(t, otherPluginKV, actualOtherPluginKV) 970 } 971 } 972 973 t.Run("no keys to delete", func(t *testing.T) { 974 pluginId, tearDown := setupKVsForDeleteAll(t) 975 defer tearDown() 976 977 err := ss.Plugin().DeleteAllForPlugin(pluginId) 978 require.NoError(t, err) 979 }) 980 981 t.Run("multiple keys to delete", func(t *testing.T) { 982 pluginId, tearDown := setupKVsForDeleteAll(t) 983 defer tearDown() 984 985 kv := &model.PluginKeyValue{ 986 PluginId: pluginId, 987 Key: model.NewId(), 988 Value: []byte(model.NewId()), 989 ExpireAt: 0, 990 } 991 _, err := ss.Plugin().SaveOrUpdate(kv) 992 require.NoError(t, err) 993 994 kv2 := &model.PluginKeyValue{ 995 PluginId: pluginId, 996 Key: model.NewId(), 997 Value: []byte(model.NewId()), 998 ExpireAt: 0, 999 } 1000 _, err = ss.Plugin().SaveOrUpdate(kv2) 1001 require.NoError(t, err) 1002 1003 err = ss.Plugin().DeleteAllForPlugin(pluginId) 1004 require.NoError(t, err) 1005 1006 _, err = ss.Plugin().Get(kv.PluginId, kv.Key) 1007 _, ok := err.(*store.ErrNotFound) 1008 require.Error(t, err) 1009 assert.True(t, ok) 1010 1011 _, err = ss.Plugin().Get(kv.PluginId, kv2.Key) 1012 _, ok = err.(*store.ErrNotFound) 1013 require.Error(t, err) 1014 assert.True(t, ok) 1015 }) 1016 } 1017 1018 func testPluginDeleteAllExpired(t *testing.T, ss store.Store) { 1019 t.Run("no keys", func(t *testing.T) { 1020 err := ss.Plugin().DeleteAllExpired() 1021 require.NoError(t, err) 1022 }) 1023 1024 t.Run("no expiring keys to delete", func(t *testing.T) { 1025 pluginIdA := model.NewId() 1026 pluginIdB := model.NewId() 1027 1028 kvA1 := &model.PluginKeyValue{ 1029 PluginId: pluginIdA, 1030 Key: model.NewId(), 1031 Value: []byte(model.NewId()), 1032 ExpireAt: 0, 1033 } 1034 _, err := ss.Plugin().SaveOrUpdate(kvA1) 1035 require.NoError(t, err) 1036 1037 kvA2 := &model.PluginKeyValue{ 1038 PluginId: pluginIdA, 1039 Key: model.NewId(), 1040 Value: []byte(model.NewId()), 1041 ExpireAt: 0, 1042 } 1043 _, err = ss.Plugin().SaveOrUpdate(kvA2) 1044 require.NoError(t, err) 1045 1046 kvB1 := &model.PluginKeyValue{ 1047 PluginId: pluginIdB, 1048 Key: model.NewId(), 1049 Value: []byte(model.NewId()), 1050 ExpireAt: 0, 1051 } 1052 _, err = ss.Plugin().SaveOrUpdate(kvB1) 1053 require.NoError(t, err) 1054 1055 kvB2 := &model.PluginKeyValue{ 1056 PluginId: pluginIdB, 1057 Key: model.NewId(), 1058 Value: []byte(model.NewId()), 1059 ExpireAt: 0, 1060 } 1061 _, err = ss.Plugin().SaveOrUpdate(kvB2) 1062 require.NoError(t, err) 1063 1064 err = ss.Plugin().DeleteAllExpired() 1065 require.NoError(t, err) 1066 1067 actualKVA1, err := ss.Plugin().Get(pluginIdA, kvA1.Key) 1068 require.NoError(t, err) 1069 assert.Equal(t, kvA1, actualKVA1) 1070 1071 actualKVA2, err := ss.Plugin().Get(pluginIdA, kvA2.Key) 1072 require.NoError(t, err) 1073 assert.Equal(t, kvA2, actualKVA2) 1074 1075 actualKVB1, err := ss.Plugin().Get(pluginIdB, kvB1.Key) 1076 require.NoError(t, err) 1077 assert.Equal(t, kvB1, actualKVB1) 1078 1079 actualKVB2, err := ss.Plugin().Get(pluginIdB, kvB2.Key) 1080 require.NoError(t, err) 1081 assert.Equal(t, kvB2, actualKVB2) 1082 }) 1083 1084 t.Run("no expired keys to delete", func(t *testing.T) { 1085 pluginIdA := model.NewId() 1086 pluginIdB := model.NewId() 1087 1088 kvA1 := &model.PluginKeyValue{ 1089 PluginId: pluginIdA, 1090 Key: model.NewId(), 1091 Value: []byte(model.NewId()), 1092 ExpireAt: model.GetMillis() + 15*1000, 1093 } 1094 _, err := ss.Plugin().SaveOrUpdate(kvA1) 1095 require.NoError(t, err) 1096 1097 kvA2 := &model.PluginKeyValue{ 1098 PluginId: pluginIdA, 1099 Key: model.NewId(), 1100 Value: []byte(model.NewId()), 1101 ExpireAt: model.GetMillis() + 15*1000, 1102 } 1103 _, err = ss.Plugin().SaveOrUpdate(kvA2) 1104 require.NoError(t, err) 1105 1106 kvB1 := &model.PluginKeyValue{ 1107 PluginId: pluginIdB, 1108 Key: model.NewId(), 1109 Value: []byte(model.NewId()), 1110 ExpireAt: model.GetMillis() + 15*1000, 1111 } 1112 _, err = ss.Plugin().SaveOrUpdate(kvB1) 1113 require.NoError(t, err) 1114 1115 kvB2 := &model.PluginKeyValue{ 1116 PluginId: pluginIdB, 1117 Key: model.NewId(), 1118 Value: []byte(model.NewId()), 1119 ExpireAt: model.GetMillis() + 15*1000, 1120 } 1121 _, err = ss.Plugin().SaveOrUpdate(kvB2) 1122 require.NoError(t, err) 1123 1124 err = ss.Plugin().DeleteAllExpired() 1125 require.NoError(t, err) 1126 1127 actualKVA1, err := ss.Plugin().Get(pluginIdA, kvA1.Key) 1128 require.NoError(t, err) 1129 assert.Equal(t, kvA1, actualKVA1) 1130 1131 actualKVA2, err := ss.Plugin().Get(pluginIdA, kvA2.Key) 1132 require.NoError(t, err) 1133 assert.Equal(t, kvA2, actualKVA2) 1134 1135 actualKVB1, err := ss.Plugin().Get(pluginIdB, kvB1.Key) 1136 require.NoError(t, err) 1137 assert.Equal(t, kvB1, actualKVB1) 1138 1139 actualKVB2, err := ss.Plugin().Get(pluginIdB, kvB2.Key) 1140 require.NoError(t, err) 1141 assert.Equal(t, kvB2, actualKVB2) 1142 }) 1143 1144 t.Run("some expired keys to delete", func(t *testing.T) { 1145 pluginIdA := model.NewId() 1146 pluginIdB := model.NewId() 1147 1148 kvA1 := &model.PluginKeyValue{ 1149 PluginId: pluginIdA, 1150 Key: model.NewId(), 1151 Value: []byte(model.NewId()), 1152 ExpireAt: model.GetMillis() + 15*1000, 1153 } 1154 _, err := ss.Plugin().SaveOrUpdate(kvA1) 1155 require.NoError(t, err) 1156 1157 expiredKVA2 := &model.PluginKeyValue{ 1158 PluginId: pluginIdA, 1159 Key: model.NewId(), 1160 Value: []byte(model.NewId()), 1161 ExpireAt: model.GetMillis() - 15*1000, 1162 } 1163 _, err = ss.Plugin().SaveOrUpdate(expiredKVA2) 1164 require.NoError(t, err) 1165 1166 kvB1 := &model.PluginKeyValue{ 1167 PluginId: pluginIdB, 1168 Key: model.NewId(), 1169 Value: []byte(model.NewId()), 1170 ExpireAt: model.GetMillis() + 15*1000, 1171 } 1172 _, err = ss.Plugin().SaveOrUpdate(kvB1) 1173 require.NoError(t, err) 1174 1175 expiredKVB2 := &model.PluginKeyValue{ 1176 PluginId: pluginIdB, 1177 Key: model.NewId(), 1178 Value: []byte(model.NewId()), 1179 ExpireAt: model.GetMillis() - 15*1000, 1180 } 1181 _, err = ss.Plugin().SaveOrUpdate(expiredKVB2) 1182 require.NoError(t, err) 1183 1184 err = ss.Plugin().DeleteAllExpired() 1185 require.NoError(t, err) 1186 1187 actualKVA1, err := ss.Plugin().Get(pluginIdA, kvA1.Key) 1188 require.NoError(t, err) 1189 assert.Equal(t, kvA1, actualKVA1) 1190 1191 actualKVA2, err := ss.Plugin().Get(pluginIdA, expiredKVA2.Key) 1192 _, ok := err.(*store.ErrNotFound) 1193 require.Error(t, err) 1194 assert.True(t, ok) 1195 assert.Nil(t, actualKVA2) 1196 1197 actualKVB1, err := ss.Plugin().Get(pluginIdB, kvB1.Key) 1198 require.NoError(t, err) 1199 assert.Equal(t, kvB1, actualKVB1) 1200 1201 actualKVB2, err := ss.Plugin().Get(pluginIdB, expiredKVB2.Key) 1202 _, ok = err.(*store.ErrNotFound) 1203 require.Error(t, err) 1204 assert.True(t, ok) 1205 assert.Nil(t, actualKVB2) 1206 }) 1207 } 1208 1209 func testPluginList(t *testing.T, ss store.Store) { 1210 t.Run("no key values", func(t *testing.T) { 1211 _, tearDown := setupKVs(t, ss) 1212 defer tearDown() 1213 1214 // Ignore the pluginId setup by setupKVs 1215 pluginId := model.NewId() 1216 keys, err := ss.Plugin().List(pluginId, 0, 100) 1217 require.NoError(t, err) 1218 assert.Empty(t, keys) 1219 }) 1220 1221 t.Run("single key", func(t *testing.T) { 1222 _, tearDown := setupKVs(t, ss) 1223 defer tearDown() 1224 1225 // Ignore the pluginId setup by setupKVs 1226 pluginId := model.NewId() 1227 1228 kv := &model.PluginKeyValue{ 1229 PluginId: pluginId, 1230 Key: model.NewId(), 1231 Value: []byte(model.NewId()), 1232 ExpireAt: 0, 1233 } 1234 _, err := ss.Plugin().SaveOrUpdate(kv) 1235 require.NoError(t, err) 1236 1237 keys, err := ss.Plugin().List(pluginId, 0, 100) 1238 require.NoError(t, err) 1239 require.Len(t, keys, 1) 1240 assert.Equal(t, kv.Key, keys[0]) 1241 }) 1242 1243 t.Run("multiple keys", func(t *testing.T) { 1244 _, tearDown := setupKVs(t, ss) 1245 defer tearDown() 1246 1247 // Ignore the pluginId setup by setupKVs 1248 pluginId := model.NewId() 1249 1250 var keys []string 1251 for i := 0; i < 150; i++ { 1252 key := model.NewId() 1253 kv := &model.PluginKeyValue{ 1254 PluginId: pluginId, 1255 Key: key, 1256 Value: []byte(model.NewId()), 1257 ExpireAt: 0, 1258 } 1259 _, err := ss.Plugin().SaveOrUpdate(kv) 1260 require.NoError(t, err) 1261 1262 keys = append(keys, key) 1263 } 1264 sort.Strings(keys) 1265 1266 keys1, err := ss.Plugin().List(pluginId, 0, 100) 1267 require.NoError(t, err) 1268 require.Len(t, keys1, 100) 1269 1270 keys2, err := ss.Plugin().List(pluginId, 100, 100) 1271 require.NoError(t, err) 1272 require.Len(t, keys2, 50) 1273 1274 actualKeys := append(keys1, keys2...) 1275 sort.Strings(actualKeys) 1276 1277 assert.Equal(t, keys, actualKeys) 1278 }) 1279 1280 t.Run("multiple keys, some expiring", func(t *testing.T) { 1281 _, tearDown := setupKVs(t, ss) 1282 defer tearDown() 1283 1284 // Ignore the pluginId setup by setupKVs 1285 pluginId := model.NewId() 1286 1287 var keys []string 1288 var expiredKeys []string 1289 now := model.GetMillis() 1290 for i := 0; i < 150; i++ { 1291 key := model.NewId() 1292 var expireAt int64 1293 1294 if i%10 == 0 { 1295 // Expire keys 0, 10, 20, ... 1296 expireAt = 1 1297 1298 } else if (i+5)%10 == 0 { 1299 // Mark for future expiry keys 5, 15, 25, ... 1300 expireAt = now + 5*60*1000 1301 } 1302 1303 kv := &model.PluginKeyValue{ 1304 PluginId: pluginId, 1305 Key: key, 1306 Value: []byte(model.NewId()), 1307 ExpireAt: expireAt, 1308 } 1309 _, err := ss.Plugin().SaveOrUpdate(kv) 1310 require.NoError(t, err) 1311 1312 if expireAt == 0 || expireAt > now { 1313 keys = append(keys, key) 1314 } else { 1315 expiredKeys = append(expiredKeys, key) 1316 } 1317 } 1318 sort.Strings(keys) 1319 1320 keys1, err := ss.Plugin().List(pluginId, 0, 100) 1321 require.NoError(t, err) 1322 require.Len(t, keys1, 100) 1323 1324 keys2, err := ss.Plugin().List(pluginId, 100, 100) 1325 require.NoError(t, err) 1326 require.Len(t, keys2, 35) 1327 1328 actualKeys := append(keys1, keys2...) 1329 sort.Strings(actualKeys) 1330 1331 assert.Equal(t, keys, actualKeys) 1332 }) 1333 1334 t.Run("offsets and limits", func(t *testing.T) { 1335 _, tearDown := setupKVs(t, ss) 1336 defer tearDown() 1337 1338 // Ignore the pluginId setup by setupKVs 1339 pluginId := model.NewId() 1340 1341 var keys []string 1342 for i := 0; i < 150; i++ { 1343 key := model.NewId() 1344 kv := &model.PluginKeyValue{ 1345 PluginId: pluginId, 1346 Key: key, 1347 Value: []byte(model.NewId()), 1348 ExpireAt: 0, 1349 } 1350 _, err := ss.Plugin().SaveOrUpdate(kv) 1351 require.NoError(t, err) 1352 1353 keys = append(keys, key) 1354 } 1355 sort.Strings(keys) 1356 1357 t.Run("default limit", func(t *testing.T) { 1358 keys1, err := ss.Plugin().List(pluginId, 0, 0) 1359 require.NoError(t, err) 1360 require.Len(t, keys1, 10) 1361 }) 1362 1363 t.Run("offset 0, limit 1", func(t *testing.T) { 1364 keys2, err := ss.Plugin().List(pluginId, 0, 1) 1365 require.NoError(t, err) 1366 require.Len(t, keys2, 1) 1367 }) 1368 1369 t.Run("offset 1, limit 1", func(t *testing.T) { 1370 keys2, err := ss.Plugin().List(pluginId, 1, 1) 1371 require.NoError(t, err) 1372 require.Len(t, keys2, 1) 1373 }) 1374 }) 1375 }