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 }