From 9ed5987104f393ad634475a4acfa40fb5ad40aef Mon Sep 17 00:00:00 2001 From: weichen Date: Sun, 13 Sep 2026 10:00:45 +0000 Subject: [PATCH] test(bizdev): drain pending confirmations in full-link stage E2E Stage chain now stops on high-risk candidates; full-link mirrors the product path of confirm + re-run until success or DocGov proposal intercept. Co-authored-by: Cursor --- internal/bizdev/full_link_e2e_test.go | 83 ++++++++++++++++++++++----- 1 file changed, 70 insertions(+), 13 deletions(-) diff --git a/internal/bizdev/full_link_e2e_test.go b/internal/bizdev/full_link_e2e_test.go index 68e7af7d..4e42d9f9 100644 --- a/internal/bizdev/full_link_e2e_test.go +++ b/internal/bizdev/full_link_e2e_test.go @@ -9,9 +9,75 @@ import ( "chat-agent-system/internal/bizdev" "chat-agent-system/internal/config" "chat-agent-system/internal/docgov" + "chat-agent-system/internal/docrevision" "chat-agent-system/internal/stream" ) +// executeStageDrainingPending runs ExecuteStage, confirming high-risk agent candidates +// (and projecting fact source) until the stage succeeds or stops on a DocGov proposal intercept. +// This mirrors the product path: confirm on the page, then re-run the stage. +func executeStageDrainingPending( + t *testing.T, + ctx context.Context, + svc *bizdev.Service, + req bizdev.StageRunRequest, + authorID int64, + dataRoot string, +) *bizdev.StageRunResult { + t.Helper() + for attempt := 0; attempt < 16; attempt++ { + res, err := svc.ExecuteStage(ctx, req) + if err != nil { + t.Fatalf("ExecuteStage attempt %d: %v", attempt, err) + } + if res.Success { + return res + } + for _, sub := range res.SubArtifacts { + if sub != nil && sub.IsProposalIntercept() { + return res + } + } + if res.PendingAgentCandidateID <= 0 { + t.Fatalf("stage failed without pending/proposal: %s", res.ErrorReason) + } + var pending *bizdev.TaskRunResult + for _, sub := range res.SubArtifacts { + if sub != nil && sub.AwaitsAgentConfirmation() { + pending = sub + break + } + } + if pending == nil || pending.DocRecord == nil || pending.DocRecord.DocID == "" { + t.Fatalf("pending stage missing DocRecord: %+v", res) + } + docID := pending.DocRecord.DocID + st, err := svc.DocRevision().GetRevisionState(ctx, docID) + if err != nil { + t.Fatalf("GetRevisionState(%s): %v", docID, err) + } + st2, err := svc.DocRevision().CommitAgentVersion(ctx, docID, pending.PendingAgentCandidateID, docrevision.CommitAgentDecision{ + Decision: "commit", Confirmed: true, ActorUserID: authorID, + ExpectedInputSHA: st.HeadSHA256, + }) + if err != nil { + t.Fatalf("CommitAgentVersion(%s,#%d): %v", docID, pending.PendingAgentCandidateID, err) + } + if err := svc.DocRevision().ProjectOutputMarkdown(docID, dataRoot); err != nil { + t.Fatalf("ProjectOutputMarkdown(%s): %v", docID, err) + } + if st2 != nil && st2.HeadSHA256 != "" { + if err := svc.DocGov().MarkDocumentContentUpdated(ctx, docID, st2.HeadSHA256, false); err != nil { + t.Fatalf("MarkDocumentContentUpdated: %v", err) + } + } + t.Logf("confirmed pending candidate #%d on %s; re-running stage (attempt %d)", + pending.PendingAgentCandidateID, docID, attempt+1) + } + t.Fatal("stage still unresolved after max pending confirm/retry loops") + return nil +} + func TestBizDev_FullLink_V01_V05_V10_E2E(t *testing.T) { cfg := &config.Config{ DataDir: t.TempDir(), @@ -114,12 +180,9 @@ func TestBizDev_FullLink_V01_V05_V10_E2E(t *testing.T) { ApproverName: approverName, } - resV05, err := svc.ExecuteStage(ctx, stageReqV05) - if err != nil { - t.Fatalf("Stage V0.5 execution failed: %v", err) - } + resV05 := executeStageDrainingPending(t, ctx, svc, stageReqV05, authorID, cfg.DataDir) if !resV05.Success { - t.Fatalf("Stage V0.5 should succeed, got error: %s", resV05.ErrorReason) + t.Fatalf("Stage V0.5 should succeed after confirming pendings, got error: %s", resV05.ErrorReason) } if len(resV05.SubArtifacts) != 5 { t.Fatalf("Stage V0.5 should produce 5 sub-artifacts, got %d", len(resV05.SubArtifacts)) @@ -167,10 +230,7 @@ func TestBizDev_FullLink_V01_V05_V10_E2E(t *testing.T) { stageReqV05Modified := stageReqV05 stageReqV05Modified.TargetMarket = "中国NMPA二类 + 欧盟CE MDR" - resV05Intercepted, err := svc.ExecuteStage(ctx, stageReqV05Modified) - if err != nil { - t.Fatalf("ExecuteStage returned unexpected runtime error: %v", err) - } + resV05Intercepted := executeStageDrainingPending(t, ctx, svc, stageReqV05Modified, authorID, cfg.DataDir) if resV05Intercepted.Success { t.Fatalf("Stage V0.5 re-run on claimed document should be intercepted (Success=false)") } @@ -217,10 +277,7 @@ func TestBizDev_FullLink_V01_V05_V10_E2E(t *testing.T) { stageReqV05Force := stageReqV05Modified stageReqV05Force.ForceDirectEdit = true - resV05Updated, err := svc.ExecuteStage(ctx, stageReqV05Force) - if err != nil { - t.Fatalf("Stage V0.5 force re-run failed: %v", err) - } + resV05Updated := executeStageDrainingPending(t, ctx, svc, stageReqV05Force, authorID, cfg.DataDir) if !resV05Updated.Success { t.Fatalf("Stage V0.5 force re-run should succeed, error: %s", resV05Updated.ErrorReason) }