github.com/ystia/yorc/v4@v4.3.0/storage/store/test.go (about)

     1  // Copyright 2019 Bull S.A.S. Atos Technologies - Bull, Rue Jean Jaures, B.P.68, 78340, Les Clayes-sous-Bois, France.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package store
    16  
    17  import (
    18  	"context"
    19  	"io/ioutil"
    20  	"math/rand"
    21  	"os"
    22  	"reflect"
    23  	"strconv"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/hashicorp/consul/api"
    28  	"github.com/hashicorp/consul/sdk/testutil"
    29  	"github.com/mitchellh/mapstructure"
    30  	"github.com/stretchr/testify/assert"
    31  	"github.com/stretchr/testify/require"
    32  	"github.com/ystia/yorc/v4/config"
    33  	"github.com/ystia/yorc/v4/helper/consulutil"
    34  )
    35  
    36  // Foo is just some struct for common tests.
    37  type Foo struct {
    38  	Bar        string
    39  	privateBar string
    40  }
    41  
    42  type privateFoo struct {
    43  	Bar        string
    44  	privateBar string
    45  }
    46  
    47  // ComplexFoo is just a complex struct for common tests.
    48  type ComplexFoo struct {
    49  	FooData   Foo
    50  	Value     string
    51  	ValueInt  int
    52  	ValueBool bool
    53  	FooList   []Foo
    54  	FooMap    map[string]Foo
    55  }
    56  
    57  func handleGetError(t *testing.T, err error, found bool) {
    58  	if err != nil {
    59  		t.Error(err)
    60  	}
    61  	if !found {
    62  		t.Error("No value was found, but should have been")
    63  	}
    64  }
    65  
    66  // SetupTestConfig sets working directory configuration
    67  // Warning: You need to defer the working directory removal
    68  // This is a private Consul server instantiation as done in github.com/ystia/yorc/v4/testutil
    69  // This allows avoiding cyclic dependencies with deployments store package
    70  func SetupTestConfig(t testing.TB) config.Configuration {
    71  	workingDir, err := ioutil.TempDir(os.TempDir(), "work")
    72  	assert.Nil(t, err)
    73  
    74  	return config.Configuration{
    75  		WorkingDirectory:        workingDir,
    76  		UpgradeConcurrencyLimit: config.DefaultUpgradesConcurrencyLimit,
    77  	}
    78  }
    79  
    80  // NewTestConsulInstance allows to provide new Consul instance for tests
    81  // This is a private Consul server instantiation as done in github.com/ystia/yorc/v4/testutil
    82  // This allows avoiding cyclic dependencies with deployments store package
    83  func NewTestConsulInstance(t testing.TB, cfg *config.Configuration) (*testutil.TestServer, *api.Client) {
    84  	logLevel := "debug"
    85  	if isCI, ok := os.LookupEnv("CI"); ok && isCI == "true" {
    86  		logLevel = "warn"
    87  	}
    88  
    89  	cb := func(c *testutil.TestServerConfig) {
    90  		c.Args = []string{"-ui"}
    91  		c.LogLevel = logLevel
    92  	}
    93  
    94  	srv1, err := testutil.NewTestServerConfigT(t, cb)
    95  	if err != nil {
    96  		t.Fatalf("Failed to create consul server: %v", err)
    97  	}
    98  
    99  	cfg.Consul.Address = srv1.HTTPAddr
   100  	cfg.Consul.PubMaxRoutines = config.DefaultConsulPubMaxRoutines
   101  	client, err := cfg.GetNewConsulClient()
   102  	assert.Nil(t, err)
   103  
   104  	kv := client.KV()
   105  	consulutil.InitConsulPublisher(cfg.Consul.PubMaxRoutines, kv)
   106  
   107  	return srv1, client
   108  }
   109  
   110  // CommonStoreTest allows to test storage by storing, reading and deleting data
   111  // TestStore tests if reading from, writing to and deleting from the store works properly.
   112  // A struct is used as value. See TestTypes() for a test that is simpler but tests all types.
   113  func CommonStoreTest(t *testing.T, store Store) {
   114  	key := strconv.FormatInt(rand.Int63(), 10)
   115  	ctx := context.Background()
   116  	// Initially the key shouldn't exist
   117  	found, err := store.Get(key, new(Foo))
   118  	if err != nil {
   119  		t.Error(err)
   120  	}
   121  	if found {
   122  		t.Error("A value was found, but no value was expected")
   123  	}
   124  
   125  	// Deleting a non-existing key-value pair should NOT lead to an error
   126  	err = store.Delete(ctx, key, false)
   127  	if err != nil {
   128  		t.Error(err)
   129  	}
   130  
   131  	// Store an object
   132  	val := Foo{
   133  		Bar: "baz",
   134  	}
   135  	err = store.Set(ctx, key, val)
   136  	if err != nil {
   137  		t.Error(err)
   138  	}
   139  
   140  	// Get last Index
   141  	lastIndex, err := store.GetLastModifyIndex(key)
   142  	require.NoError(t, err)
   143  
   144  	// Storing it again should not lead to an error but just overwrite it
   145  	err = store.Set(ctx, key, val)
   146  	if err != nil {
   147  		t.Error(err)
   148  	}
   149  	time.Sleep(10 * time.Millisecond)
   150  	// The last Index should be greater than previous one
   151  	nextLastIndex, err := store.GetLastModifyIndex(key)
   152  	require.NoError(t, err)
   153  	require.True(t, nextLastIndex >= lastIndex)
   154  
   155  	// Retrieve the object
   156  	expected := val
   157  	actualPtr := new(Foo)
   158  	found, err = store.Get(key, actualPtr)
   159  	if err != nil {
   160  		t.Error(err)
   161  	}
   162  	if !found {
   163  		t.Error("No value was found, but should have been")
   164  	}
   165  	actual := *actualPtr
   166  	if actual != expected {
   167  		t.Errorf("Expected: %v, but was: %v", expected, actual)
   168  	}
   169  
   170  	// Delete
   171  	err = store.Delete(ctx, key, false)
   172  	if err != nil {
   173  		t.Error(err)
   174  	}
   175  	// wait for value to be deleted
   176  	time.Sleep(10 * time.Millisecond)
   177  	// Key-value pair shouldn't exist anymore
   178  	found, err = store.Get(key, new(Foo))
   179  	if err != nil {
   180  		t.Error(err)
   181  	}
   182  	if found {
   183  		t.Error("A value was found, but no value was expected")
   184  	}
   185  
   186  	// Tree handling
   187  	keypath1 := "one"
   188  	keypath2 := "one/two"
   189  	keypath3 := "one/two/one"
   190  	keypath4 := "one/two/two"
   191  	keypath5 := "one/two/three"
   192  	err = store.Set(ctx, keypath1, val)
   193  	require.NoError(t, err)
   194  
   195  	err = store.Set(ctx, keypath2, val)
   196  	require.NoError(t, err)
   197  
   198  	err = store.Set(ctx, keypath3, val)
   199  	require.NoError(t, err)
   200  
   201  	err = store.Set(ctx, keypath4, val)
   202  	require.NoError(t, err)
   203  
   204  	err = store.Set(ctx, keypath5, val)
   205  	require.NoError(t, err)
   206  
   207  	// Check sub-keys
   208  	keys, err := store.Keys(keypath1)
   209  	require.NoError(t, err)
   210  	require.Equal(t, 1, len(keys))
   211  	require.Contains(t, keys, keypath2)
   212  
   213  	keys, err = store.Keys(keypath2)
   214  	require.NoError(t, err)
   215  	require.Equal(t, 3, len(keys))
   216  	require.Contains(t, keys, keypath3)
   217  	require.Contains(t, keys, keypath4)
   218  	require.Contains(t, keys, keypath5)
   219  
   220  	// Delete recursively tree
   221  	store.Delete(ctx, keypath1, true)
   222  	keys, err = store.Keys(keypath1)
   223  	require.NoError(t, err)
   224  	require.Nil(t, keys)
   225  
   226  	// wait for value to be deleted
   227  	time.Sleep(10 * time.Millisecond)
   228  
   229  	keys, err = store.Keys(keypath2)
   230  	require.NoError(t, err)
   231  	require.Nil(t, keys)
   232  
   233  	// Test List
   234  	val0 := Foo{
   235  		Bar: "mybar",
   236  	}
   237  
   238  	val1 := ComplexFoo{
   239  		FooData: Foo{
   240  			Bar: "Bar1",
   241  		},
   242  		Value:     "myValue",
   243  		ValueInt:  10,
   244  		ValueBool: true,
   245  		FooList:   []Foo{val0},
   246  		FooMap: map[string]Foo{
   247  			"keyOne": {
   248  				Bar: "BarMap",
   249  			},
   250  		},
   251  	}
   252  
   253  	val2 := ComplexFoo{
   254  		FooData: Foo{
   255  			Bar: "Bar2",
   256  		},
   257  		Value:     "myValue2",
   258  		ValueInt:  20,
   259  		ValueBool: false,
   260  		FooList:   []Foo{val0, val0},
   261  		FooMap: map[string]Foo{
   262  			"keyOne": {
   263  				Bar: "BarMap",
   264  			},
   265  		},
   266  	}
   267  
   268  	keyValues := []KeyValueIn{
   269  		{
   270  			Key:   "rootList/testlist/one",
   271  			Value: val1,
   272  		},
   273  		{
   274  			Key:   "rootList/testlist2/two",
   275  			Value: val2,
   276  		},
   277  	}
   278  
   279  	err = store.SetCollection(ctx, keyValues)
   280  	require.NoError(t, err)
   281  
   282  	kvs, index, err := store.List(ctx, "rootList", 0, 0)
   283  	require.NoError(t, err)
   284  	require.NotZero(t, index)
   285  	require.NotNil(t, kvs)
   286  	require.Equal(t, 2, len(kvs))
   287  
   288  	for _, kv := range kvs {
   289  		value := ComplexFoo{}
   290  		err = mapstructure.Decode(kv.Value, &value)
   291  		require.NoError(t, err)
   292  		switch kv.Key {
   293  		case "rootList/testlist/one":
   294  			if !reflect.DeepEqual(value, val1) {
   295  				t.Errorf("List() = %v, want %v", value, val1)
   296  			}
   297  		case "rootList/testlist2/two":
   298  			if !reflect.DeepEqual(value, val2) {
   299  				t.Errorf("List() = %v, want %v", value, val2)
   300  			}
   301  		default:
   302  			require.Fail(t, "unexpected key:%q", kv.Key)
   303  		}
   304  	}
   305  
   306  	// List with blocking query and no new key so timeout si done
   307  	time.Sleep(time.Second)
   308  	kvs, nextLastIndex, err = store.List(ctx, "rootList", index, 1*time.Second)
   309  	require.NoError(t, err)
   310  	require.NotZero(t, nextLastIndex)
   311  	require.NotNil(t, kvs)
   312  	require.Equal(t, 0, len(kvs))
   313  	require.True(t, nextLastIndex == index)
   314  	// List with blocking query and new key so index is changed
   315  	go func() {
   316  		kvs, nextLastIndex, err = store.List(ctx, "rootList", index, 1*time.Second)
   317  		require.NoError(t, err)
   318  		assert.NotZero(t, nextLastIndex)
   319  		assert.NotNil(t, kvs)
   320  		assert.Equal(t, 1, len(kvs))
   321  		assert.True(t, nextLastIndex >= lastIndex)
   322  	}()
   323  	err = store.Set(ctx, "rootList/testlist/three", val1)
   324  	require.NoError(t, err)
   325  
   326  	// List on non-existing path
   327  	kvs, index, err = store.List(ctx, "this/path/dont/exist", 0, 0)
   328  	require.NoError(t, err)
   329  	require.Nil(t, kvs)
   330  }
   331  
   332  // CommonStoreTestAllTypes allows to test storage of all types
   333  func CommonStoreTestAllTypes(t *testing.T, store Store) {
   334  	ctx := context.Background()
   335  	boolVar := true
   336  	// Omit byte
   337  	// Omit error - it's a Go builtin type but marshalling and then unmarshalling doesn't lead to equal objects
   338  	floatVar := 1.2
   339  	intVar := 1
   340  	runeVar := '⚡'
   341  	stringVar := "foo"
   342  
   343  	structVar := Foo{
   344  		Bar: "baz",
   345  	}
   346  	structWithPrivateFieldVar := Foo{
   347  		Bar:        "baz",
   348  		privateBar: "privBaz",
   349  	}
   350  	// The differing expected var for structWithPrivateFieldVar
   351  	structWithPrivateFieldExpectedVar := Foo{
   352  		Bar: "baz",
   353  	}
   354  	privateStructVar := privateFoo{
   355  		Bar: "baz",
   356  	}
   357  	privateStructWithPrivateFieldVar := privateFoo{
   358  		Bar:        "baz",
   359  		privateBar: "privBaz",
   360  	}
   361  	// The differing expected var for privateStructWithPrivateFieldVar
   362  	privateStructWithPrivateFieldExpectedVar := privateFoo{
   363  		Bar: "baz",
   364  	}
   365  
   366  	sliceOfBool := []bool{true, false}
   367  	sliceOfByte := []byte("foo")
   368  	// Omit slice of float
   369  	sliceOfInt := []int{1, 2}
   370  	// Omit slice of rune
   371  	sliceOfString := []string{"foo", "bar"}
   372  
   373  	sliceOfSliceOfString := [][]string{{"foo", "bar"}}
   374  
   375  	sliceOfStruct := []Foo{{Bar: "baz"}}
   376  	sliceOfPrivateStruct := []privateFoo{{Bar: "baz"}}
   377  
   378  	testVals := []struct {
   379  		subTestName string
   380  		val         interface{}
   381  		expected    interface{}
   382  		testGet     func(*testing.T, Store, string, interface{})
   383  	}{
   384  		{"bool", boolVar, boolVar, func(t *testing.T, store Store, key string, expected interface{}) {
   385  			actualPtr := new(bool)
   386  			found, err := store.Get(key, actualPtr)
   387  			handleGetError(t, err, found)
   388  			actual := *actualPtr
   389  			if actual != expected {
   390  				t.Errorf("Expected: %v, but was: %v", expected, actual)
   391  			}
   392  		}},
   393  		{"float", floatVar, floatVar, func(t *testing.T, store Store, key string, expected interface{}) {
   394  			actualPtr := new(float64)
   395  			found, err := store.Get(key, actualPtr)
   396  			handleGetError(t, err, found)
   397  			actual := *actualPtr
   398  			if actual != expected {
   399  				t.Errorf("Expected: %v, but was: %v", expected, actual)
   400  			}
   401  		}},
   402  		{"int", intVar, intVar, func(t *testing.T, store Store, key string, expected interface{}) {
   403  			actualPtr := new(int)
   404  			found, err := store.Get(key, actualPtr)
   405  			handleGetError(t, err, found)
   406  			actual := *actualPtr
   407  			if actual != expected {
   408  				t.Errorf("Expected: %v, but was: %v", expected, actual)
   409  			}
   410  		}},
   411  		{"rune", runeVar, runeVar, func(t *testing.T, store Store, key string, expected interface{}) {
   412  			actualPtr := new(rune)
   413  			found, err := store.Get(key, actualPtr)
   414  			handleGetError(t, err, found)
   415  			actual := *actualPtr
   416  			if actual != expected {
   417  				t.Errorf("Expected: %v, but was: %v", expected, actual)
   418  			}
   419  		}},
   420  		{"string", stringVar, stringVar, func(t *testing.T, store Store, key string, expected interface{}) {
   421  			actualPtr := new(string)
   422  			found, err := store.Get(key, actualPtr)
   423  			handleGetError(t, err, found)
   424  			actual := *actualPtr
   425  			if actual != expected {
   426  				t.Errorf("Expected: %v, but was: %v", expected, actual)
   427  			}
   428  		}},
   429  		{"struct", structVar, structVar, func(t *testing.T, store Store, key string, expected interface{}) {
   430  			actualPtr := new(Foo)
   431  			found, err := store.Get(key, actualPtr)
   432  			handleGetError(t, err, found)
   433  			actual := *actualPtr
   434  			if actual != expected {
   435  				t.Errorf("Expected: %v, but was: %v", expected, actual)
   436  			}
   437  		}},
   438  		{"struct with private field", structWithPrivateFieldVar, structWithPrivateFieldExpectedVar, func(t *testing.T, store Store, key string, expected interface{}) {
   439  			actualPtr := new(Foo)
   440  			found, err := store.Get(key, actualPtr)
   441  			handleGetError(t, err, found)
   442  			actual := *actualPtr
   443  			if actual != expected {
   444  				t.Errorf("Expected: %v, but was: %v", expected, actual)
   445  			}
   446  		}},
   447  		{"private struct", privateStructVar, privateStructVar, func(t *testing.T, store Store, key string, expected interface{}) {
   448  			actualPtr := new(privateFoo)
   449  			found, err := store.Get(key, actualPtr)
   450  			handleGetError(t, err, found)
   451  			actual := *actualPtr
   452  			if actual != expected {
   453  				t.Errorf("Expected: %v, but was: %v", expected, actual)
   454  			}
   455  		}},
   456  		{"private struct with private field", privateStructWithPrivateFieldVar, privateStructWithPrivateFieldExpectedVar, func(t *testing.T, store Store, key string, expected interface{}) {
   457  			actualPtr := new(privateFoo)
   458  			found, err := store.Get(key, actualPtr)
   459  			handleGetError(t, err, found)
   460  			actual := *actualPtr
   461  			if actual != expected {
   462  				t.Errorf("Expected: %v, but was: %v", expected, actual)
   463  			}
   464  		}},
   465  		{"slice of bool", sliceOfBool, sliceOfBool, func(t *testing.T, store Store, key string, expected interface{}) {
   466  			actualPtr := new([]bool)
   467  			found, err := store.Get(key, actualPtr)
   468  			handleGetError(t, err, found)
   469  			actual := *actualPtr
   470  			if !reflect.DeepEqual(actual, expected) {
   471  				t.Errorf("testTypes() = %v, want %v", actual, expected)
   472  			}
   473  		}},
   474  		{"slice of byte", sliceOfByte, sliceOfByte, func(t *testing.T, store Store, key string, expected interface{}) {
   475  			actualPtr := new([]byte)
   476  			found, err := store.Get(key, actualPtr)
   477  			handleGetError(t, err, found)
   478  			actual := *actualPtr
   479  			if !reflect.DeepEqual(actual, expected) {
   480  				t.Errorf("testTypes() = %v, want %v", actual, expected)
   481  			}
   482  		}},
   483  		{"slice of int", sliceOfInt, sliceOfInt, func(t *testing.T, store Store, key string, expected interface{}) {
   484  			actualPtr := new([]int)
   485  			found, err := store.Get(key, actualPtr)
   486  			handleGetError(t, err, found)
   487  			actual := *actualPtr
   488  			if !reflect.DeepEqual(actual, expected) {
   489  				t.Errorf("testTypes() = %v, want %v", actual, expected)
   490  			}
   491  		}},
   492  		{"slice of string", sliceOfString, sliceOfString, func(t *testing.T, store Store, key string, expected interface{}) {
   493  			actualPtr := new([]string)
   494  			found, err := store.Get(key, actualPtr)
   495  			handleGetError(t, err, found)
   496  			actual := *actualPtr
   497  			if !reflect.DeepEqual(actual, expected) {
   498  				t.Errorf("testTypes() = %v, want %v", actual, expected)
   499  			}
   500  		}},
   501  		{"slice of slice of string", sliceOfSliceOfString, sliceOfSliceOfString, func(t *testing.T, store Store, key string, expected interface{}) {
   502  			actualPtr := new([][]string)
   503  			found, err := store.Get(key, actualPtr)
   504  			handleGetError(t, err, found)
   505  			actual := *actualPtr
   506  			if !reflect.DeepEqual(actual, expected) {
   507  				t.Errorf("testTypes() = %v, want %v", actual, expected)
   508  			}
   509  		}},
   510  		{"slice of struct", sliceOfStruct, sliceOfStruct, func(t *testing.T, store Store, key string, expected interface{}) {
   511  			actualPtr := new([]Foo)
   512  			found, err := store.Get(key, actualPtr)
   513  			handleGetError(t, err, found)
   514  			actual := *actualPtr
   515  			if !reflect.DeepEqual(actual, expected) {
   516  				t.Errorf("testTypes() = %v, want %v", actual, expected)
   517  			}
   518  		}},
   519  		{"slice of private struct", sliceOfPrivateStruct, sliceOfPrivateStruct, func(t *testing.T, store Store, key string, expected interface{}) {
   520  			actualPtr := new([]privateFoo)
   521  			found, err := store.Get(key, actualPtr)
   522  			handleGetError(t, err, found)
   523  			actual := *actualPtr
   524  			if !reflect.DeepEqual(actual, expected) {
   525  				t.Errorf("testTypes() = %v, want %v", actual, expected)
   526  			}
   527  		}},
   528  	}
   529  
   530  	for _, testVal := range testVals {
   531  		t.Run(testVal.subTestName, func(t2 *testing.T) {
   532  			key := strconv.FormatInt(rand.Int63(), 10)
   533  			err := store.Set(ctx, key, testVal.val)
   534  			if err != nil {
   535  				t.Error(err)
   536  			}
   537  			testVal.testGet(t, store, key, testVal.expected)
   538  		})
   539  	}
   540  }