package confext import ( "git.blackforestbytes.com/BlackForestBytes/goext/tst" "testing" "time" ) func TestApplyEnvOverridesPrefix(t *testing.T) { type testdata struct { V1 int `env:"V1"` V2 string `env:"V2"` } data := testdata{V1: 1, V2: "x"} t.Setenv("MYAPP_V1", "42") t.Setenv("MYAPP_V2", "hello") t.Setenv("V1", "111") t.Setenv("V2", "noprefix") err := ApplyEnvOverrides("MYAPP_", &data, ".") tst.AssertNoErr(t, err) tst.AssertEqual(t, data.V1, 42) tst.AssertEqual(t, data.V2, "hello") } func TestApplyEnvOverridesUnexportedFieldsIgnored(t *testing.T) { type testdata struct { V1 int `env:"TEST_V1"` v2 int `env:"TEST_V2"` } data := testdata{V1: 1, v2: 2} t.Setenv("TEST_V1", "11") t.Setenv("TEST_V2", "22") err := ApplyEnvOverrides("", &data, ".") tst.AssertNoErr(t, err) tst.AssertEqual(t, data.V1, 11) tst.AssertEqual(t, data.v2, 2) } func TestApplyEnvOverridesNoEnvTagIgnored(t *testing.T) { type testdata struct { V1 int `env:"TEST_V1"` V2 int `` } data := testdata{V1: 1, V2: 2} t.Setenv("TEST_V1", "11") t.Setenv("V2", "22") err := ApplyEnvOverrides("", &data, ".") tst.AssertNoErr(t, err) tst.AssertEqual(t, data.V1, 11) tst.AssertEqual(t, data.V2, 2) } func TestApplyEnvOverridesDashTagIgnored(t *testing.T) { type testdata struct { V1 int `env:"TEST_V1"` V2 string `env:"-"` } data := testdata{V1: 1, V2: "no"} t.Setenv("TEST_V1", "11") t.Setenv("-", "yes") err := ApplyEnvOverrides("", &data, ".") tst.AssertNoErr(t, err) tst.AssertEqual(t, data.V1, 11) tst.AssertEqual(t, data.V2, "no") } func TestApplyEnvOverridesEnvNotSetKeepsValue(t *testing.T) { type testdata struct { V1 int `env:"NOT_SET_INT_KEY_XYZ"` V2 string `env:"NOT_SET_STR_KEY_XYZ"` } data := testdata{V1: 7, V2: "keep"} err := ApplyEnvOverrides("", &data, ".") tst.AssertNoErr(t, err) tst.AssertEqual(t, data.V1, 7) tst.AssertEqual(t, data.V2, "keep") } func TestApplyEnvOverridesBoolVariants(t *testing.T) { type testdata struct { B1 bool `env:"B1"` B2 bool `env:"B2"` B3 bool `env:"B3"` B4 bool `env:"B4"` B5 bool `env:"B5"` B6 bool `env:"B6"` } data := testdata{} t.Setenv("B1", "true") t.Setenv("B2", "false") t.Setenv("B3", "1") t.Setenv("B4", "0") t.Setenv("B5", " TRUE ") t.Setenv("B6", "FaLsE") err := ApplyEnvOverrides("", &data, ".") tst.AssertNoErr(t, err) tst.AssertEqual(t, data.B1, true) tst.AssertEqual(t, data.B2, false) tst.AssertEqual(t, data.B3, true) tst.AssertEqual(t, data.B4, false) tst.AssertEqual(t, data.B5, true) tst.AssertEqual(t, data.B6, false) } func TestApplyEnvOverridesInvalidIntReturnsError(t *testing.T) { type testdata struct { V1 int `env:"BAD_INT"` } data := testdata{} t.Setenv("BAD_INT", "not_a_number") err := ApplyEnvOverrides("", &data, ".") if err == nil { t.Errorf("expected error for invalid int, got nil") } } func TestApplyEnvOverridesInvalidInt8ReturnsError(t *testing.T) { type testdata struct { V1 int8 `env:"BAD_INT8"` } data := testdata{} t.Setenv("BAD_INT8", "9999") err := ApplyEnvOverrides("", &data, ".") if err == nil { t.Errorf("expected error for invalid int8, got nil") } } func TestApplyEnvOverridesInvalidInt32ReturnsError(t *testing.T) { type testdata struct { V1 int32 `env:"BAD_INT32"` } data := testdata{} t.Setenv("BAD_INT32", "not_an_int32") err := ApplyEnvOverrides("", &data, ".") if err == nil { t.Errorf("expected error for invalid int32, got nil") } } func TestApplyEnvOverridesInvalidInt64ReturnsError(t *testing.T) { type testdata struct { V1 int64 `env:"BAD_INT64"` } data := testdata{} t.Setenv("BAD_INT64", "not_an_int64") err := ApplyEnvOverrides("", &data, ".") if err == nil { t.Errorf("expected error for invalid int64, got nil") } } func TestApplyEnvOverridesInvalidDurationReturnsError(t *testing.T) { type testdata struct { V1 time.Duration `env:"BAD_DUR"` } data := testdata{} t.Setenv("BAD_DUR", "not_a_duration") err := ApplyEnvOverrides("", &data, ".") if err == nil { t.Errorf("expected error for invalid duration, got nil") } } func TestApplyEnvOverridesInvalidTimeReturnsError(t *testing.T) { type testdata struct { V1 time.Time `env:"BAD_TIME"` } data := testdata{} t.Setenv("BAD_TIME", "not_a_time") err := ApplyEnvOverrides("", &data, ".") if err == nil { t.Errorf("expected error for invalid time, got nil") } } func TestApplyEnvOverridesInvalidBoolReturnsError(t *testing.T) { type testdata struct { V1 bool `env:"BAD_BOOL"` } data := testdata{} t.Setenv("BAD_BOOL", "yesno") err := ApplyEnvOverrides("", &data, ".") if err == nil { t.Errorf("expected error for invalid bool, got nil") } } func TestApplyEnvOverridesUnsupportedTypeReturnsError(t *testing.T) { type testdata struct { V1 []int `env:"UNSUPPORTED"` } data := testdata{} t.Setenv("UNSUPPORTED", "1,2,3") err := ApplyEnvOverrides("", &data, ".") if err == nil { t.Errorf("expected error for unsupported type, got nil") } } func TestApplyEnvOverridesFloatUnsupportedReturnsError(t *testing.T) { type testdata struct { V1 float64 `env:"UNSUPPORTED_FLOAT"` } data := testdata{} t.Setenv("UNSUPPORTED_FLOAT", "1.5") err := ApplyEnvOverrides("", &data, ".") if err == nil { t.Errorf("expected error for float64, got nil") } } func TestApplyEnvOverridesPointerInvalidReturnsError(t *testing.T) { type testdata struct { V1 *int `env:"PTR_BAD_INT"` } data := testdata{} t.Setenv("PTR_BAD_INT", "not_a_number") err := ApplyEnvOverrides("", &data, ".") if err == nil { t.Errorf("expected error for invalid pointer int, got nil") } } func TestApplyEnvOverridesPointerNotSetStaysNil(t *testing.T) { type testdata struct { V1 *int `env:"PTR_NOT_SET_KEY_ABC"` V2 *string `env:"PTR_NOT_SET_KEY_DEF"` } data := testdata{} err := ApplyEnvOverrides("", &data, ".") tst.AssertNoErr(t, err) if data.V1 != nil { t.Errorf("expected V1 to remain nil, got %v", *data.V1) } if data.V2 != nil { t.Errorf("expected V2 to remain nil, got %v", *data.V2) } } func TestApplyEnvOverridesAliasBool(t *testing.T) { type aliasbool bool type testdata struct { V1 aliasbool `env:"ALIAS_BOOL"` } data := testdata{} t.Setenv("ALIAS_BOOL", "true") err := ApplyEnvOverrides("", &data, ".") tst.AssertNoErr(t, err) tst.AssertEqual(t, data.V1, aliasbool(true)) } func TestApplyEnvOverridesNestedRecursiveError(t *testing.T) { type subdata struct { V1 int `env:"V1"` } type testdata struct { Sub subdata `env:"SUB"` } data := testdata{} t.Setenv("SUB.V1", "not_a_number") err := ApplyEnvOverrides("", &data, ".") if err == nil { t.Errorf("expected error from nested struct invalid value, got nil") } } func TestApplyEnvOverridesTimeFieldInsideStructIsParsed(t *testing.T) { type testdata struct { T time.Time `env:"MYTIME"` } data := testdata{} t.Setenv("MYTIME", "2023-01-02T03:04:05Z") err := ApplyEnvOverrides("", &data, ".") tst.AssertNoErr(t, err) tst.AssertEqual(t, data.T.Equal(time.Date(2023, 1, 2, 3, 4, 5, 0, time.UTC)), true) } func TestApplyEnvOverridesPointerStringAlias(t *testing.T) { type aliasstr string type testdata struct { V1 *aliasstr `env:"PTR_ALIAS_STR"` } data := testdata{} t.Setenv("PTR_ALIAS_STR", "hello") err := ApplyEnvOverrides("", &data, ".") tst.AssertNoErr(t, err) if data.V1 == nil { t.Fatalf("expected V1 to be set") } tst.AssertEqual(t, *data.V1, aliasstr("hello")) } func TestApplyEnvOverridesEmptyEnvTagOnSubstruct(t *testing.T) { type subdata struct { V1 int `env:"INNER"` } type testdata struct { Sub subdata `env:""` } data := testdata{} t.Setenv("INNER", "55") err := ApplyEnvOverrides("PRE_", &data, "_") tst.AssertNoErr(t, err) tst.AssertEqual(t, data.Sub.V1, 0) t.Setenv("PRE_INNER", "77") err = ApplyEnvOverrides("PRE_", &data, "_") tst.AssertNoErr(t, err) tst.AssertEqual(t, data.Sub.V1, 77) }