5849898283
Unit tests functional and coverage back to 100% Add more routes to dictionary, add more credentials, add default port 5554, rename cameradar logs ENV variable, improve unit test readability, remove tmp file
174 lines
6.3 KiB
Go
174 lines
6.3 KiB
Go
package viper
|
||
|
||
import (
|
||
"fmt"
|
||
"strings"
|
||
"testing"
|
||
|
||
"github.com/spf13/cast"
|
||
"github.com/stretchr/testify/assert"
|
||
)
|
||
|
||
type layer int
|
||
|
||
const (
|
||
defaultLayer layer = iota + 1
|
||
overrideLayer
|
||
)
|
||
|
||
func TestNestedOverrides(t *testing.T) {
|
||
assert := assert.New(t)
|
||
var v *Viper
|
||
|
||
// Case 0: value overridden by a value
|
||
overrideDefault(assert, "tom", 10, "tom", 20) // "tom" is first given 10 as default value, then overridden by 20
|
||
override(assert, "tom", 10, "tom", 20) // "tom" is first given value 10, then overridden by 20
|
||
overrideDefault(assert, "tom.age", 10, "tom.age", 20)
|
||
override(assert, "tom.age", 10, "tom.age", 20)
|
||
overrideDefault(assert, "sawyer.tom.age", 10, "sawyer.tom.age", 20)
|
||
override(assert, "sawyer.tom.age", 10, "sawyer.tom.age", 20)
|
||
|
||
// Case 1: key:value overridden by a value
|
||
v = overrideDefault(assert, "tom.age", 10, "tom", "boy") // "tom.age" is first given 10 as default value, then "tom" is overridden by "boy"
|
||
assert.Nil(v.Get("tom.age")) // "tom.age" should not exist anymore
|
||
v = override(assert, "tom.age", 10, "tom", "boy")
|
||
assert.Nil(v.Get("tom.age"))
|
||
|
||
// Case 2: value overridden by a key:value
|
||
overrideDefault(assert, "tom", "boy", "tom.age", 10) // "tom" is first given "boy" as default value, then "tom" is overridden by map{"age":10}
|
||
override(assert, "tom.age", 10, "tom", "boy")
|
||
|
||
// Case 3: key:value overridden by a key:value
|
||
v = overrideDefault(assert, "tom.size", 4, "tom.age", 10)
|
||
assert.Equal(4, v.Get("tom.size")) // value should still be reachable
|
||
v = override(assert, "tom.size", 4, "tom.age", 10)
|
||
assert.Equal(4, v.Get("tom.size"))
|
||
deepCheckValue(assert, v, overrideLayer, []string{"tom", "size"}, 4)
|
||
|
||
// Case 4: key:value overridden by a map
|
||
v = overrideDefault(assert, "tom.size", 4, "tom", map[string]interface{}{"age": 10}) // "tom.size" is first given "4" as default value, then "tom" is overridden by map{"age":10}
|
||
assert.Equal(4, v.Get("tom.size")) // "tom.size" should still be reachable
|
||
assert.Equal(10, v.Get("tom.age")) // new value should be there
|
||
deepCheckValue(assert, v, overrideLayer, []string{"tom", "age"}, 10) // new value should be there
|
||
v = override(assert, "tom.size", 4, "tom", map[string]interface{}{"age": 10})
|
||
assert.Nil(v.Get("tom.size"))
|
||
assert.Equal(10, v.Get("tom.age"))
|
||
deepCheckValue(assert, v, overrideLayer, []string{"tom", "age"}, 10)
|
||
|
||
// Case 5: array overridden by a value
|
||
overrideDefault(assert, "tom", []int{10, 20}, "tom", 30)
|
||
override(assert, "tom", []int{10, 20}, "tom", 30)
|
||
overrideDefault(assert, "tom.age", []int{10, 20}, "tom.age", 30)
|
||
override(assert, "tom.age", []int{10, 20}, "tom.age", 30)
|
||
|
||
// Case 6: array overridden by an array
|
||
overrideDefault(assert, "tom", []int{10, 20}, "tom", []int{30, 40})
|
||
override(assert, "tom", []int{10, 20}, "tom", []int{30, 40})
|
||
overrideDefault(assert, "tom.age", []int{10, 20}, "tom.age", []int{30, 40})
|
||
v = override(assert, "tom.age", []int{10, 20}, "tom.age", []int{30, 40})
|
||
// explicit array merge:
|
||
s, ok := v.Get("tom.age").([]int)
|
||
if assert.True(ok, "tom[\"age\"] is not a slice") {
|
||
v.Set("tom.age", append(s, []int{50, 60}...))
|
||
assert.Equal([]int{30, 40, 50, 60}, v.Get("tom.age"))
|
||
deepCheckValue(assert, v, overrideLayer, []string{"tom", "age"}, []int{30, 40, 50, 60})
|
||
}
|
||
}
|
||
|
||
func overrideDefault(assert *assert.Assertions, firstPath string, firstValue interface{}, secondPath string, secondValue interface{}) *Viper {
|
||
return overrideFromLayer(defaultLayer, assert, firstPath, firstValue, secondPath, secondValue)
|
||
}
|
||
func override(assert *assert.Assertions, firstPath string, firstValue interface{}, secondPath string, secondValue interface{}) *Viper {
|
||
return overrideFromLayer(overrideLayer, assert, firstPath, firstValue, secondPath, secondValue)
|
||
}
|
||
|
||
// overrideFromLayer performs the sequential override and low-level checks.
|
||
//
|
||
// First assignment is made on layer l for path firstPath with value firstValue,
|
||
// the second one on the override layer (i.e., with the Set() function)
|
||
// for path secondPath with value secondValue.
|
||
//
|
||
// firstPath and secondPath can include an arbitrary number of dots to indicate
|
||
// a nested element.
|
||
//
|
||
// After each assignment, the value is checked, retrieved both by its full path
|
||
// and by its key sequence (successive maps).
|
||
func overrideFromLayer(l layer, assert *assert.Assertions, firstPath string, firstValue interface{}, secondPath string, secondValue interface{}) *Viper {
|
||
v := New()
|
||
firstKeys := strings.Split(firstPath, v.keyDelim)
|
||
if assert == nil ||
|
||
len(firstKeys) == 0 || len(firstKeys[0]) == 0 {
|
||
return v
|
||
}
|
||
|
||
// Set and check first value
|
||
switch l {
|
||
case defaultLayer:
|
||
v.SetDefault(firstPath, firstValue)
|
||
case overrideLayer:
|
||
v.Set(firstPath, firstValue)
|
||
default:
|
||
return v
|
||
}
|
||
assert.Equal(firstValue, v.Get(firstPath))
|
||
deepCheckValue(assert, v, l, firstKeys, firstValue)
|
||
|
||
// Override and check new value
|
||
secondKeys := strings.Split(secondPath, v.keyDelim)
|
||
if len(secondKeys) == 0 || len(secondKeys[0]) == 0 {
|
||
return v
|
||
}
|
||
v.Set(secondPath, secondValue)
|
||
assert.Equal(secondValue, v.Get(secondPath))
|
||
deepCheckValue(assert, v, overrideLayer, secondKeys, secondValue)
|
||
|
||
return v
|
||
}
|
||
|
||
// deepCheckValue checks that all given keys correspond to a valid path in the
|
||
// configuration map of the given layer, and that the final value equals the one given
|
||
func deepCheckValue(assert *assert.Assertions, v *Viper, l layer, keys []string, value interface{}) {
|
||
if assert == nil || v == nil ||
|
||
len(keys) == 0 || len(keys[0]) == 0 {
|
||
return
|
||
}
|
||
|
||
// init
|
||
var val interface{}
|
||
var ms string
|
||
switch l {
|
||
case defaultLayer:
|
||
val = v.defaults
|
||
ms = "v.defaults"
|
||
case overrideLayer:
|
||
val = v.override
|
||
ms = "v.override"
|
||
}
|
||
|
||
// loop through map
|
||
var m map[string]interface{}
|
||
err := false
|
||
for _, k := range keys {
|
||
if val == nil {
|
||
assert.Fail(fmt.Sprintf("%s is not a map[string]interface{}", ms))
|
||
return
|
||
}
|
||
|
||
// deep scan of the map to get the final value
|
||
switch val.(type) {
|
||
case map[interface{}]interface{}:
|
||
m = cast.ToStringMap(val)
|
||
case map[string]interface{}:
|
||
m = val.(map[string]interface{})
|
||
default:
|
||
assert.Fail(fmt.Sprintf("%s is not a map[string]interface{}", ms))
|
||
return
|
||
}
|
||
ms = ms + "[\"" + k + "\"]"
|
||
val = m[k]
|
||
}
|
||
if !err {
|
||
assert.Equal(value, val)
|
||
}
|
||
}
|