Add unit tests for system_settings.go map utility functions

52 test cases covering:
- firstValueForKeys: 18 cases for multi-key lookup with priority ordering
- hasAnyKey: 15 cases for key existence checking
- discoveryConfigMap: 19 cases for nested config extraction with camelCase/snake_case support

Tests verify edge cases including nil maps, empty key slices, type assertion failures,
and priority ordering for field name variants used in configuration parsing.
This commit is contained in:
rcourtman
2025-12-01 01:05:07 +00:00
parent 67bf3816ba
commit bd3e5e44e6

View File

@@ -0,0 +1,516 @@
package api
import (
"testing"
)
func TestFirstValueForKeys(t *testing.T) {
t.Parallel()
tests := []struct {
name string
m map[string]interface{}
keys []string
wantVal interface{}
wantBool bool
}{
// Basic functionality
{
name: "single key found",
m: map[string]interface{}{"foo": "bar"},
keys: []string{"foo"},
wantVal: "bar",
wantBool: true,
},
{
name: "single key not found",
m: map[string]interface{}{"foo": "bar"},
keys: []string{"baz"},
wantVal: nil,
wantBool: false,
},
{
name: "first of multiple keys found",
m: map[string]interface{}{"camelCase": "value1", "snake_case": "value2"},
keys: []string{"camelCase", "snake_case"},
wantVal: "value1",
wantBool: true,
},
{
name: "second key found when first missing",
m: map[string]interface{}{"snake_case": "value2"},
keys: []string{"camelCase", "snake_case"},
wantVal: "value2",
wantBool: true,
},
{
name: "third key found when first two missing",
m: map[string]interface{}{"third": "value3"},
keys: []string{"first", "second", "third"},
wantVal: "value3",
wantBool: true,
},
// Edge cases
{
name: "nil map",
m: nil,
keys: []string{"foo"},
wantVal: nil,
wantBool: false,
},
{
name: "empty map",
m: map[string]interface{}{},
keys: []string{"foo"},
wantVal: nil,
wantBool: false,
},
{
name: "empty keys slice",
m: map[string]interface{}{"foo": "bar"},
keys: []string{},
wantVal: nil,
wantBool: false,
},
{
name: "nil keys slice",
m: map[string]interface{}{"foo": "bar"},
keys: nil,
wantVal: nil,
wantBool: false,
},
{
name: "empty string key",
m: map[string]interface{}{"": "empty-key-value"},
keys: []string{""},
wantVal: "empty-key-value",
wantBool: true,
},
// Value types
{
name: "nil value is found",
m: map[string]interface{}{"foo": nil},
keys: []string{"foo"},
wantVal: nil,
wantBool: true,
},
{
name: "integer value",
m: map[string]interface{}{"count": 42},
keys: []string{"count"},
wantVal: 42,
wantBool: true,
},
{
name: "float value",
m: map[string]interface{}{"ratio": 3.14},
keys: []string{"ratio"},
wantVal: 3.14,
wantBool: true,
},
{
name: "bool value",
m: map[string]interface{}{"enabled": true},
keys: []string{"enabled"},
wantVal: true,
wantBool: true,
},
{
name: "nested map value",
m: map[string]interface{}{"nested": map[string]interface{}{"inner": "data"}},
keys: []string{"nested"},
wantVal: map[string]interface{}{"inner": "data"},
wantBool: true,
},
{
name: "slice value",
m: map[string]interface{}{"items": []string{"a", "b", "c"}},
keys: []string{"items"},
wantVal: []string{"a", "b", "c"},
wantBool: true,
},
// Priority order matters
{
name: "priority prefers first key even if second also exists",
m: map[string]interface{}{"a": "first", "b": "second"},
keys: []string{"a", "b"},
wantVal: "first",
wantBool: true,
},
{
name: "priority skips nil value keys - nil value is still returned",
m: map[string]interface{}{"a": nil, "b": "second"},
keys: []string{"a", "b"},
wantVal: nil,
wantBool: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotVal, gotBool := firstValueForKeys(tt.m, tt.keys...)
if gotBool != tt.wantBool {
t.Errorf("firstValueForKeys() bool = %v, want %v", gotBool, tt.wantBool)
}
// For comparing interface{} values
if !equalInterface(gotVal, tt.wantVal) {
t.Errorf("firstValueForKeys() val = %v (%T), want %v (%T)", gotVal, gotVal, tt.wantVal, tt.wantVal)
}
})
}
}
func TestHasAnyKey(t *testing.T) {
t.Parallel()
tests := []struct {
name string
m map[string]interface{}
keys []string
want bool
}{
// Basic functionality
{
name: "single key exists",
m: map[string]interface{}{"foo": "bar"},
keys: []string{"foo"},
want: true,
},
{
name: "single key does not exist",
m: map[string]interface{}{"foo": "bar"},
keys: []string{"baz"},
want: false,
},
{
name: "first of multiple keys exists",
m: map[string]interface{}{"first": "value"},
keys: []string{"first", "second"},
want: true,
},
{
name: "second of multiple keys exists",
m: map[string]interface{}{"second": "value"},
keys: []string{"first", "second"},
want: true,
},
{
name: "last of multiple keys exists",
m: map[string]interface{}{"third": "value"},
keys: []string{"first", "second", "third"},
want: true,
},
{
name: "none of multiple keys exist",
m: map[string]interface{}{"other": "value"},
keys: []string{"first", "second", "third"},
want: false,
},
// Edge cases
{
name: "nil map",
m: nil,
keys: []string{"foo"},
want: false,
},
{
name: "empty map",
m: map[string]interface{}{},
keys: []string{"foo"},
want: false,
},
{
name: "empty keys slice",
m: map[string]interface{}{"foo": "bar"},
keys: []string{},
want: false,
},
{
name: "nil keys slice",
m: map[string]interface{}{"foo": "bar"},
keys: nil,
want: false,
},
{
name: "empty string key exists",
m: map[string]interface{}{"": "empty"},
keys: []string{""},
want: true,
},
// Key exists with nil value
{
name: "key with nil value counts as existing",
m: map[string]interface{}{"foo": nil},
keys: []string{"foo"},
want: true,
},
// Real-world usage: camelCase vs snake_case
{
name: "discoveryConfig camelCase exists",
m: map[string]interface{}{"discoveryConfig": map[string]interface{}{}},
keys: []string{"discoveryConfig", "discovery_config"},
want: true,
},
{
name: "discovery_config snake_case exists",
m: map[string]interface{}{"discovery_config": map[string]interface{}{}},
keys: []string{"discoveryConfig", "discovery_config"},
want: true,
},
{
name: "neither naming convention exists",
m: map[string]interface{}{"otherConfig": map[string]interface{}{}},
keys: []string{"discoveryConfig", "discovery_config"},
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := hasAnyKey(tt.m, tt.keys...); got != tt.want {
t.Errorf("hasAnyKey() = %v, want %v", got, tt.want)
}
})
}
}
func TestDiscoveryConfigMap(t *testing.T) {
t.Parallel()
tests := []struct {
name string
raw map[string]interface{}
wantMap map[string]interface{}
wantBool bool
}{
// CamelCase variant
{
name: "discoveryConfig with valid map",
raw: map[string]interface{}{
"discoveryConfig": map[string]interface{}{
"enabled": true,
"subnet": "192.168.1.0/24",
},
},
wantMap: map[string]interface{}{"enabled": true, "subnet": "192.168.1.0/24"},
wantBool: true,
},
{
name: "discoveryConfig with empty map",
raw: map[string]interface{}{
"discoveryConfig": map[string]interface{}{},
},
wantMap: map[string]interface{}{},
wantBool: true,
},
// Snake_case variant
{
name: "discovery_config with valid map",
raw: map[string]interface{}{
"discovery_config": map[string]interface{}{
"enabled": false,
"timeout": 30,
},
},
wantMap: map[string]interface{}{"enabled": false, "timeout": 30},
wantBool: true,
},
{
name: "discovery_config with empty map",
raw: map[string]interface{}{
"discovery_config": map[string]interface{}{},
},
wantMap: map[string]interface{}{},
wantBool: true,
},
// Priority: camelCase over snake_case
{
name: "camelCase takes priority over snake_case",
raw: map[string]interface{}{
"discoveryConfig": map[string]interface{}{"from": "camel"},
"discovery_config": map[string]interface{}{"from": "snake"},
},
wantMap: map[string]interface{}{"from": "camel"},
wantBool: true,
},
// Not found
{
name: "neither key exists",
raw: map[string]interface{}{"otherKey": "value"},
wantMap: nil,
wantBool: false,
},
{
name: "empty raw map",
raw: map[string]interface{}{},
wantMap: nil,
wantBool: false,
},
{
name: "nil raw map",
raw: nil,
wantMap: nil,
wantBool: false,
},
// Type assertion failures - key exists but not a map
{
name: "discoveryConfig is string not map",
raw: map[string]interface{}{
"discoveryConfig": "not-a-map",
},
wantMap: nil,
wantBool: true, // key found, but type assertion failed
},
{
name: "discoveryConfig is int not map",
raw: map[string]interface{}{
"discoveryConfig": 42,
},
wantMap: nil,
wantBool: true,
},
{
name: "discoveryConfig is slice not map",
raw: map[string]interface{}{
"discoveryConfig": []string{"a", "b"},
},
wantMap: nil,
wantBool: true,
},
{
name: "discoveryConfig is nil",
raw: map[string]interface{}{
"discoveryConfig": nil,
},
wantMap: nil,
wantBool: true,
},
{
name: "discovery_config is string not map",
raw: map[string]interface{}{
"discovery_config": "also-not-a-map",
},
wantMap: nil,
wantBool: true,
},
{
name: "discovery_config is bool not map",
raw: map[string]interface{}{
"discovery_config": true,
},
wantMap: nil,
wantBool: true,
},
// Nested map structure
{
name: "deeply nested config values",
raw: map[string]interface{}{
"discoveryConfig": map[string]interface{}{
"nested": map[string]interface{}{
"deep": "value",
},
"array": []int{1, 2, 3},
},
},
wantMap: map[string]interface{}{
"nested": map[string]interface{}{"deep": "value"},
"array": []int{1, 2, 3},
},
wantBool: true,
},
// Edge case: wrong type map (not map[string]interface{})
{
name: "discoveryConfig is map[string]string not map[string]interface{}",
raw: map[string]interface{}{
"discoveryConfig": map[string]string{"key": "value"},
},
wantMap: nil,
wantBool: true, // key found, type assertion to map[string]interface{} fails
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotMap, gotBool := discoveryConfigMap(tt.raw)
if gotBool != tt.wantBool {
t.Errorf("discoveryConfigMap() bool = %v, want %v", gotBool, tt.wantBool)
}
if !equalMapInterface(gotMap, tt.wantMap) {
t.Errorf("discoveryConfigMap() map = %v, want %v", gotMap, tt.wantMap)
}
})
}
}
// equalInterface compares two interface{} values for equality
func equalInterface(a, b interface{}) bool {
if a == nil && b == nil {
return true
}
if a == nil || b == nil {
return false
}
// Handle map[string]interface{} specially
aMap, aIsMap := a.(map[string]interface{})
bMap, bIsMap := b.(map[string]interface{})
if aIsMap && bIsMap {
return equalMapInterface(aMap, bMap)
}
// Handle []string specially
aSlice, aIsSlice := a.([]string)
bSlice, bIsSlice := b.([]string)
if aIsSlice && bIsSlice {
if len(aSlice) != len(bSlice) {
return false
}
for i := range aSlice {
if aSlice[i] != bSlice[i] {
return false
}
}
return true
}
// Handle []int specially
aIntSlice, aIsIntSlice := a.([]int)
bIntSlice, bIsIntSlice := b.([]int)
if aIsIntSlice && bIsIntSlice {
if len(aIntSlice) != len(bIntSlice) {
return false
}
for i := range aIntSlice {
if aIntSlice[i] != bIntSlice[i] {
return false
}
}
return true
}
// Default comparison (may panic for uncomparable types, but those should be handled above)
return a == b
}
// equalMapInterface compares two map[string]interface{} values
func equalMapInterface(a, b map[string]interface{}) bool {
if a == nil && b == nil {
return true
}
if a == nil || b == nil {
return false
}
if len(a) != len(b) {
return false
}
for k, v := range a {
bv, ok := b[k]
if !ok {
return false
}
if !equalInterface(v, bv) {
return false
}
}
return true
}