package ctxmgr import ( "context" "strings" "testing" "time" ) func TestRollingSummarizer_LifecycleAndFactPreservation(t *testing.T) { mgr := NewContextManager(nil, nil, DefaultTimeDecayConfig()) ctx := context.Background() sessID := "sess_sum_test_001" // 模拟首批 4 条早期消息(将被淘汰出窗口) batch1 := []ChatMessageRecord{ { ID: 1, Role: "user", Content: "我们准备研发高频超声手术刀系统,请教立项建议", CreatedAt: time.Now().Add(-10 * time.Minute), }, { ID: 2, Role: "assistant", Content: "超声手术设备属于二类或三类器械,需着重关注电气安全与空化热效应控制。", CreatedAt: time.Now().Add(-9 * time.Minute), }, { ID: 3, Role: "user", Content: "目标市场是国内及欧洲CE,项目预算大约在200万左右,型检周期通常需要多久?", CreatedAt: time.Now().Add(-8 * time.Minute), }, { ID: 4, Role: "assistant", Content: "国内型检通常预估4-6个月,需符合GB 9706.1-2020标准。", CreatedAt: time.Now().Add(-7 * time.Minute), }, } sum1, err := mgr.GenerateRollingSummary(ctx, sessID, nil, batch1) if err != nil { t.Fatalf("GenerateRollingSummary failed: %v", err) } if sum1.CoveredCount != 4 { t.Errorf("expected covered_count 4, got %d", sum1.CoveredCount) } if sum1.Facts["device_type"] != "超声手术设备" { t.Errorf("expected device_type extracted, got %v", sum1.Facts) } if sum1.Facts["target_market"] != "国内及欧洲CE" { t.Errorf("expected target_market extracted, got %v", sum1.Facts) } if sum1.Facts["budget"] != "200万元" { t.Errorf("expected budget extracted, got %v", sum1.Facts) } if len(sum1.OpenQuestions) == 0 { t.Errorf("expected at least 1 open question extracted") } snippet1 := sum1.RenderPromptSnippet() if !strings.Contains(snippet1, "【早期对话摘要】(已压缩前 4 条对话内容)") { t.Errorf("snippet missing header: %s", snippet1) } if !strings.Contains(snippet1, "device_type=超声手术设备") { t.Errorf("snippet missing device_type: %s", snippet1) } // 模拟第二批消息追加(增量滚动压缩) batch2 := []ChatMessageRecord{ { ID: 5, Role: "user", Content: "临床评价路径该怎么走?", CreatedAt: time.Now().Add(-5 * time.Minute), }, { ID: 6, Role: "assistant", Content: "可优先考虑同品种比对路径以节省费用。", CreatedAt: time.Now().Add(-4 * time.Minute), }, } sum2, err := mgr.GenerateRollingSummary(ctx, sessID, sum1, batch2) if err != nil { t.Fatalf("incremental GenerateRollingSummary failed: %v", err) } if sum2.CoveredCount != 6 { t.Errorf("expected covered_count 6, got %d", sum2.CoveredCount) } // 验证硬事实依然完整保留(永不丢失) if sum2.Facts["device_type"] != "超声手术设备" || sum2.Facts["budget"] != "200万元" { t.Errorf("hard facts lost during incremental roll: %v", sum2.Facts) } // 验证 ContextManager 的读取 saved, err := mgr.GetChatSummary(ctx, sessID) if err != nil || saved == nil { t.Fatalf("GetChatSummary failed: %v", err) } if saved.CoveredCount != 6 { t.Errorf("saved summary covered count mismatch: %d", saved.CoveredCount) } }