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  }