Files
Agent-System/internal/projectinstance/duty_mode_test.go
T

109 lines
4.1 KiB
Go

package projectinstance
import (
"testing"
"chat-agent-system/internal/workflowdef"
)
func TestClassifyDocDuty(t *testing.T) {
t.Parallel()
cases := []struct {
name string
duty DocDuty
want DutyMode
}{
{name: "all_zero", duty: DocDuty{}, want: DutyModeNone},
{name: "negative", duty: DocDuty{AuthorUserID: -1}, want: DutyModePartial},
{name: "one_role", duty: DocDuty{AuthorUserID: 9}, want: DutyModePartial},
{name: "two_roles", duty: DocDuty{AuthorUserID: 9, ReviewerUserID: 2}, want: DutyModePartial},
{name: "full_distinct", duty: DocDuty{AuthorUserID: 9, ReviewerUserID: 2, ApproverUserID: 3}, want: DutyModeFull},
{name: "full_duplicate", duty: DocDuty{AuthorUserID: 9, ReviewerUserID: 9, ApproverUserID: 3}, want: DutyModeFull},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
if got := classifyDocDuty(tc.duty); got != tc.want {
t.Fatalf("classifyDocDuty(%+v)=%s want %s", tc.duty, got, tc.want)
}
})
}
}
func TestDocTaskRequiresDuty(t *testing.T) {
t.Parallel()
if docTaskRequiresDuty(workflowdef.DocTaskDef{CreatorConfigurable: true}) {
t.Fatal("configurable optional doc must not require duties")
}
if !docTaskRequiresDuty(workflowdef.DocTaskDef{Required: true, CreatorConfigurable: true}) {
t.Fatal("required doc must keep duties")
}
if !docTaskRequiresDuty(workflowdef.DocTaskDef{Blocking: true, CreatorConfigurable: true}) {
t.Fatal("blocking doc must keep duties")
}
if !docTaskRequiresDuty(workflowdef.DocTaskDef{Required: true}) {
t.Fatal("non-configurable required doc must keep duties")
}
}
func TestNormalizeDocTaskDuties(t *testing.T) {
t.Parallel()
cleared := normalizeDocTaskDuties(DocTaskPlan{
Enabled: false,
Duties: DocDuty{AuthorUserID: 9, ReviewerUserID: 2, ApproverUserID: 3},
})
if cleared.Duties != (DocDuty{}) {
t.Fatalf("disabled task must drop leftover duties, got %+v", cleared.Duties)
}
none := normalizeDocTaskDuties(DocTaskPlan{Enabled: true, Duties: DocDuty{AuthorUserID: 0, ReviewerUserID: -0}})
if none.Duties != (DocDuty{}) {
t.Fatalf("none must canonicalize to zero duties, got %+v", none.Duties)
}
partial := DocTaskPlan{Enabled: true, Duties: DocDuty{AuthorUserID: 9}}
if got := normalizeDocTaskDuties(partial); got.Duties.AuthorUserID != 9 {
t.Fatalf("partial must not auto-clear, got %+v", got.Duties)
}
}
func TestValidateDocTaskDuties(t *testing.T) {
t.Parallel()
optional := workflowdef.DocTaskDef{Key: "research", Title: "调研", CreatorConfigurable: true}
required := workflowdef.DocTaskDef{Key: "intake", Title: "需求", Required: true}
if issues := validateDocTaskDuties(DocTaskPlan{TaskKey: "research", Enabled: true}, optional); len(issues) != 0 {
t.Fatalf("optional none should pass, got %+v", issues)
}
reqIssues := validateDocTaskDuties(DocTaskPlan{TaskKey: "intake", TaskTitle: "需求", Enabled: true}, required)
if len(reqIssues) != 1 || reqIssues[0].Code != issueDutyAssignmentRequired || reqIssues[0].Field != "intake.duties" {
t.Fatalf("required none: %+v", reqIssues)
}
partial := validateDocTaskDuties(DocTaskPlan{
TaskKey: "research", Enabled: true, Duties: DocDuty{AuthorUserID: 1},
}, optional)
if len(partial) != 1 || partial[0].Code != issueDutyPartialAssignment || partial[0].Field != "research.duties" {
t.Fatalf("partial field must be exact, got %+v", partial)
}
sep := validateDocTaskDuties(DocTaskPlan{
TaskKey: "intake", Enabled: true,
Duties: DocDuty{AuthorUserID: 1, ReviewerUserID: 1, ApproverUserID: 2},
}, required)
if len(sep) != 1 || sep[0].Code != issueSeparationOfDutyViolation {
t.Fatalf("duplicate full must keep SEPARATION_OF_DUTY_VIOLATION, got %+v", sep)
}
}
func TestCountFullDutyDocs(t *testing.T) {
t.Parallel()
plan := WorkflowPlan{Stages: []StagePlan{{
Enabled: true,
DocTasks: []DocTaskPlan{
{Enabled: true, Duties: DocDuty{AuthorUserID: 1, ReviewerUserID: 2, ApproverUserID: 3}},
{Enabled: true},
{Enabled: false, Duties: DocDuty{AuthorUserID: 1, ReviewerUserID: 2, ApproverUserID: 3}},
},
}}}
if n := countFullDutyDocs(plan); n != 1 {
t.Fatalf("countFullDutyDocs=%d want 1", n)
}
}