Files
Agent-System/internal/coordinator/docgov_dialogue_test.go
T

169 lines
5.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package coordinator_test
import (
"context"
"strings"
"testing"
"chat-agent-system/internal/contracts/vocabulary"
"chat-agent-system/internal/coordinator"
"chat-agent-system/internal/intent"
)
type stubDocResolverDialogue struct {
doc *coordinator.ResolvedDoc
ok bool
candidates []string
err error
}
func (s *stubDocResolverDialogue) ResolveDoc(context.Context, int64, string, string) (*coordinator.ResolvedDoc, bool, []string, error) {
return s.doc, s.ok, s.candidates, s.err
}
func TestListDocResolver_ZeroAndMultiMatchClarify(t *testing.T) {
r := &coordinator.ListDocResolver{
List: func(context.Context, int64) ([]coordinator.DocListItem, error) {
return []coordinator.DocListItem{
{DocID: "d1", Title: "需求收集表", State: vocabulary.StateDrafting, TaskKey: "intake"},
{DocID: "d2", Title: "需求评估表", State: vocabulary.StateDrafting, TaskKey: "eval"},
}, nil
},
}
_, ok, cands, err := r.ResolveDoc(context.Background(), 1, "需求", "")
if err != nil {
t.Fatal(err)
}
if ok {
t.Fatal("expected multi-match not ok")
}
if len(cands) < 2 {
t.Fatalf("expected candidates, got %v", cands)
}
_, ok, cands, err = r.ResolveDoc(context.Background(), 1, "不存在的文档XYZ", "")
if err != nil {
t.Fatal(err)
}
if ok {
t.Fatal("expected zero unique match")
}
if len(cands) == 0 {
t.Fatal("expected inventory candidates on zero match")
}
doc, ok, _, err := r.ResolveDoc(context.Background(), 1, "需求收集表", "")
if err != nil || !ok || doc == nil || doc.DocID != "d1" {
t.Fatalf("unique title match failed: ok=%v doc=%+v err=%v", ok, doc, err)
}
doc, ok, _, err = r.ResolveDoc(context.Background(), 1, "@active", "d2")
if err != nil || !ok || doc.DocID != "d2" {
t.Fatalf("active resolve failed: %+v", doc)
}
}
func TestHandleDocGovSubmitReview_ConfirmationCard(t *testing.T) {
engine := coordinator.NewCoordinatorEngine(nil)
engine.SetDocResolver(&stubDocResolver{
doc: &coordinator.ResolvedDoc{DocID: "doc-001", Title: "需求收集表", State: vocabulary.StateDrafting, TaskKey: "intake"},
ok: true,
})
userCtx := &coordinator.UserProjectContext{
User: coordinator.UserInfo{ID: "1", Name: "张工", Role: "author", ProjectRole: "author"},
Project: coordinator.ProjectInfo{ID: "10", Name: "测试项目"},
}
res, err := engine.ProcessIntent(context.Background(), &intent.IntentRecognitionResult{
TopIntent: &intent.CandidateIntent{
Intent: intent.IntentDocGovSubmitReview,
FilledSlots: map[string]string{intent.SlotTargetDoc: "需求收集表"},
},
}, userCtx)
if err != nil {
t.Fatal(err)
}
if res.Status != coordinator.StatusAccepted {
t.Fatalf("status=%s", res.Status)
}
if res.InteractiveCard == nil || res.InteractiveCard.CardType != "CONFIRMATION" {
t.Fatalf("expected CONFIRMATION card, got %+v", res.InteractiveCard)
}
if res.PendingAction != nil {
t.Fatal("must not execute before confirm")
}
// confirm
res2, err := engine.ProcessIntent(context.Background(), &intent.IntentRecognitionResult{
TopIntent: &intent.CandidateIntent{
Intent: intent.IntentDocGovProposalDecide,
FilledSlots: map[string]string{
"target_action": "confirm_submit_review",
"target_doc": "doc-001",
},
},
}, userCtx)
if err != nil {
t.Fatal(err)
}
if res2.PendingAction == nil || res2.PendingAction.Kind != "docgov.submit_review" {
t.Fatalf("expected submit_review pending, got %+v", res2.PendingAction)
}
if res2.PendingAction.DocID != "doc-001" {
t.Fatalf("doc id=%s", res2.PendingAction.DocID)
}
}
func TestHandleDocGovCommentAdd_ClarifyOnMultiDoc(t *testing.T) {
engine := coordinator.NewCoordinatorEngine(nil)
engine.SetDocResolver(&stubDocResolver{
ok: false,
candidates: []string{"需求收集表(d1)", "需求评估表(d2)"},
})
userCtx := &coordinator.UserProjectContext{
User: coordinator.UserInfo{ID: "1", Role: "author", ProjectRole: "author"},
Project: coordinator.ProjectInfo{ID: "10"},
}
res, err := engine.ProcessIntent(context.Background(), &intent.IntentRecognitionResult{
TopIntent: &intent.CandidateIntent{
Intent: intent.IntentDocGovCommentAdd,
FilledSlots: map[string]string{
intent.SlotTargetDoc: "需求",
intent.SlotCommentContent: "缺风险分析",
},
},
}, userCtx)
if err != nil {
t.Fatal(err)
}
if res.Status != coordinator.StatusClarificationRequired {
t.Fatalf("expected clarification, got %s", res.Status)
}
if !strings.Contains(res.NaturalReply, "需求收集表") {
t.Fatalf("reply should list candidates: %s", res.NaturalReply)
}
}
func TestHandleQueryPendingSupplements(t *testing.T) {
engine := coordinator.NewCoordinatorEngine(nil)
engine.SetPendingSupplementReader(&stubPendingReader{rows: []coordinator.PendingSupplementView{
{DocID: "d1", DocTitle: "需求表", SourceSummary: "预算改为200万", ImpactReason: "锁定中"},
}})
res, err := engine.ProcessIntent(context.Background(), &intent.IntentRecognitionResult{
TopIntent: &intent.CandidateIntent{Intent: intent.IntentQueryPendingSupplements},
}, &coordinator.UserProjectContext{Project: coordinator.ProjectInfo{ID: "1"}})
if err != nil {
t.Fatal(err)
}
if !strings.Contains(res.NaturalReply, "预算改为200万") {
t.Fatalf("reply=%s", res.NaturalReply)
}
}
type stubPendingReader struct {
rows []coordinator.PendingSupplementView
}
func (s *stubPendingReader) ListPendingSupplements(context.Context, int64, string) ([]coordinator.PendingSupplementView, error) {
return s.rows, nil
}