Exact out-of-workflow documents can be summarized when WORKFLOW_SCOPE_MODE=weighted and the caller can still view the active instance; writes stay blocked. Co-authored-by: Cursor <cursoragent@cursor.com>
122 lines
4.2 KiB
Go
122 lines
4.2 KiB
Go
package server
|
|
|
|
import (
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
|
|
"chat-agent-system/internal/coordinator"
|
|
"chat-agent-system/internal/docgov"
|
|
"chat-agent-system/internal/projectinstance"
|
|
"chat-agent-system/internal/workflowscope"
|
|
)
|
|
|
|
func TestSelectReadableWorkflowDocumentMatchesInitialRequirementsAlias(t *testing.T) {
|
|
docs := []docgov.WorkflowDocumentSummary{
|
|
{DocID: "doc_proj143_intake", TaskKey: "intake", Title: "用户初始需求收集表"},
|
|
{DocID: "doc_proj143_research", TaskKey: "research", Title: "技术与市场调研报告"},
|
|
}
|
|
doc, err := selectReadableWorkflowDocument(docs, "用户初始需求表", "", nil)
|
|
if err != nil {
|
|
t.Fatalf("select: %v", err)
|
|
}
|
|
if doc.DocID != "doc_proj143_intake" {
|
|
t.Fatalf("doc=%s", doc.DocID)
|
|
}
|
|
}
|
|
|
|
func TestSelectReadableWorkflowDocumentDoesNotGuessWhenTargetMissing(t *testing.T) {
|
|
docs := []docgov.WorkflowDocumentSummary{
|
|
{DocID: "doc_a", TaskKey: "intake", Title: "用户初始需求收集表"},
|
|
{DocID: "doc_b", TaskKey: "research", Title: "调研报告"},
|
|
}
|
|
if _, err := selectReadableWorkflowDocument(docs, "", "", nil); err == nil {
|
|
t.Fatal("expected target-required error")
|
|
}
|
|
}
|
|
|
|
func TestSelectReadableWorkflowDocumentScopesToActiveWorkflow(t *testing.T) {
|
|
docs := []docgov.WorkflowDocumentSummary{
|
|
{DocID: "doc_proj1_research", TaskKey: "bd_research", Title: "调研报告"},
|
|
{DocID: "doc_proj1_proposal", TaskKey: "rd_proposal", Title: "项目建议书"},
|
|
}
|
|
scope := map[string]bool{"bd_research": true, "research": true}
|
|
|
|
doc, err := selectReadableWorkflowDocument(docs, "调研报告", "main", scope)
|
|
if err != nil {
|
|
t.Fatalf("in-scope select: %v", err)
|
|
}
|
|
if doc.TaskKey != "bd_research" {
|
|
t.Fatalf("want bd_research, got %s", doc.TaskKey)
|
|
}
|
|
|
|
_, err = selectReadableWorkflowDocument(docs, "项目建议书", "main", scope)
|
|
if err == nil {
|
|
t.Fatal("expected wrong-workflow error")
|
|
}
|
|
var wrong *coordinator.ErrWrongWorkflow
|
|
if !errors.As(err, &wrong) {
|
|
t.Fatalf("want ErrWrongWorkflow, got %T %v", err, err)
|
|
}
|
|
if !strings.Contains(wrong.Message, "切换") {
|
|
t.Fatalf("guidance should mention 切换, got %q", wrong.Message)
|
|
}
|
|
}
|
|
|
|
func TestSelectReadableWorkflowDocumentNoScopeKeepsLegacy(t *testing.T) {
|
|
docs := []docgov.WorkflowDocumentSummary{
|
|
{DocID: "doc_proj1_proposal", TaskKey: "rd_proposal", Title: "项目建议书"},
|
|
}
|
|
doc, err := selectReadableWorkflowDocument(docs, "项目建议书", "", nil)
|
|
if err != nil {
|
|
t.Fatalf("legacy passthrough: %v", err)
|
|
}
|
|
if doc.TaskKey != "rd_proposal" {
|
|
t.Fatalf("want rd_proposal, got %s", doc.TaskKey)
|
|
}
|
|
}
|
|
|
|
func TestSelectReadableWeightedAllowsExactCrossRead(t *testing.T) {
|
|
workflowscope.SetModeForTest(workflowscope.ModeWeighted)
|
|
t.Cleanup(workflowscope.ResetModeForTest)
|
|
|
|
docs := []docgov.WorkflowDocumentSummary{
|
|
{DocID: "doc_proj1_research", TaskKey: "bd_research", Title: "调研报告"},
|
|
{DocID: "doc_proj1_proposal", TaskKey: "rd_proposal", Title: "项目建议书"},
|
|
}
|
|
scope := map[string]bool{"bd_research": true}
|
|
doc, err := selectReadableWorkflowDocument(docs, "项目建议书", "main", scope)
|
|
if err != nil {
|
|
t.Fatalf("weighted cross read: %v", err)
|
|
}
|
|
if doc.TaskKey != "rd_proposal" {
|
|
t.Fatalf("want rd_proposal, got %s", doc.TaskKey)
|
|
}
|
|
|
|
inScope, err := selectReadableWorkflowDocument(docs, "调研报告", "main", scope)
|
|
if err != nil || inScope.TaskKey != "bd_research" {
|
|
t.Fatalf("in-scope still wins: %v %v", inScope, err)
|
|
}
|
|
}
|
|
|
|
func TestCrossWorkflowTaskVisible(t *testing.T) {
|
|
open := &projectinstance.StageAccess{
|
|
Restricted: true,
|
|
ViewableTaskKeys: map[string]bool{"bd_research": true},
|
|
}
|
|
scope := map[string]bool{"bd_research": true, "bd_hidden": true}
|
|
if !crossWorkflowTaskVisible(open, scope, "rd_proposal") {
|
|
t.Fatal("foreign key should be readable when the caller can view the active instance")
|
|
}
|
|
if crossWorkflowTaskVisible(open, scope, "bd_hidden") {
|
|
t.Fatal("in-scope but not viewable must be denied")
|
|
}
|
|
closed := &projectinstance.StageAccess{Restricted: true, ViewableTaskKeys: map[string]bool{}}
|
|
if crossWorkflowTaskVisible(closed, scope, "rd_proposal") {
|
|
t.Fatal("closed instance must deny cross read")
|
|
}
|
|
if !crossWorkflowTaskVisible(nil, scope, "rd_proposal") {
|
|
t.Fatal("unrestricted access must allow")
|
|
}
|
|
}
|