test all keyvalue and fix bugs

This commit is contained in:
2025-06-26 15:56:43 +01:00
parent 35b488e59d
commit 1cf2094634
2 changed files with 118 additions and 10 deletions

View File

@@ -23,21 +23,30 @@ func (kv KVSlice) Less(i, j int) bool { return kv[i].Key < kv[j].Key }
func (kv KVSlice) Swap(i, j int) { kv[i], kv[j] = kv[j], kv[i] }
// EnvKV turns a struct with `env` keys (used with go-simpler/env) into a standard formatted
// environment variable key/value pair list, one per line. Note you must dereference a pointer
// type to use this. This allows the composition of the config in this file with an extended
// form with a customized variant of realy to produce correct environment variables both read
// and write.
// environment variable key/value pair list, one per line. If a pointer to a struct is passed,
// it will be automatically dereferenced. This allows the composition of the config in this file
// with an extended form with a customized variant of realy to produce correct environment
// variables both read and write.
func EnvKV(cfg any) (m KVSlice) {
t := reflect.TypeOf(cfg)
// Handle pointer types by dereferencing them
v := reflect.ValueOf(cfg)
if v.Kind() == reflect.Ptr {
if v.IsNil() {
return // Return empty slice for nil pointers
}
return EnvKV(v.Elem().Interface())
}
t := v.Type()
for i := 0; i < t.NumField(); i++ {
k := t.Field(i).Tag.Get("env")
v := reflect.ValueOf(cfg).Field(i).Interface()
fieldValue := v.Field(i).Interface()
var val string
switch u := v.(type) {
switch u := fieldValue.(type) {
case string:
val = u
case int, int64, int32, uint64, uint32, bool, time.Duration:
val = fmt.Sprint(v)
val = fmt.Sprint(fieldValue)
case []string:
if len(u) > 0 {
val = strings.Join(u, ",")
@@ -52,7 +61,8 @@ func EnvKV(cfg any) (m KVSlice) {
return
}
// PrintEnv renders the key/values of a config.C to a provided io.Writer.
// PrintEnv renders the key/values of a config struct to a provided io.Writer.
// If a pointer to a struct is passed, it will be automatically dereferenced.
func PrintEnv(cfg any, printer io.Writer) {
_, _ = fmt.Fprintln(printer, "#!/usr/bin/env bash")
kvs := EnvKV(cfg)

View File

@@ -90,10 +90,58 @@ func TestEnvKV(t *testing.T) {
if !found {
t.Errorf("Expected to find ADDITIONAL_FIELD with value 'additional' in embedded struct")
}
// Test case 3: Pointer to a struct
pointerStruct := &TestStruct{
StringField: "pointer-string",
IntField: 123,
BoolField: false,
DurationField: 10 * time.Second,
StringSlice: []string{"four", "five", "six"},
}
pointerKVs := EnvKV(pointerStruct)
// Expected key-value pairs for pointer struct
expectedPointer := KVSlice{
{Key: "STRING_FIELD", Value: "pointer-string"},
{Key: "INT_FIELD", Value: "123"},
{Key: "BOOL_FIELD", Value: "false"},
{Key: "DURATION_FIELD", Value: "10s"},
{Key: "STRING_SLICE", Value: "four,five,six"},
{Key: "EMPTY_SLICE", Value: ""},
}
// Check if the number of key-value pairs is correct
if len(pointerKVs) != len(expectedPointer) {
t.Errorf("Expected %d key-value pairs for pointer struct, got %d", len(expectedPointer), len(pointerKVs))
}
// Create a map for easier comparison
pointerKVMap := make(map[string]string)
for _, kv := range pointerKVs {
pointerKVMap[kv.Key] = kv.Value
}
// Check if all expected key-value pairs are present
for _, kv := range expectedPointer {
if val, ok := pointerKVMap[kv.Key]; !ok || val != kv.Value {
t.Errorf("Expected key %s with value %s for pointer struct, got %s", kv.Key, kv.Value, val)
}
}
// Test case 4: Nil pointer
var nilPointer *TestStruct = nil
nilKVs := EnvKV(nilPointer)
// Nil pointer should return empty slice
if len(nilKVs) != 0 {
t.Errorf("Expected empty slice for nil pointer, got %d items", len(nilKVs))
}
}
func TestPrintEnv(t *testing.T) {
// Create a test struct
// Test case 1: Regular struct
testStruct := TestStruct{
StringField: "test-string",
IntField: 42,
@@ -123,6 +171,56 @@ func TestPrintEnv(t *testing.T) {
if output != expected {
t.Errorf("PrintEnv output does not match expected.\nExpected:\n%s\nGot:\n%s", expected, output)
}
// Test case 2: Pointer to struct
pointerStruct := &TestStruct{
StringField: "pointer-string",
IntField: 123,
BoolField: false,
DurationField: 10 * time.Second,
StringSlice: []string{"four", "five", "six"},
}
// Create a new buffer for the pointer test
var pointerBuf bytes.Buffer
// Call PrintEnv with a pointer
PrintEnv(pointerStruct, &pointerBuf)
// Get the output as a string
pointerOutput := pointerBuf.String()
// Expected output for pointer struct
expectedPointer := "#!/usr/bin/env bash\n" +
"export BOOL_FIELD=false\n" +
"export DURATION_FIELD=10s\n" +
"export EMPTY_SLICE=\n" +
"export INT_FIELD=123\n" +
"export STRING_FIELD=pointer-string\n" +
"export STRING_SLICE=four,five,six\n"
if pointerOutput != expectedPointer {
t.Errorf("PrintEnv output for pointer struct does not match expected.\nExpected:\n%s\nGot:\n%s",
expectedPointer, pointerOutput)
}
// Test case 3: Nil pointer
var nilPointer *TestStruct = nil
var nilBuf bytes.Buffer
// Call PrintEnv with a nil pointer
PrintEnv(nilPointer, &nilBuf)
// Get the output as a string
nilOutput := nilBuf.String()
// Expected output for nil pointer (just the shebang line)
expectedNil := "#!/usr/bin/env bash\n"
if nilOutput != expectedNil {
t.Errorf("PrintEnv output for nil pointer does not match expected.\nExpected:\n%s\nGot:\n%s",
expectedNil, nilOutput)
}
}
func TestKVSliceSorting(t *testing.T) {