// 织忆 MemoryWeave — 自优化层单元测试 package selfoptimize import ( "testing" "time" ) // ─── 仪表盘 ────────────────────────────────────────────── func TestDashboard_InitialMetricsAllZero(t *testing.T) { d := &Dashboard{} m := d.Metrics() if m["recall_usefulness_rate"] != 0 { t.Errorf("expected 0, got %f", m["recall_usefulness_rate"]) } if m["recall_hit_rate"] != 0 { t.Errorf("expected 0, got %f", m["recall_hit_rate"]) } } func TestDashboard_UsefulnessRate(t *testing.T) { d := &Dashboard{} d.RecordFeedback(true) d.RecordFeedback(true) d.RecordFeedback(true) d.RecordFeedback(false) m := d.Metrics() if m["recall_usefulness_rate"] != 0.75 { t.Errorf("expected 0.75, got %f", m["recall_usefulness_rate"]) } } func TestDashboard_HitRate(t *testing.T) { d := &Dashboard{} d.RecordRecall(true) d.RecordRecall(true) d.RecordRecall(false) m := d.Metrics() if m["recall_hit_rate"] < 0.66 || m["recall_hit_rate"] > 0.67 { t.Errorf("expected ~0.67, got %f", m["recall_hit_rate"]) } } func TestDashboard_GapClosureRate(t *testing.T) { d := &Dashboard{} d.TotalGaps = 10 d.ClosedGaps = 5 m := d.Metrics() if m["gap_closure_rate"] != 0.5 { t.Errorf("expected 0.5, got %f", m["gap_closure_rate"]) } } func TestDashboard_RecordDeprecation(t *testing.T) { d := &Dashboard{} d.RecordDeprecation() d.RecordDeprecation() if d.DeprecatedToday != 2 { t.Errorf("expected 2, got %d", d.DeprecatedToday) } } func TestDashboard_RecordCorrection(t *testing.T) { d := &Dashboard{} d.RecordCorrection("muchen_correction") d.RecordCorrection("config_parse") if d.TotalFixes != 2 { t.Errorf("expected 2 fixes, got %d", d.TotalFixes) } if d.CascadeFixedTotal != 1 { t.Errorf("expected 1 cascade, got %d", d.CascadeFixedTotal) } } func TestDashboard_RecordConflictResolved(t *testing.T) { d := &Dashboard{} d.RecordConflictResolved(true) d.RecordConflictResolved(false) if d.TotalConflicts != 2 { t.Errorf("expected 2 conflicts, got %d", d.TotalConflicts) } if d.AutoResolvedConflicts != 1 { t.Errorf("expected 1 auto, got %d", d.AutoResolvedConflicts) } m := d.Metrics() if m["auto_resolve_rate"] != 0.5 { t.Errorf("expected 0.5, got %f", m["auto_resolve_rate"]) } } // ─── 知识缺口检测 ──────────────────────────────────────── func TestGapDetector_NotTriggeredBeforeThreshold(t *testing.T) { gd := NewGapDetector() gap := gd.RecordMiss("kubernetes") if gap != nil { t.Error("should not trigger gap after 1 miss") } gd.RecordMiss("kubernetes") if gap := gd.RecordMiss("kubernetes"); gap == nil { t.Error("should trigger gap after 3 misses") } } func TestGapDetector_DetectsGapAtThreshold(t *testing.T) { gd := NewGapDetector() gd.RecordMiss("topicX") gd.RecordMiss("topicX") gap := gd.RecordMiss("topicX") if gap == nil { t.Fatal("expected gap after 3 misses") } if gap.Topic != "topicX" { t.Errorf("wrong topic: %s", gap.Topic) } if gap.MissCount != 3 { t.Errorf("expected 3 misses, got %d", gap.MissCount) } } func TestGapDetector_CloseGap(t *testing.T) { gd := NewGapDetector() gd.RecordMiss("topic") gd.RecordMiss("topic") gd.RecordMiss("topic") gd.Close("topic") gaps := gd.List() if len(gaps) == 0 || !gaps[0].Closed { t.Error("gap should be closed") } } func TestGapDetector_ClassifySynonym(t *testing.T) { gd := NewGapDetector() gd.RecordMiss("API") gd.RecordMiss("API") gd.RecordMiss("API") gaps := gd.List() if len(gaps) > 0 && gaps[0].Type != GapSynonym { t.Errorf("expected synonym gap, got %s", gaps[0].Type) } } // ─── 因果追踪 ──────────────────────────────────────────── func TestCausalTracker_RecordVersions(t *testing.T) { ct := NewCausalTracker() ct.RecordVersion("mem1", "Docker is used by Hermes", "muchen_oral", "init") ct.RecordVersion("mem1", "Docker is used by Hermes AND OpenClaw", "config_parse", "update") if !ct.IsVolatile("mem1") { // 2 versions → not volatile (volatile = 3+) } ct.RecordVersion("mem1", "Docker usage updated", "muchen_correction", "correction") if !ct.IsVolatile("mem1") { t.Error("mem1 should be volatile after 3 versions") } } func TestCausalTracker_DependencyChain(t *testing.T) { ct := NewCausalTracker() ct.AddDependency("memory_A", "memory_B") ct.AddDependency("memory_B", "memory_C") affected := ct.GetAffected("memory_C", nil) // memory_B depends on C, memory_A depends on B foundB := false foundA := false for _, id := range affected { if id == "memory_B" { foundB = true } if id == "memory_A" { foundA = true } } if !foundB || !foundA { t.Errorf("expected both A and B affected, got %v", affected) } } func TestCausalTracker_CircularDependency(t *testing.T) { ct := NewCausalTracker() ct.AddDependency("A", "B") ct.AddDependency("B", "A") affected := ct.GetAffected("A", nil) // Should not loop infinitely if len(affected) >= 10 { t.Errorf("circular dependency not handled: %d affected", len(affected)) } } // ─── 来源信任度 ────────────────────────────────────────── func TestSourceTrust(t *testing.T) { tests := []struct { source string expect float64 }{ {"muchen_oral", 1.0}, {"muchen_feishu", 0.95}, {"config_parse", 0.7}, {"agent_infer", 0.5}, {"llm_distill", 0.4}, {"unknown", 0.3}, } for _, tc := range tests { if got := SourceTrust(tc.source); got != tc.expect { t.Errorf("SourceTrust(%s) = %f, want %f", tc.source, got, tc.expect) } } } // ─── 记忆预取 ──────────────────────────────────────────── func TestPrefetchGraph_CoOccurrence(t *testing.T) { pg := NewPrefetchGraph() pg.RecordCoAccess("docker", "nginx") pg.RecordCoAccess("docker", "nginx") pg.RecordCoAccess("docker", "kubernetes") prefetch := pg.GetPrefetch("docker") if len(prefetch) < 1 { t.Errorf("expected at least 1 prefetch item, got %d", len(prefetch)) } } func TestPrefetchGraph_NothingToPrefetch(t *testing.T) { pg := NewPrefetchGraph() prefetch := pg.GetPrefetch("nonexistent") if len(prefetch) != 0 { t.Errorf("expected empty prefetch, got %d", len(prefetch)) } } // ─── 并发安全 ──────────────────────────────────────────── func TestDashboard_Concurrent(t *testing.T) { d := &Dashboard{} done := make(chan bool) for i := 0; i < 50; i++ { go func(v bool) { for j := 0; j < 100; j++ { d.RecordFeedback(v) d.RecordRecall(v) } done <- true }(i%2 == 0) } for i := 0; i < 50; i++ { <-done } m := d.Metrics() if m["recall_usefulness_rate"] < 0 || m["recall_usefulness_rate"] > 1 { t.Errorf("usefulness rate out of bounds: %f", m["recall_usefulness_rate"]) } } // ─── 基准测试 ──────────────────────────────────────────── func BenchmarkDashboardMetrics(b *testing.B) { d := &Dashboard{} d.UsefulCount = 1000 d.NotUsefulCount = 200 d.TotalRecalls = 1200 d.HitCount = 900 b.ResetTimer() for i := 0; i < b.N; i++ { d.Metrics() } } func BenchmarkGapDetection(b *testing.B) { gd := NewGapDetector() b.ResetTimer() for i := 0; i < b.N; i++ { gd.RecordMiss("benchmark_topic") } } func BenchmarkCausalTracking(b *testing.B) { ct := NewCausalTracker() for i := 0; i < 100; i++ { ct.AddDependency("dep"+string(rune('a'+i%26)), "dep"+string(rune('a'+(i+1)%26))) } b.ResetTimer() for i := 0; i < b.N; i++ { ct.GetAffected("depa", nil) } } func BenchmarkPrefetch(b *testing.B) { pg := NewPrefetchGraph() for i := 0; i < 100; i++ { pg.RecordCoAccess("main", "item"+string(rune('a'+i%26))) } b.ResetTimer() for i := 0; i < b.N; i++ { pg.GetPrefetch("main") } } func init() { // Ensure consistent timestamps for snapshot tests time.Local = time.UTC }