fix: ensure proper handling of JSON unmarshalling for maps in config update
This commit is contained in:
@@ -252,8 +252,16 @@ func updateConfigFromMap(config interface{}, configMap map[string]string) error
|
||||
continue
|
||||
}
|
||||
}
|
||||
case reflect.Map, reflect.Slice, reflect.Struct:
|
||||
// 复杂类型使用JSON反序列化
|
||||
case reflect.Map:
|
||||
// json.Unmarshal merges into existing maps (keeps old keys that are
|
||||
// absent from the new JSON). Allocate a fresh map so removed keys
|
||||
// are properly cleared.
|
||||
fresh := reflect.New(field.Type())
|
||||
if err := json.Unmarshal([]byte(strValue), fresh.Interface()); err != nil {
|
||||
continue
|
||||
}
|
||||
field.Set(fresh.Elem())
|
||||
case reflect.Slice, reflect.Struct:
|
||||
err := json.Unmarshal([]byte(strValue), field.Addr().Interface())
|
||||
if err != nil {
|
||||
continue
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
type testConfigWithMap struct {
|
||||
Modes map[string]string `json:"modes"`
|
||||
Exprs map[string]string `json:"exprs"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
func TestUpdateConfigFromMap_MapReplacement(t *testing.T) {
|
||||
cfg := &testConfigWithMap{
|
||||
Modes: map[string]string{
|
||||
"model-a": "tiered_expr",
|
||||
"model-b": "tiered_expr",
|
||||
},
|
||||
Exprs: map[string]string{
|
||||
"model-a": "p * 5 + c * 25",
|
||||
"model-b": "p * 10 + c * 50",
|
||||
},
|
||||
Name: "billing",
|
||||
}
|
||||
|
||||
// Simulate removing model-a: new value only has model-b
|
||||
err := UpdateConfigFromMap(cfg, map[string]string{
|
||||
"modes": `{"model-b": "tiered_expr"}`,
|
||||
"exprs": `{"model-b": "p * 10 + c * 50"}`,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("UpdateConfigFromMap failed: %v", err)
|
||||
}
|
||||
|
||||
if _, ok := cfg.Modes["model-a"]; ok {
|
||||
t.Errorf("Modes still contains model-a after it was removed from the update; got %v", cfg.Modes)
|
||||
}
|
||||
if _, ok := cfg.Exprs["model-a"]; ok {
|
||||
t.Errorf("Exprs still contains model-a after it was removed from the update; got %v", cfg.Exprs)
|
||||
}
|
||||
|
||||
if cfg.Modes["model-b"] != "tiered_expr" {
|
||||
t.Errorf("Modes[model-b] = %q, want %q", cfg.Modes["model-b"], "tiered_expr")
|
||||
}
|
||||
if cfg.Exprs["model-b"] != "p * 10 + c * 50" {
|
||||
t.Errorf("Exprs[model-b] = %q, want %q", cfg.Exprs["model-b"], "p * 10 + c * 50")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateConfigFromMap_EmptyMapClearsAll(t *testing.T) {
|
||||
cfg := &testConfigWithMap{
|
||||
Modes: map[string]string{
|
||||
"model-a": "tiered_expr",
|
||||
},
|
||||
Exprs: map[string]string{
|
||||
"model-a": "p * 5 + c * 25",
|
||||
},
|
||||
}
|
||||
|
||||
err := UpdateConfigFromMap(cfg, map[string]string{
|
||||
"modes": `{}`,
|
||||
"exprs": `{}`,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("UpdateConfigFromMap failed: %v", err)
|
||||
}
|
||||
|
||||
if len(cfg.Modes) != 0 {
|
||||
t.Errorf("Modes should be empty after updating with {}, got %v", cfg.Modes)
|
||||
}
|
||||
if len(cfg.Exprs) != 0 {
|
||||
t.Errorf("Exprs should be empty after updating with {}, got %v", cfg.Exprs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateConfigFromMap_ScalarFieldsUnchanged(t *testing.T) {
|
||||
cfg := &testConfigWithMap{
|
||||
Modes: map[string]string{"m": "v"},
|
||||
Name: "old",
|
||||
}
|
||||
|
||||
err := UpdateConfigFromMap(cfg, map[string]string{
|
||||
"name": "new",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("UpdateConfigFromMap failed: %v", err)
|
||||
}
|
||||
|
||||
if cfg.Name != "new" {
|
||||
t.Errorf("Name = %q, want %q", cfg.Name, "new")
|
||||
}
|
||||
// modes was not in configMap, should remain unchanged
|
||||
if cfg.Modes["m"] != "v" {
|
||||
t.Errorf("Modes should be unchanged, got %v", cfg.Modes)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user