package workbench import ( "errors" "reflect" "strings" "testing" ) func TestQualitySettingsInvalidateApprovalWithoutChangingAcceptedText(t *testing.T) { s, ctx, job, _ := generationHarness(t) job.Action = "review-settings" before := TaskData{Segments: []Segment{{ID: "a", Status: "accepted", Content: "原文\n必须保留。"}}, Review: "旧报告", Composed: "旧成稿", Quality: &QualityReport{Passed: true}} r := &Resource{Kind: "content-tasks", Status: "needs_configuration", Data: asJSON(before)} settings := &QualitySettings{Rules: "观点不能互相矛盾", Style: "面向初学者"} if err := s.executeTask(ctx, job, r, ActionRequest{QualitySettings: settings}); err != nil { t.Fatal(err) } after, _ := decode[TaskData](r.Data) if after.QualitySettings != *settings || !reflect.DeepEqual(after.Segments, before.Segments) || after.Quality != nil || after.Review != "" || after.Composed != "" { t.Fatalf("settings must invalidate old results while preserving originals: %+v", after) } if err := applyManualUpdate(r, resourceRequest{Name: r.Name, Data: asJSON(before)}); !errors.Is(err, ErrInvalid) { t.Fatalf("manual update bypassed review-settings action: %v", err) } } func TestReviewUsesSavedCriteriaAndBindsApprovalToTheirVersion(t *testing.T) { s, ctx, job, model := generationHarness(t) job.Action = "review" model.output = `{"passed":true,"summary":"已检查指定要求","issues":[]}` d := TaskData{AgentID: "author", QualitySettings: QualitySettings{Rules: "EXACT_CRITERIA", Style: "EXACT_STYLE"}, Segments: []Segment{{ID: "a", Status: "accepted", Content: "已采用正文"}}} r := &Resource{Kind: "content-tasks", Data: asJSON(d)} if err := s.executeTask(ctx, job, r, ActionRequest{}); err != nil { t.Fatal(err) } after, _ := decode[TaskData](r.Data) if len(model.messages) != 2 || !strings.Contains(model.messages[1].Content, "EXACT_CRITERIA") || !strings.Contains(model.messages[1].Content, "EXACT_STYLE") || after.Quality.SettingsHash != d.QualitySettings.hash() { t.Fatal("review did not use or record current rules and style") } accepted, _ := acceptedManuscript(after) if err := requireQuality(after, accepted); err != nil { t.Fatal(err) } after.QualitySettings.Style = "new style" if !errors.Is(requireQuality(after, accepted), ErrInvalid) { t.Fatal("stale review accepted after criteria changed") } } func TestCompositionPreservesShortAndLongTextWithoutDependencies(t *testing.T) { for _, count := range []int{1, 5000} { s, ctx, job, _ := generationHarness(t) job.Action = "compose" s.models, s.agents, s.knowledgeBases = nil, nil, nil d := TaskData{FlowID: "deleted-flow", AgentID: "deleted-agent", Segments: []Segment{ {ID: "a", Title: "不得添加的标题", Status: "accepted", Content: " " + strings.Repeat("第一段。\n", count)}, {ID: "skip", Status: "skipped", Content: "跳过的正文"}, {ID: "c", Status: "accepted", Content: "下一段。 "}, }} accepted, _ := acceptedManuscript(d) d.Quality = &QualityReport{Passed: true, ManuscriptHash: manuscriptHash(accepted)} r := &Resource{Kind: "content-tasks", Status: "needs_configuration", Data: asJSON(d)} if err := s.executeTask(ctx, job, r, ActionRequest{}); err != nil { t.Fatal(err) } after, _ := decode[TaskData](r.Data) if after.Composed != d.Segments[0].Content+"\n\n"+d.Segments[2].Content || r.Status != "completed" { t.Fatal("composition changed accepted text or canvas order") } } }