Grafana/InfluxData style config overrides based on environment variables
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

205 lines
5.0 KiB

package influxStyleEnvOverride
import (
"encoding/json"
"reflect"
"strings"
"testing"
)
type ExampleObject struct {
A string
Other *ExampleSubObject
Others []ExampleSubObject
}
type ExampleSubObject struct {
Integer int
B string
unexported int
Other *ExampleSubObject
Thing interface{}
Things []interface{}
}
type mockKeyValueRetriever struct {
KeyValues map[string]string
}
func (this mockKeyValueRetriever) get(key string) string {
return this.KeyValues[key]
}
type testCase struct {
mutateExampleObject func(*ExampleObject)
environment map[string]string
expectedError string
}
func TestApplyEnvOverridesBasic(t *testing.T) {
toTest := testCase{
mutateExampleObject: func(example *ExampleObject) {
example.A = "asd2"
example.Other.B = "asd2"
example.Other.Integer = 10
things0 := example.Others[0].Things[0].(ExampleSubObject)
things0.B = "asd2"
example.Others[0].B = "asd2"
thing := example.Others[0].Thing.(ExampleSubObject)
thing.B = "asd2"
},
environment: map[string]string{
"TEST_A": "asd2",
"TEST_OTHER_B": "asd2",
"TEST_OTHER_INTEGER": "10",
"TEST_OTHERS_0_THINGS_0_B": "asd2",
"TEST_OTHERS_0_B": "asd2",
"TEST_OTHERS_0_THING_B": "asd2",
},
}
toTest.execute(t)
}
func TestApplyEnvOverridesWithInvalidInteger(t *testing.T) {
toTest := testCase{
mutateExampleObject: func(example *ExampleObject) {
},
environment: map[string]string{
"TEST_OTHER_INTEGER": "o no",
},
expectedError: "failed to apply TEST_OTHER_INTEGER to Integer",
}
toTest.execute(t)
}
func TestApplyEnvOverridesWithUnsettableField(t *testing.T) {
toTest := testCase{
mutateExampleObject: func(example *ExampleObject) {
},
environment: map[string]string{
"TEST_OTHERS_0_UNEXPORTED": "o no",
},
expectedError: "is not settable",
}
toTest.execute(t)
}
// Note currently this test fails. Haven't implemented additional slice elements yet.
//func TestApplyEnvOverridesWithNonExistentObject(t *testing.T) {
// toTest := testCase{
// mutateExampleObject: func(example *ExampleObject) {
// example.Others = append(example.Others, ExampleSubObject{
// B: "asd2",
// })
// },
// environment: map[string]string{
// "TEST_OTHERS_1_B": "asd2",
// },
// }
// toTest.execute(t)
//}
func TestApplyEnvOverridesWithWrongType(t *testing.T) {
exampleObjectUnderTest := newExampleObject()
var exampleObjectUnderTestInterface interface{}
exampleObjectUnderTestInterface = exampleObjectUnderTest
err := applyEnvOverrides(mockKeyValueRetriever{}, "TEST", reflect.ValueOf(&exampleObjectUnderTestInterface), 0)
expectedError := "expected a Struct"
actualErrorDisplay := "nil"
if err != nil {
actualErrorDisplay = err.Error()
}
if !strings.Contains(actualErrorDisplay, expectedError) {
t.Errorf("Expected Error: %s, Actual Error: %s", expectedError, actualErrorDisplay)
}
}
func TestApplyEnvOverridesWithNonDAG(t *testing.T) {
exampleObjectUnderTest := newExampleObject()
exampleObjectUnderTest.Other.Other = exampleObjectUnderTest.Other
err := applyEnvOverrides(mockKeyValueRetriever{}, "TEST", reflect.ValueOf(exampleObjectUnderTest), 0)
expectedError := "recursive overflow"
actualErrorDisplay := "nil"
if err != nil {
actualErrorDisplay = err.Error()
}
if !strings.Contains(actualErrorDisplay, expectedError) {
t.Errorf("Expected Error: %s, Actual Error: %s", expectedError, actualErrorDisplay)
}
}
func (this testCase) execute(t *testing.T) {
exampleObjectUnderTest := newExampleObject()
exampleObjectForComparison := newExampleObject()
exampleKeyValueRetriever := mockKeyValueRetriever{
KeyValues: this.environment,
}
err := applyEnvOverrides(exampleKeyValueRetriever, "TEST", reflect.ValueOf(&exampleObjectUnderTest), 0)
this.mutateExampleObject(&exampleObjectForComparison)
if err != nil || this.expectedError != "" {
actualError := ""
if err != nil {
actualError = err.Error()
}
if !strings.Contains(actualError, this.expectedError) || this.expectedError == "" {
expectedErrorDisplay := "nil"
if this.expectedError != "" {
expectedErrorDisplay = this.expectedError
}
t.Errorf("Expected Error: %s, Actual Error: %s", expectedErrorDisplay, actualError)
return
}
}
jsonA, err := json.MarshalIndent(exampleObjectUnderTest, "", " ")
if err != nil {
t.Error(err)
}
jsonB, err := json.MarshalIndent(exampleObjectForComparison, "", " ")
if err != nil {
t.Error(err)
}
if string(jsonA) != string(jsonB) {
t.Errorf("Expected Value: \n%s,\n Actual Value: \n%s\n\n", string(jsonB), string(jsonA))
}
}
func newExampleObject() ExampleObject {
toReturn := ExampleObject{
A: "asd",
Other: &ExampleSubObject{
B: "bsd",
Integer: 2,
},
Others: []ExampleSubObject{
ExampleSubObject{
B: "bsd",
Thing: ExampleSubObject{
B: "bsd",
},
},
},
}
toReturn.Others[0].Things = make([]interface{}, 0)
toReturn.Others[0].Things = append(
toReturn.Others[0].Things,
ExampleSubObject{
B: "bsd",
},
)
return toReturn
}