internal/autogen/settingsmerge_test.go
Rama main · réplica de lectura del repositorio original.
package autogen
import (
"reflect"
"testing"
)
// TestSettingsPatch_AllFieldsArePointers is the guard behind MergeSettingsPatch.
// A value-typed field would look "set" at its zero value, so the merge would
// happily overwrite a stored setting with 0/""/false on every save from a
// section that doesn't model it. Adding such a field must fail here, loudly,
// rather than in a user's config a week later.
func TestSettingsPatch_AllFieldsArePointers(t *testing.T) {
rt := reflect.TypeOf(SettingsPatch{})
for i := 0; i < rt.NumField(); i++ {
f := rt.Field(i)
if f.Type.Kind() != reflect.Ptr {
t.Errorf("SettingsPatch.%s is %s, must be a pointer (see MergeSettingsPatch)", f.Name, f.Type)
}
}
}
// TestMergeSettingsPatch_CarriesForwardUntouchedFields is the regression the
// merge exists for: the dashboard saves in sections, so a patch carrying only
// the memory form's fields must leave the guard/advanced fields alone.
func TestMergeSettingsPatch_CarriesForwardUntouchedFields(t *testing.T) {
f := func(v float64) *float64 { return &v }
i := func(v int) *int { return &v }
b := func(v bool) *bool { return &v }
prev := SettingsPatch{
TargetVramGB: f(20),
OomGuardEvict: b(false),
OomGuardGraceSec: i(90),
ComputeBufFactor: f(1.3),
DryDefault: b(true),
TtlSec: i(300),
}
// What the memory form PUTs: its own four fields, nothing else.
next := SettingsPatch{
TargetVramGB: f(22),
VramOverheadGB: f(1.5),
MaxRamGB: f(32),
AutoVram: b(false),
}
got := MergeSettingsPatch(prev, next)
if got.TargetVramGB == nil || *got.TargetVramGB != 22 {
t.Errorf("TargetVramGB: the new value must win, got %v", got.TargetVramGB)
}
if got.OomGuardEvict == nil || *got.OomGuardEvict != false {
t.Errorf("OomGuardEvict was wiped by a memory-form save: %v", got.OomGuardEvict)
}
if got.OomGuardGraceSec == nil || *got.OomGuardGraceSec != 90 {
t.Errorf("OomGuardGraceSec was wiped by a memory-form save: %v", got.OomGuardGraceSec)
}
if got.ComputeBufFactor == nil || *got.ComputeBufFactor != 1.3 {
t.Errorf("ComputeBufFactor was wiped by a memory-form save: %v", got.ComputeBufFactor)
}
// The two fields that used to need bespoke carry-forward lines.
if got.DryDefault == nil || *got.DryDefault != true {
t.Errorf("DryDefault was wiped: %v", got.DryDefault)
}
if got.TtlSec == nil || *got.TtlSec != 300 {
t.Errorf("TtlSec was wiped: %v", got.TtlSec)
}
}
// TestMergeSettingsPatch_ClearedFieldStaysCleared: nil in prev and nil in next
// must stay nil, i.e. the merge never invents a value. That is what makes a
// per-section "restore defaults" (which nils the section's fields) work.
func TestMergeSettingsPatch_ClearedFieldStaysCleared(t *testing.T) {
got := MergeSettingsPatch(SettingsPatch{}, SettingsPatch{})
rv := reflect.ValueOf(got)
for i := 0; i < rv.NumField(); i++ {
if !rv.Field(i).IsNil() {
t.Errorf("field %s became non-nil from two empty patches", rv.Type().Field(i).Name)
}
}
}
// TestSettingsPatch_ApplyCoversEveryField keeps (*SettingsPatch).apply in step
// with the struct: every field must actually reach Settings. apply is written
// explicitly (Settings is flat values, not pointers), so a new patch field can
// be added and silently never applied — the setting would save to disk, survive
// a reload, and do nothing.
func TestSettingsPatch_ApplyCoversEveryField(t *testing.T) {
// Fill every pointer field with a non-zero value, apply, and assert Settings
// changed for each one in turn.
rt := reflect.TypeOf(SettingsPatch{})
for i := 0; i < rt.NumField(); i++ {
f := rt.Field(i)
p := SettingsPatch{}
pv := reflect.ValueOf(&p).Elem().Field(i)
elem := reflect.New(f.Type.Elem())
switch elem.Elem().Kind() {
case reflect.Float64:
elem.Elem().SetFloat(3.5)
case reflect.Int:
elem.Elem().SetInt(4242)
case reflect.Bool:
elem.Elem().SetBool(true)
case reflect.String:
elem.Elem().SetString("q8_0")
case reflect.Slice:
elem.Elem().Set(reflect.ValueOf([]int{4096}))
case reflect.Map:
m := reflect.MakeMap(elem.Elem().Type())
m.SetMapIndex(reflect.ValueOf("image"), reflect.ValueOf(`D:/loras`))
elem.Elem().Set(m)
default:
t.Fatalf("field %s has unhandled kind %s — extend this test", f.Name, elem.Elem().Kind())
}
pv.Set(elem)
var s Settings
p.apply(&s)
if reflect.DeepEqual(s, Settings{}) {
t.Errorf("(*SettingsPatch).apply ignores field %s — it would save but never take effect", f.Name)
}
}
}