package projectinstance_test import ( "context" "testing" "time" "chat-agent-system/internal/projectinstance" "chat-agent-system/internal/projectworkflow" "chat-agent-system/internal/workflowdef" ) // TestPG_RDevActivateWithNAClose 激活 rdev(60 任务)并将 rd_sw_plan 规划关闭。 func TestPG_RDevActivateWithNAClose(t *testing.T) { pool := openPGPool(t) defer pool.Close() ctx := context.Background() wfStore := workflowdef.NewStore(pool) def, err := workflowdef.LoadManifestDefinition("rdev.v2.json") if err != nil { t.Fatalf("load rdev manifest: %v", err) } if err := wfStore.UpsertPublishedSeed(ctx, workflowdef.CreateDefinitionInput{ Key: workflowdef.OfficialKeyRDev, Name: "研发设计控制流程", Definition: def, }, 1); err != nil { t.Fatalf("seed rdev: %v", err) } pwStore := projectworkflow.NewStore(pool) instStore := projectinstance.NewStore(pool) svc := projectinstance.NewService(instStore, wfStore, pwStore, nil) var projectID int64 code := "pg-rdev-" + 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, 'PG RDev Activation', 'active', 1, 'planning', '', 1, 0) RETURNING id `, 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.OfficialKeyRDev, 0) if err != nil { t.Fatalf("default plan: %v", err) } if len(plan.Stages) != 13 { t.Fatalf("want 13 stages, got %d", len(plan.Stages)) } taskCount := 0 for i := range plan.Stages { for j := range plan.Stages[i].DocTasks { taskCount++ plan.Stages[i].DocTasks[j].Duties = projectinstance.DocDuty{ AuthorUserID: 1, ReviewerUserID: 2, ApproverUserID: 3, } if plan.Stages[i].DocTasks[j].TaskKey == "rd_sw_plan" { plan.Stages[i].DocTasks[j].Enabled = false } } } if taskCount != 60 { t.Fatalf("want 60 tasks, got %d", taskCount) } 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, got %q", activated.Instance.Status) } if activated.Instance.ConfigSnapshot.WorkflowDefinitionKey != workflowdef.OfficialKeyRDev { t.Fatalf("key=%s", activated.Instance.ConfigSnapshot.WorkflowDefinitionKey) } var executorKey string var enabled bool err = pool.QueryRow(ctx, ` SELECT dti.executor_key, dti.enabled 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 = 'rd_proposal' `, activated.Instance.ID).Scan(&executorKey, &enabled) if err != nil { t.Fatalf("query rd_proposal: %v", err) } if executorKey != "squad.rdev_doc.v1" || !enabled { t.Fatalf("rd_proposal executor=%q enabled=%v", executorKey, enabled) } err = pool.QueryRow(ctx, ` SELECT dti.enabled 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 = 'rd_sw_plan' `, activated.Instance.ID).Scan(&enabled) if err != nil { t.Fatalf("query rd_sw_plan: %v", err) } if enabled { t.Fatal("rd_sw_plan should be disabled (N/A planning close)") } }