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