package dialoguectx_test import ( "os" "strings" "testing" "time" "chat-agent-system/internal/ctxmgr" "chat-agent-system/internal/dialoguectx" "chat-agent-system/internal/replyexport" ) func TestInjectionEnabledDefaultOn(t *testing.T) { t.Setenv("DIALOGUE_CTX_INJECTION", "") if !dialoguectx.InjectionEnabled() { t.Fatal("expected default on") } t.Setenv("DIALOGUE_CTX_INJECTION", "off") if dialoguectx.InjectionEnabled() { t.Fatal("expected off") } } func TestCollectFromRecordsCleansAndEnriches(t *testing.T) { now := time.Date(2026, 9, 24, 15, 0, 0, 0, time.UTC) recs := []ctxmgr.ChatMessageRecord{ {ID: 1, SessionID: "s1", UserID: 7, Role: "user", Content: "你好", CreatedAt: now, DayKey: "2026-09-24"}, {ID: 2, SessionID: "s1", UserID: 7, Role: "user", Content: "器械类型:眼科OCT", CreatedAt: now.Add(time.Minute), DayKey: "2026-09-24"}, {ID: 3, SessionID: "s1", UserID: 0, Role: "assistant", Content: "状态更新中", ContentKind: string(replyexport.KindStatusUpdate), ExportPolicy: string(replyexport.PolicyNone), CreatedAt: now.Add(2 * time.Minute), DayKey: "2026-09-24"}, {ID: 4, SessionID: "s1", UserID: 7, Role: "user", Content: "[private] 草稿备注", CreatedAt: now.Add(3 * time.Minute), DayKey: "2026-09-24"}, {ID: 5, SessionID: "s1", UserID: 0, Role: "assistant", Content: "已记录眼科OCT分类建议", ContentKind: string(replyexport.KindDocumentText), ExportPolicy: string(replyexport.PolicyFull), CreatedAt: now.Add(4 * time.Minute), DayKey: "2026-09-24"}, {ID: 6, SessionID: "s1", UserID: 8, Role: "user", Content: "目标市场:国内III类", CreatedAt: now.Add(5 * time.Minute), DayKey: "2026-09-24", Metadata: map[string]any{"stage_key": "other"}}, } enrich := map[int64]struct { Name, SystemRole, ProjectRole, DocDuty string }{ 7: {Name: "张工", SystemRole: "engineer", ProjectRole: "member"}, 8: {Name: "李工", SystemRole: "engineer", ProjectRole: "member"}, } // CollectFromRecords takes unexported speakerMeta — use public path via package rebuild: // call through exported helper by duplicating via CollectAndPackage memory is heavy; // instead test packaging via Inject test shim in same package. _ = enrich _ = os.Getenv pkg, err := dialoguectx.CollectFromRecords(recs, dialoguectx.CollectOptions{ ProjectID: 1, MaxDays: 7, MaxTurns: 80, Now: now.Add(time.Hour), AllowedStageKey: "", }, nil) if err != nil { t.Fatal(err) } if pkg.TurnCount < 2 { t.Fatalf("expected cleaned turns, got %d (noise=%d private=%d)", pkg.TurnCount, pkg.ExcludedNoise, pkg.ExcludedPrivate) } if pkg.ExcludedPrivate < 1 { t.Fatalf("expected private exclusion") } if pkg.ExcludedNoise < 1 { t.Fatalf("expected noise exclusion for greeting/status") } if !strings.Contains(pkg.TurnsMD, "器械类型") { t.Fatalf("turns missing content: %s", pkg.TurnsMD) } foundDevice := false for _, f := range pkg.Facts { if f.Key == "device_type" && strings.Contains(f.Value, "眼科OCT") { foundDevice = true } } if !foundDevice { t.Fatalf("expected device_type fact, got %#v", pkg.Facts) } snip := dialoguectx.RenderAssistantSnippet(pkg) if !strings.Contains(snip, "项目成员协同近况") { t.Fatalf("snippet missing header: %s", snip) } } func TestStageFilterExcludes(t *testing.T) { now := time.Now() recs := []ctxmgr.ChatMessageRecord{ {ID: 1, Role: "user", Content: "当前阶段事实:预算一千万", CreatedAt: now, Metadata: map[string]any{"stage_key": "rd_proposal"}}, {ID: 2, Role: "user", Content: "其他阶段事实:预算两千万", CreatedAt: now, Metadata: map[string]any{"stage_key": "system_reqs"}}, } pkg, err := dialoguectx.CollectFromRecords(recs, dialoguectx.CollectOptions{ ProjectID: 1, MaxDays: 7, Now: now.Add(time.Minute), AllowedStageKey: "rd_proposal", }, nil) if err != nil { t.Fatal(err) } if pkg.ExcludedNoAccess != 1 { t.Fatalf("expected 1 stage exclusion, got %d", pkg.ExcludedNoAccess) } if pkg.TurnCount != 1 { t.Fatalf("expected 1 turn, got %d", pkg.TurnCount) } }