package confmgr import ( "testing" ) // TestParamTypeValid covers the valid/invalid branches of ParamType.valid. func TestParamTypeValid(t *testing.T) { for _, ok := range []ParamType{TypeFloat, TypeInt, TypeBool, TypeString, TypeEnum, TypeFloatArray} { if !ok.valid() { t.Errorf("%q should be valid", ok) } } if ParamType("bogus").valid() { t.Error("bogus type should be invalid") } } // TestCheckValue exercises every type branch of Parameter.checkValue, including // the type-mismatch and range-violation error paths. func TestCheckValue(t *testing.T) { cases := []struct { name string p Parameter v any wantErr bool }{ {"float ok", Parameter{Type: TypeFloat}, 1.5, false}, {"float from string", Parameter{Type: TypeFloat}, "2.5", false}, {"float not numeric", Parameter{Type: TypeFloat}, true, true}, {"float below min", Parameter{Type: TypeFloat, Min: fptr(0)}, -1.0, true}, {"float above max", Parameter{Type: TypeFloat, Max: fptr(10)}, 11.0, true}, {"int ok", Parameter{Type: TypeInt}, 4.0, false}, {"int non-integer", Parameter{Type: TypeInt}, 4.5, true}, {"bool ok", Parameter{Type: TypeBool}, true, false}, {"bool wrong type", Parameter{Type: TypeBool}, 1.0, true}, {"string ok", Parameter{Type: TypeString}, "hi", false}, {"string wrong type", Parameter{Type: TypeString}, 1.0, true}, {"enum ok", Parameter{Type: TypeEnum, EnumValues: []string{"a", "b"}}, "b", false}, {"enum not string", Parameter{Type: TypeEnum, EnumValues: []string{"a"}}, 1.0, true}, {"enum not allowed", Parameter{Type: TypeEnum, EnumValues: []string{"a"}}, "z", true}, {"array ok", Parameter{Type: TypeFloatArray}, []any{1.0, 2.0}, false}, {"array native", Parameter{Type: TypeFloatArray}, []float64{1, 2}, false}, {"array not array", Parameter{Type: TypeFloatArray}, 1.0, true}, {"array elem below min", Parameter{Type: TypeFloatArray, Min: fptr(0)}, []any{-1.0}, true}, {"array elem above max", Parameter{Type: TypeFloatArray, Max: fptr(5)}, []any{9.0}, true}, {"array bad elem", Parameter{Type: TypeFloatArray}, []any{"nope"}, true}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { err := tc.p.checkValue(tc.v) if tc.wantErr && err == nil { t.Errorf("checkValue(%v): want error", tc.v) } if !tc.wantErr && err != nil { t.Errorf("checkValue(%v): unexpected error %v", tc.v, err) } }) } } // TestValidateInvalidType covers the invalid-type branch of ConfigSet.Validate. func TestValidateInvalidType(t *testing.T) { set := ConfigSet{Name: "x", Parameters: []Parameter{ {Key: "a", DS: "d", Signal: "s", Type: ParamType("weird")}, }} if err := set.Validate(); err == nil { t.Error("invalid parameter type: want error") } } // TestValidateEnumRequiresValues covers the enum-without-values branch. func TestValidateEnumRequiresValues(t *testing.T) { set := ConfigSet{Name: "x", Parameters: []Parameter{ {Key: "a", DS: "d", Signal: "s", Type: TypeEnum}, }} if err := set.Validate(); err == nil { t.Error("enum without values: want error") } } // TestValidateMinGreaterThanMax covers the min>max branch. func TestValidateMinGreaterThanMax(t *testing.T) { set := ConfigSet{Name: "x", Parameters: []Parameter{ {Key: "a", DS: "d", Signal: "s", Type: TypeFloat, Min: fptr(10), Max: fptr(1)}, }} if err := set.Validate(); err == nil { t.Error("min>max: want error") } } // TestValidateAgainstMandatoryNoDefault covers the mandatory-without-default // branch of ValidateAgainst. func TestValidateAgainstMandatoryNoDefault(t *testing.T) { set := ConfigSet{Name: "x", Parameters: []Parameter{ {Key: "req", DS: "d", Signal: "s", Type: TypeFloat, Mandatory: true}, }} inst := ConfigInstance{SetID: "x", Values: map[string]any{}} if err := inst.ValidateAgainst(set); err == nil { t.Error("missing mandatory value: want error") } // Providing the value clears the error. inst.Values["req"] = 1.0 if err := inst.ValidateAgainst(set); err != nil { t.Errorf("with value: unexpected error %v", err) } } // TestResolveAndNormalize covers Resolve fallbacks and array normalization. func TestResolveAndNormalize(t *testing.T) { p := Parameter{Key: "v", Type: TypeFloat, Default: 7.0} inst := ConfigInstance{Values: map[string]any{}} if got, ok := inst.Resolve(p); !ok || got != 7.0 { t.Errorf("Resolve default: got %v,%v want 7,true", got, ok) } inst.Values["v"] = 3.0 if got, ok := inst.Resolve(p); !ok || got != 3.0 { t.Errorf("Resolve value: got %v,%v want 3,true", got, ok) } // Optional param without default → no value. if _, ok := inst.Resolve(Parameter{Key: "none", Type: TypeFloat}); ok { t.Error("Resolve no-default: want ok=false") } arr := Parameter{Type: TypeFloatArray} out := arr.normalize([]any{1.0, 2.0, 3.0}) if got, ok := out.([]float64); !ok || len(got) != 3 { t.Errorf("normalize array: got %T %v", out, out) } // Non-array passthrough. if got := arr.normalize("scalar"); got != "scalar" { t.Errorf("normalize passthrough: got %v", got) } }