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  }