2016-10-08 08:00:18 +00:00
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 )
}
2020-09-11 15:48:38 +00:00
2016-10-08 08:00:18 +00:00
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
2023-07-27 18:56:32 +00:00
switch val := val . ( type ) {
2016-10-08 08:00:18 +00:00
case map [ interface { } ] interface { } :
m = cast . ToStringMap ( val )
case map [ string ] interface { } :
2023-07-27 18:56:32 +00:00
m = val
2016-10-08 08:00:18 +00:00
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 )
}
}