blob: adc8c65a432594db60717248b28092d686c02390 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package config
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"reflect"
"testing"
"github.com/stretchr/testify/require"
)
func TestSaveConfig(t *testing.T) {
// Create a temporary directory for testing
tempDir := t.TempDir()
configMap, err := NewConfigMapBuilder().WithConfigJson(filepath.Join(tempDir, "config.json")).Build()
require.NoError(t, err)
err = configMap.Insert("key", "value")
require.NoError(t, err)
err = configMap.SaveConfig()
require.NoError(t, err)
// Read the saved file
savedJSON, err := os.ReadFile(configMap.configPath)
require.NoError(t, err)
// Unmarshal the JSON
var savedConfig map[string]interface{}
err = json.Unmarshal(savedJSON, &savedConfig)
require.NoError(t, err)
// Verify the saved config matches the original config
require.Equal(t, configMap.config, savedConfig)
}
func TestInsert(t *testing.T) {
testCases := []struct {
name string
startingMap ConfigMap
key string
value string
expected ConfigMap
err error
}{
{
name: "empty map",
startingMap: ConfigMap{
config: map[string]interface{}{},
},
key: "KEY",
value: "value",
expected: ConfigMap{
config: map[string]interface{}{
"key": "value",
},
},
err: nil,
},
{
name: "map with key",
startingMap: ConfigMap{
config: map[string]interface{}{
"key": "value",
},
},
key: "KEY2",
value: "value2",
expected: ConfigMap{
config: map[string]interface{}{
"key": "value",
"key2": "value2",
},
},
err: nil,
},
{
name: "map with nested key",
startingMap: ConfigMap{
config: map[string]interface{}{
"key": map[string]interface{}{
"key": "value",
},
},
},
key: "KEY_OTHER",
value: "value2",
expected: ConfigMap{
config: map[string]interface{}{
"key": map[string]interface{}{
"key": "value",
"other": "value2",
},
},
},
err: nil,
},
{
name: "existing key is overwritten",
startingMap: ConfigMap{
config: map[string]interface{}{
"key": "value",
},
},
key: "KEY",
value: "value2",
expected: ConfigMap{
config: map[string]interface{}{
"key": "value2",
},
},
err: nil,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err := tc.startingMap.Insert(tc.key, tc.value)
assertExpectedError(t, err, tc.err)
if !reflect.DeepEqual(tc.startingMap, tc.expected) {
t.Errorf("expected '%v' but got '%v'", tc.expected, tc.startingMap)
}
})
}
}
func TestGet(t *testing.T) {
testCases := []struct {
name string
startingMap ConfigMap
key string
expected string
err error
}{
{
name: "empty maps",
startingMap: ConfigMap{
opsRootConfig: map[string]interface{}{},
config: map[string]interface{}{},
},
key: "KEY",
expected: "",
err: fmt.Errorf("invalid key: '%s' - key does not exist", "KEY"),
},
{
name: "config map with key",
startingMap: ConfigMap{
opsRootConfig: map[string]interface{}{},
config: map[string]interface{}{
"key": "value",
},
},
key: "KEY",
expected: "value",
err: nil,
},
{
name: "ops root config with key",
startingMap: ConfigMap{
opsRootConfig: map[string]interface{}{
"key": "value",
},
config: map[string]interface{}{},
},
key: "KEY",
expected: "value",
err: nil,
},
{
name: "map with nested key",
startingMap: ConfigMap{
config: map[string]interface{}{
"nested": map[string]interface{}{
"key": "value",
},
},
},
key: "NESTED_KEY",
expected: "value",
err: nil,
},
{
name: "map with both ops root and config keys",
startingMap: ConfigMap{
opsRootConfig: map[string]interface{}{
"key": "value",
"key2": "value2",
},
config: map[string]interface{}{
"key": "value2",
"different": "value3",
},
},
key: "KEY",
expected: "value2",
err: nil,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
value, err := tc.startingMap.Get(tc.key)
assertExpectedError(t, err, tc.err)
if value != tc.expected {
t.Errorf("expected '%s' but got '%s'", tc.expected, value)
}
})
}
}
func TestFlatten(t *testing.T) {
testCases := []struct {
name string
input ConfigMap
expected map[string]string
}{
{
name: "empty map",
input: ConfigMap{
config: map[string]interface{}{},
opsRootConfig: map[string]interface{}{},
},
expected: map[string]string{},
},
{
name: "one key map",
input: ConfigMap{
opsRootConfig: map[string]interface{}{},
config: map[string]interface{}{
"key": "value",
},
},
expected: map[string]string{
"KEY": "value",
},
},
{
name: "nested map",
input: ConfigMap{
config: map[string]interface{}{
"key": map[string]interface{}{
"key": "value",
},
},
},
expected: map[string]string{
"KEY_KEY": "value",
},
},
{
name: "nested map with multiple keys",
input: ConfigMap{
opsRootConfig: map[string]interface{}{
"different": "value",
"key": "value0",
},
config: map[string]interface{}{
"key": map[string]interface{}{
"key": "value1",
"other": "value2",
},
},
},
expected: map[string]string{
"DIFFERENT": "value",
"KEY_KEY": "value1",
"KEY_OTHER": "value2",
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
flattened := tc.input.Flatten()
if !reflect.DeepEqual(flattened, tc.expected) {
t.Errorf("expected '%v' but got '%v'", tc.expected, flattened)
}
})
}
}
func TestDelete(t *testing.T) {
emptyMap := ConfigMap{
opsRootConfig: map[string]interface{}{},
config: map[string]interface{}{},
}
testCases := []struct {
name string
startingMap ConfigMap
key string
expected ConfigMap
err error
}{
{
name: "empty map",
startingMap: emptyMap,
key: "KEY",
expected: emptyMap,
err: fmt.Errorf("invalid key: '%s' - key does not exist in config.json", "KEY"),
},
{
name: "map with key",
startingMap: ConfigMap{
opsRootConfig: map[string]interface{}{},
config: map[string]interface{}{"key": "value"},
},
key: "KEY",
expected: emptyMap,
err: nil,
},
{
name: "map with nested key",
startingMap: ConfigMap{
opsRootConfig: map[string]interface{}{},
config: map[string]interface{}{
"nested": map[string]interface{}{
"key": "value",
},
},
},
key: "NESTED_KEY",
expected: ConfigMap{
opsRootConfig: map[string]interface{}{},
config: map[string]interface{}{},
},
err: nil,
},
{
name: "opsroot is ignored",
startingMap: ConfigMap{
opsRootConfig: map[string]interface{}{
"key": "value",
},
config: map[string]interface{}{},
},
key: "KEY",
expected: ConfigMap{
opsRootConfig: map[string]interface{}{
"key": "value",
},
config: map[string]interface{}{},
},
err: fmt.Errorf("invalid key: '%s' - key does not exist in config.json", "KEY"),
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err := tc.startingMap.Delete(tc.key)
assertExpectedError(t, err, tc.err)
if !reflect.DeepEqual(tc.startingMap, tc.expected) {
t.Errorf("expected '%v' but got '%v'", tc.expected, tc.startingMap)
}
})
}
}
func Test_parseKey(t *testing.T) {
testCases := []struct {
name string
input string
want []string
err error
}{
{
name: "Simple Key",
input: "foo",
want: []string{"foo"},
err: nil,
},
{
name: "Complex Key",
input: "foo_bar",
want: []string{"foo", "bar"},
err: nil,
},
{
name: "Complex Key 2",
input: "foo_bar_baz",
want: []string{"foo", "bar", "baz"},
err: nil,
},
{
name: "Invalid Key",
input: "foo_bar_baz_",
want: nil,
err: fmt.Errorf("invalid key: %s", "foo_bar_baz_"),
},
{
name: "Invalid Key 2",
input: "_foo_bar_baz",
want: nil,
err: fmt.Errorf("invalid key: %s", "_foo_bar_baz"),
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
got, err := parseKey(tc.input)
assertExpectedError(t, err, tc.err)
if !reflect.DeepEqual(tc.want, got) {
t.Errorf("Expected %v, got %v", tc.want, got)
}
})
}
}
func Test_parseValue(t *testing.T) {
testCases := []struct {
name string
input string
want interface{}
err error
}{
{
name: "String",
input: "foo",
want: "foo",
err: nil,
},
{
name: "Complex String",
input: "Another foo bar",
want: "Another foo bar",
err: nil,
},
{
name: "Number",
input: "123.456",
want: 123.456,
err: nil,
},
{
name: "Boolean True",
input: "true",
want: true,
err: nil,
},
{
name: "Boolean False",
input: "false",
want: false,
err: nil,
},
{
name: "Null",
input: "null",
want: nil,
err: nil,
},
{
name: "JSON",
input: `{"foo": "bar"}`,
want: map[string]interface{}{"foo": "bar"},
err: nil,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
got, err := parseValue(tc.input)
assertExpectedError(t, err, tc.err)
if !reflect.DeepEqual(tc.want, got) {
t.Errorf("Expected %v, got %v", tc.want, got)
}
})
}
}
func assertExpectedError(t *testing.T, err error, expected error) {
t.Helper()
if expected != nil {
if err == nil {
t.Fatalf("expected error '%s' but got nil", expected)
}
if err.Error() != expected.Error() {
t.Errorf("expected error '%s' but got '%s'", expected, err)
}
}
if expected == nil && err != nil {
t.Errorf("expected no error but got '%s'", err)
}
}
func Test_mergeMaps(t *testing.T) {
testCases := []struct {
name string
m1 map[string]interface{}
m2 map[string]interface{}
expected map[string]interface{}
}{
{
name: "m1 empty",
m1: map[string]interface{}{},
m2: map[string]interface{}{
"test": "test",
},
expected: map[string]interface{}{
"test": "test",
},
},
{
name: "m2 empty",
m1: map[string]interface{}{
"test": "test",
},
m2: map[string]interface{}{},
expected: map[string]interface{}{
"test": "test",
},
},
{
name: "m1 and m2 not empty",
m1: map[string]interface{}{
"test": "test",
},
m2: map[string]interface{}{
"test2": "test2",
},
expected: map[string]interface{}{
"test": "test",
"test2": "test2",
},
},
{
name: "m1 and m2 not empty with same key",
m1: map[string]interface{}{
"test": "test",
},
m2: map[string]interface{}{
"test": "test2",
},
expected: map[string]interface{}{
"test": "test2",
},
},
{
name: "m1 and m2 not empty with same key and nested map",
m1: map[string]interface{}{
"test": map[string]interface{}{
"test": "test",
},
},
m2: map[string]interface{}{
"test": map[string]interface{}{
"test2": "test2",
},
},
expected: map[string]interface{}{
"test": map[string]interface{}{
"test": "test",
"test2": "test2",
},
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
result := mergeMaps(tc.m1, tc.m2)
if !reflect.DeepEqual(result, tc.expected) {
t.Errorf("expected: %v, got: %v", tc.expected, result)
}
})
}
}