first commit

This commit is contained in:
2026-08-14 23:41:57 +08:00
commit 086803a8dd
471 changed files with 91938 additions and 0 deletions
+234
View File
@@ -0,0 +1,234 @@
package config
import (
"os"
"path/filepath"
"reflect"
"testing"
)
// boolPtr / floatPtr are test helpers for the pointer-valued MemoryConfig fields.
func boolPtr(b bool) *bool { return &b }
func floatPtr(f float64) *float64 { return &f }
func TestMemoryConfig_ResolveDefaults(t *testing.T) {
got := MemoryConfig{}.Resolve()
want := ResolvedMemory{
Enabled: true,
ReconcileOnSearch: true,
SearchScoreFloor: 0.15,
CCIndex: false,
}
if got != want {
t.Fatalf("Resolve() defaults = %+v, want %+v", got, want)
}
}
func TestMemoryConfig_ResolveOverrides(t *testing.T) {
got := MemoryConfig{
Enabled: boolPtr(false),
ReconcileOnSearch: boolPtr(false),
SearchScoreFloor: floatPtr(0.5),
CCIndex: true,
}.Resolve()
want := ResolvedMemory{
Enabled: false,
ReconcileOnSearch: false,
SearchScoreFloor: 0.5,
CCIndex: true,
}
if got != want {
t.Fatalf("Resolve() overrides = %+v, want %+v", got, want)
}
}
// memory.enabled=false must be representable and distinct from "absent".
func TestMemoryConfig_EnabledFalseRepresentable(t *testing.T) {
if r := (MemoryConfig{Enabled: boolPtr(false)}).Resolve(); r.Enabled {
t.Fatal("explicit enabled=false should resolve to false")
}
if r := (MemoryConfig{}).Resolve(); !r.Enabled {
t.Fatal("absent enabled should default to true")
}
}
func TestMemoryConfig_ScoreFloorClamp(t *testing.T) {
if r := (MemoryConfig{SearchScoreFloor: floatPtr(-1)}).Resolve(); r.SearchScoreFloor != 0 {
t.Errorf("negative score floor should clamp to 0, got %v", r.SearchScoreFloor)
}
if r := (MemoryConfig{SearchScoreFloor: floatPtr(2)}).Resolve(); r.SearchScoreFloor != 1 {
t.Errorf("score floor >1 should clamp to 1, got %v", r.SearchScoreFloor)
}
}
func TestParseThresholdFraction(t *testing.T) {
cases := []struct {
in string
want float64
ok bool
}{
{"40%", 0.40, true},
{"60%", 0.60, true},
{"80%", 0.80, true},
{"100%", 1.0, true},
{" 75% ", 0.75, true},
{"0%", 0, false}, // out of range (must be >0)
{"120%", 0, false}, // out of range (>100%)
{"-10%", 0, false},
{"80", 0, false}, // missing %
{"abc%", 0, false},
}
for _, c := range cases {
got, ok := ParseThresholdFraction(c.in)
if ok != c.ok || (ok && got != c.want) {
t.Errorf("ParseThresholdFraction(%q) = (%v,%v), want (%v,%v)", c.in, got, ok, c.want, c.ok)
}
}
}
func TestCheckpointConfig_ResolveThresholds(t *testing.T) {
// Absent → defaults 40/60/80%.
if got := (CheckpointConfig{}).ResolveThresholds(); !reflect.DeepEqual(got, []float64{0.40, 0.60, 0.80}) {
t.Errorf("default thresholds = %v, want [0.4 0.6 0.8]", got)
}
// Explicit override.
if got := (CheckpointConfig{Thresholds: []string{"50%", "90%"}}).ResolveThresholds(); !reflect.DeepEqual(got, []float64{0.50, 0.90}) {
t.Errorf("override thresholds = %v, want [0.5 0.9]", got)
}
// Out-of-range entries skipped, valid kept.
if got := (CheckpointConfig{Thresholds: []string{"120%", "70%"}}).ResolveThresholds(); !reflect.DeepEqual(got, []float64{0.70}) {
t.Errorf("mixed thresholds = %v, want [0.7]", got)
}
// All invalid → fall back to defaults.
if got := (CheckpointConfig{Thresholds: []string{"nope", "0%"}}).ResolveThresholds(); !reflect.DeepEqual(got, []float64{0.40, 0.60, 0.80}) {
t.Errorf("all-invalid thresholds = %v, want defaults", got)
}
}
func TestParseMaxContext(t *testing.T) {
cases := []struct {
in string
set bool
window int
want int
}{
{"", false, 200000, 0},
{"300000", true, 200000, 300000},
{"300K", true, 200000, 300000},
{"1M", true, 200000, 1000000},
{"1m", true, 200000, 1000000},
{"1.5M", true, 200000, 1500000},
{"50%", true, 200000, 100000},
{"25%", true, 400000, 100000},
}
for _, c := range cases {
mc, err := ParseMaxContext(c.in)
if err != nil {
t.Errorf("ParseMaxContext(%q) unexpected error: %v", c.in, err)
continue
}
if mc.IsSet() != c.set {
t.Errorf("ParseMaxContext(%q).IsSet() = %v, want %v", c.in, mc.IsSet(), c.set)
}
if got := mc.Resolve(c.window); got != c.want {
t.Errorf("ParseMaxContext(%q).Resolve(%d) = %d, want %d", c.in, c.window, got, c.want)
}
}
}
func TestParseMaxContext_Invalid(t *testing.T) {
for _, in := range []string{"0%", "150%", "-100%", "abc", "0", "-5", "12x"} {
if _, err := ParseMaxContext(in); err == nil {
t.Errorf("ParseMaxContext(%q) should error", in)
}
}
}
func TestLoadFileConfig_MemoryTables(t *testing.T) {
path := filepath.Join(t.TempDir(), "config.toml")
content := `
[memory]
enabled = false
reconcile_on_search = false
search_score_floor = 0.3
cc_index = true
[checkpoint]
thresholds = ["50%", "70%"]
reserved = 4096
[checkpoint.push_caps]
memory = 800
recall = 1200
[compaction]
max_context = "300K"
`
if err := os.WriteFile(path, []byte(content), 0o600); err != nil {
t.Fatal(err)
}
cfg, err := LoadFileConfig(path)
if err != nil {
t.Fatalf("LoadFileConfig: %v", err)
}
mem := cfg.Memory.Resolve()
if mem.Enabled || mem.ReconcileOnSearch || !mem.CCIndex || mem.SearchScoreFloor != 0.3 {
t.Errorf("memory resolved = %+v", mem)
}
if got := cfg.Checkpoint.ResolveThresholds(); !reflect.DeepEqual(got, []float64{0.50, 0.70}) {
t.Errorf("thresholds = %v, want [0.5 0.7]", got)
}
if !cfg.Checkpoint.Reserved.Set || !cfg.Checkpoint.Reserved.IsInt || cfg.Checkpoint.Reserved.Int != 4096 {
t.Errorf("reserved = %+v, want int 4096", cfg.Checkpoint.Reserved)
}
if cfg.Checkpoint.PushCaps["memory"] != 800 || cfg.Checkpoint.PushCaps["recall"] != 1200 {
t.Errorf("push_caps = %v", cfg.Checkpoint.PushCaps)
}
mc, err := cfg.Compaction.ResolveMaxContext()
if err != nil {
t.Fatalf("ResolveMaxContext: %v", err)
}
if got := mc.Resolve(200000); got != 300000 {
t.Errorf("max_context resolve = %d, want 300000", got)
}
}
func TestLoadFileConfig_ReservedString(t *testing.T) {
path := filepath.Join(t.TempDir(), "config.toml")
if err := os.WriteFile(path, []byte("[checkpoint]\nreserved = \"10%\"\n"), 0o600); err != nil {
t.Fatal(err)
}
cfg, err := LoadFileConfig(path)
if err != nil {
t.Fatalf("LoadFileConfig: %v", err)
}
if !cfg.Checkpoint.Reserved.Set || cfg.Checkpoint.Reserved.IsInt || cfg.Checkpoint.Reserved.Str != "10%" {
t.Errorf("reserved = %+v, want string 10%%", cfg.Checkpoint.Reserved)
}
}
// Absent tables → default-safe resolved settings.
func TestResolveMemorySettings_Defaults(t *testing.T) {
ms := FileConfig{}.ResolveMemorySettings()
if !ms.Memory.Enabled || !ms.Memory.ReconcileOnSearch || ms.Memory.SearchScoreFloor != 0.15 || ms.Memory.CCIndex {
t.Errorf("default memory = %+v", ms.Memory)
}
if !reflect.DeepEqual(ms.CheckpointThresholds, []float64{0.40, 0.60, 0.80}) {
t.Errorf("default thresholds = %v", ms.CheckpointThresholds)
}
if ms.MaxContext.IsSet() {
t.Errorf("default max_context should be unset")
}
if ms.CheckpointReserved.Set {
t.Errorf("default reserved should be unset")
}
}
// An invalid max_context must not fail the overlay — it drops to unset.
func TestResolveMemorySettings_InvalidMaxContextIgnored(t *testing.T) {
ms := FileConfig{Compaction: CompactionConfig{MaxContext: "garbage"}}.ResolveMemorySettings()
if ms.MaxContext.IsSet() {
t.Errorf("invalid max_context should resolve to unset, got set")
}
}