package conflictresolve_test import ( "context" "testing" "time" "chat-agent-system/internal/conflictresolve" "chat-agent-system/internal/dialoguectx" ) func TestAdjudicateBlackboardBeatsDialogue(t *testing.T) { now := time.Now() engine := &conflictresolve.Engine{} verdicts := engine.Adjudicate(context.Background(), []dialoguectx.DialogueFact{{ Key: "device_type", Value: "超声", SpeakerName: "张工", StatedAt: now, Authority: dialoguectx.AuthorityCollabDialogue, }}, []conflictresolve.FactClaim{{ Key: "device_type", Value: "眼科OCT", AuthorityRank: conflictresolve.RankBlackboard, SourceLabel: "blackboard", StatedAt: now.Add(-time.Hour), }}, ) if len(verdicts) != 1 { t.Fatalf("want 1 verdict, got %d", len(verdicts)) } v := verdicts[0] if v.Winning.Value != "眼科OCT" { t.Fatalf("blackboard should win, got %s", v.Winning.Value) } if v.NeedsHITL { t.Fatal("clear authority gap should not need HITL") } } func TestAdjudicateAmbiguousNeedsHITL(t *testing.T) { now := time.Now() engine := &conflictresolve.Engine{} verdicts := engine.Adjudicate(context.Background(), []dialoguectx.DialogueFact{ {Key: "budget", Value: "一千万", SpeakerName: "张工", StatedAt: now}, {Key: "budget", Value: "两千万", SpeakerName: "李工", StatedAt: now.Add(5 * time.Minute)}, }, nil, ) if len(verdicts) != 1 { t.Fatalf("want 1 verdict, got %d", len(verdicts)) } if !verdicts[0].NeedsHITL { t.Fatalf("expected HITL for ambiguous same-rank, got %#v", verdicts[0]) } if verdicts[0].HITLRoute != conflictresolve.RouteTriggerUser { t.Fatalf("expected trigger_user route, got %s", verdicts[0].HITLRoute) } } func TestCardLimitAutoAdjudicates(t *testing.T) { verdicts := make([]conflictresolve.Verdict, 0, 5) for i := 0; i < 5; i++ { verdicts = append(verdicts, conflictresolve.Verdict{ FactKey: "k", NeedsHITL: true, CardEligible: true, Resolution: conflictresolve.ResolutionNeedsConfirm, Winning: conflictresolve.FactClaim{Value: "v"}, }) } out := conflictresolve.ApplyCardLimit(verdicts) cards := 0 autos := 0 for _, v := range out { if v.NeedsHITL { cards++ } if v.Resolution == conflictresolve.ResolutionCardLimitAuto { autos++ } } if cards != conflictresolve.MaxCardsPerTask { t.Fatalf("want %d cards, got %d", conflictresolve.MaxCardsPerTask, cards) } if autos != 2 { t.Fatalf("want 2 auto, got %d", autos) } } func TestRenderConfirmationsMD(t *testing.T) { md := conflictresolve.RenderConfirmationsMD(nil, time.Now()) if md == "" { t.Fatal("empty md") } }