package projectinstance_test import ( "context" "testing" "time" "chat-agent-system/internal/projectinstance" "chat-agent-system/internal/projectworkflow" "chat-agent-system/internal/workflowdef" ) // TestPG_ActivateOfficialWorkflowRuntimeMetadata 回归激活链路: // ActivateInstance.applyRuntimeMetadata 会遍历文档任务实例并逐条 UPDATE。 // 若在同一事务连接上 rows 未读完时就执行 Exec,pgx 会返回 "conn busy"。 // 本测试使用正式商务对接流程(3 阶段 7 任务)覆盖该路径。 func TestPG_ActivateOfficialWorkflowRuntimeMetadata(t *testing.T) { pool := openPGPool(t) defer pool.Close() ctx := context.Background() wfStore := workflowdef.NewStore(pool) def, err := workflowdef.LoadManifestDefinition("main.v2.json") if err != nil { t.Fatalf("load main.v2 manifest: %v", err) } if err := wfStore.UpsertPublishedSeed(ctx, workflowdef.CreateDefinitionInput{ Key: workflowdef.OfficialKeyMain, Name: "商务对接流程", Definition: def, }, 1); err != nil { t.Fatalf("seed official main workflow: %v", err) } pwStore := projectworkflow.NewStore(pool) instStore := projectinstance.NewStore(pool) svc := projectinstance.NewService(instStore, wfStore, pwStore, nil) var projectID int64 code := "pg-official-" + time.Now().Format("150405.000") err = pool.QueryRow(ctx, ` INSERT INTO projects (code, name, status, creator_user_id, lifecycle_status, active_workflow_key, workflow_selection_version, workflow_plan_revision) VALUES ($1, $2, 'active', 1, 'planning', '', 1, 0) RETURNING id `, code, "PG Official Activation Test "+code).Scan(&projectID) if err != nil { t.Fatalf("insert project: %v", err) } t.Cleanup(func() { _, _ = pool.Exec(context.Background(), `DELETE FROM projects WHERE id = $1`, projectID) }) plan, err := svc.BuildDefaultPlan(ctx, workflowdef.OfficialKeyMain, 0) if err != nil { t.Fatalf("default plan: %v", err) } for i := range plan.Stages { for j := range plan.Stages[i].DocTasks { plan.Stages[i].DocTasks[j].Duties = projectinstance.DocDuty{ AuthorUserID: 1, ReviewerUserID: 2, ApproverUserID: 3, } } } if _, err := svc.SaveWorkflowPlan(ctx, projectID, 1, true, projectinstance.SavePlanInput{ Plan: *plan, ExpectedPlanRevision: 0, ExpectedSelectionVersion: 1, }); err != nil { t.Fatalf("save plan: %v", err) } preview, err := svc.ActivationPreview(ctx, projectID, 1, true) if err != nil { t.Fatalf("preview: %v", err) } activated, err := svc.Activate(ctx, projectID, 1, true, projectinstance.ActivateInput{ ConfirmationToken: preview.ConfirmationToken, ExpectedProjectVersion: preview.ExpectedProjectVersion, ExpectedPlanRevision: preview.ExpectedPlanRevision, }) if err != nil { t.Fatalf("activate: %v", err) } if activated.Instance.Status != projectinstance.InstanceActive { t.Fatalf("expected active instance, got %q", activated.Instance.Status) } // 运行时元数据应写回文档任务实例(回归 applyRuntimeMetadata 的逐条 UPDATE)。 var executorKey string err = pool.QueryRow(ctx, ` SELECT dti.executor_key FROM project_doc_task_instances dti JOIN project_stage_instances si ON si.id = dti.stage_instance_id WHERE si.workflow_instance_id = $1 AND dti.task_key IN ('intake', 'bd_intake') `, activated.Instance.ID).Scan(&executorKey) if err != nil { t.Fatalf("query runtime metadata: %v", err) } if executorKey != "bizdev.intake.v1" { t.Fatalf("expected executor_key bizdev.intake.v1, got %q", executorKey) } }