test(governance): PI Agent PG 全链路实测通过并固化运行条件
修复 runner 鉴权与 data/dev 工作区挂载,使用正式 main 工作流完成 intake→批准→推进。
This commit is contained in:
@@ -69,6 +69,32 @@ PASS ok chat-agent-system/internal/server 0.514s
|
||||
|
||||
**评价判定**: PASS — 正式工作流 `test_manager` 末阶段完成→`planning`;正式工作流 `main` 激活/DocGov 批准/阶段推进链通过
|
||||
|
||||
### 5.5 PI Agent 真实全链路(隔离 test 库 + MiniMax-M3)
|
||||
|
||||
**环境**:
|
||||
- `TEST_DATABASE_URL` → `agent_system_test@127.0.0.1:5434`
|
||||
- `RUNNER_AUTH_TOKEN` → dev runner(`18865/health` 200)
|
||||
- 工作区挂载 → `data/dev/workspaces`(与 pi-agent 容器共享)
|
||||
|
||||
**命令**:
|
||||
```bash
|
||||
RUNNER_AUTH_TOKEN='dev_runner_secret_token' \
|
||||
TEST_DATABASE_URL='postgres://app:test_secret_db_pass@127.0.0.1:5434/agent_system_test' \
|
||||
go test ./internal/server/... -run TestWorkflowGovernance_PiAgent_PG_FullChain -count=1 -timeout 1h -v
|
||||
```
|
||||
|
||||
**实际输出**:
|
||||
```
|
||||
✓ [1. 项目创建] id=10 code=pg-piagent-治-dbbbb9f7
|
||||
✓ [2. 工作流激活] instance_id=6
|
||||
✓ [3. pi-agent intake] doc=doc_proj10_intake title=用户初始需求收集表 elapsed=6m28s
|
||||
✓ [4. 文档批准] stage ready hook 已触发
|
||||
✓ [5. 阶段推进] PG + pi-agent 治理全链路完成
|
||||
--- PASS: TestWorkflowGovernance_PiAgent_PG_FullChain (387.93s)
|
||||
```
|
||||
|
||||
**评价判定**: PASS — 正式工作流 `main` + 真实 pi-agent intake 三步骤 + DocGov 批准 + 阶段推进
|
||||
|
||||
## 六、 自动化测试套件汇总 (Test Suite Execution)
|
||||
|
||||
```
|
||||
@@ -79,6 +105,6 @@ TEST_DATABASE_URL=... go test ./internal/server/... -run 'TestE2E_WorkflowGovern
|
||||
|
||||
## 七、 测试结论与后续优化建议 (Conclusion & Next Steps)
|
||||
|
||||
- Phase A–C 核心路径与 PG 治理 E2E 已通过;`test_manager`/`main` 正式工作流在隔离 test 库验证 PASS。
|
||||
- 仍待:三流程 + 真实 PI Agent(MiniMax-M3)全链路实测(`TestWorkflowGovernance_PiAgent_PG_FullChain`)写入本报告后再合 main。
|
||||
- Phase A–C 核心路径、PG 治理 E2E 与 PI Agent 真实全链路均已 PASS。
|
||||
- 仍待:`product_manager` 正式工作流单独 PG+PI 链路(可选补测)后合 main。
|
||||
- 建议每周运行 `scripts/fix-completed-instance-lifecycle.sql` dry-run 检查历史不一致。
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -117,6 +118,29 @@ func envOr(key, fallback string) string {
|
||||
return fallback
|
||||
}
|
||||
|
||||
func piAgentSharedDataDir(t *testing.T) string {
|
||||
t.Helper()
|
||||
if d := strings.TrimSpace(os.Getenv("DATA_DIR")); d != "" {
|
||||
return d
|
||||
}
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatalf("getwd: %v", err)
|
||||
}
|
||||
for _, rel := range []string{".", "..", "../..", "../../.."} {
|
||||
base := filepath.Clean(filepath.Join(cwd, rel))
|
||||
for _, envName := range []string{"dev", "test"} {
|
||||
candidate := filepath.Join(base, "data", envName)
|
||||
ws := filepath.Join(candidate, "workspaces")
|
||||
if st, statErr := os.Stat(ws); statErr == nil && st.IsDir() {
|
||||
return candidate
|
||||
}
|
||||
}
|
||||
}
|
||||
t.Skip("shared pi-agent DATA_DIR not found (mount data/test/workspaces or data/dev/workspaces)")
|
||||
return ""
|
||||
}
|
||||
|
||||
func seedOfficialWorkflowManifest(t *testing.T, pool *pgxpool.Pool, workflowKey string) {
|
||||
t.Helper()
|
||||
filename, ok := workflowdef.V2ManifestFiles[workflowKey]
|
||||
|
||||
@@ -17,7 +17,6 @@ import (
|
||||
"chat-agent-system/internal/projectinstance"
|
||||
"chat-agent-system/internal/projectworkflow"
|
||||
"chat-agent-system/internal/server"
|
||||
"chat-agent-system/internal/squad"
|
||||
"chat-agent-system/internal/workflowdef"
|
||||
)
|
||||
|
||||
@@ -34,10 +33,17 @@ func piAgentRunnerHealthy() bool {
|
||||
func setupPGPiAgentServer(t *testing.T, pool *pgxpool.Pool) (http.Handler, *server.Server) {
|
||||
t.Helper()
|
||||
cfg := configFromTestDatabaseURL(t)
|
||||
cfg.DataDir = piAgentSharedDataDir(t)
|
||||
cfg.AgentBackend = "pi-agent"
|
||||
cfg.PiAgentRunnerURL = "http://127.0.0.1:18865"
|
||||
cfg.AgentTimeout = 60 * time.Minute
|
||||
cfg.PiAgentRunnerURL = envOr("PI_AGENT_RUNNER_URL", "http://127.0.0.1:18865")
|
||||
cfg.PiAgentRunnerAuthToken = envOr("PI_AGENT_RUNNER_AUTH_TOKEN", envOr("RUNNER_AUTH_TOKEN", ""))
|
||||
cfg.PiAgentModel = "MiniMax-M3"
|
||||
cfg.PiAgentProvider = "minimax"
|
||||
if cfg.PiAgentRunnerAuthToken == "" {
|
||||
t.Skip("RUNNER_AUTH_TOKEN not set; skipping pi-agent PG E2E")
|
||||
}
|
||||
mergePiAgentE2EKbConfig(cfg)
|
||||
svc := bizdev.MustNewService(t, cfg)
|
||||
if svc.Pool() == nil {
|
||||
t.Skip("bizdev service not connected to postgres")
|
||||
@@ -70,33 +76,8 @@ func TestWorkflowGovernance_PiAgent_PG_FullChain(t *testing.T) {
|
||||
defer ts.Close()
|
||||
client := &http.Client{Timeout: 50 * time.Minute}
|
||||
|
||||
wfSvc := workflowdef.NewService(pool, nil)
|
||||
wfKey := "pg_pi_p5_" + time.Now().Format("150405.000")
|
||||
_, ver, err := wfSvc.CreateDefinition(ctx, workflowdef.CreateDefinitionInput{
|
||||
Key: wfKey, Name: "PG PiAgent Governance WF",
|
||||
Definition: workflowdef.VersionDefinition{
|
||||
SchemaVersion: 1,
|
||||
Stages: []workflowdef.StageDef{{
|
||||
Key: "biz_intake", Name: "Intake", Position: 0,
|
||||
DocTasks: []workflowdef.DocTaskDef{{
|
||||
Key: "intake", Required: true, Blocking: true, DefaultEnabled: true,
|
||||
OutputDocType: "intake", OutputDocTitle: "Customer Intake",
|
||||
}},
|
||||
}, {
|
||||
Key: "biz_feasibility", Name: "Feasibility", Position: 1, Optional: true,
|
||||
DocTasks: []workflowdef.DocTaskDef{{
|
||||
Key: "feasibility_reg", Required: false, Blocking: false, DefaultEnabled: true,
|
||||
OutputDocType: "feasibility", OutputDocTitle: "Regulatory Path",
|
||||
}},
|
||||
}},
|
||||
},
|
||||
}, 1)
|
||||
if err != nil {
|
||||
t.Fatalf("create workflow def: %v", err)
|
||||
}
|
||||
if _, err := wfSvc.PublishVersion(ctx, ver.WorkflowID, ver.Version, 1); err != nil {
|
||||
t.Fatalf("publish workflow: %v", err)
|
||||
}
|
||||
wfKey := workflowdef.OfficialKeyMain
|
||||
seedOfficialWorkflowManifest(t, pool, wfKey)
|
||||
|
||||
creatorToken := loginTestUser(t, handler, "zhanggong", "123456")
|
||||
reviewerToken := loginTestUser(t, handler, "lizhuguan", "123456")
|
||||
@@ -128,7 +109,7 @@ func TestWorkflowGovernance_PiAgent_PG_FullChain(t *testing.T) {
|
||||
t.Logf("✓ [1. 项目创建] id=%d code=%s", projectID, proj.Code)
|
||||
|
||||
// 规划 + 激活
|
||||
reqDefault := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/api/v1/projects/%d/workflow-plan/default?workflow_key=%s&version=1", projectID, wfKey), nil)
|
||||
reqDefault := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/api/v1/projects/%d/workflow-plan/default?workflow_key=%s&version=0", projectID, wfKey), nil)
|
||||
withBearerAuth(reqDefault, creatorToken)
|
||||
recDefault := httptest.NewRecorder()
|
||||
handler.ServeHTTP(recDefault, reqDefault)
|
||||
@@ -176,7 +157,7 @@ func TestWorkflowGovernance_PiAgent_PG_FullChain(t *testing.T) {
|
||||
instanceID := activated.Instance.ID
|
||||
t.Logf("✓ [2. 工作流激活] instance_id=%d", instanceID)
|
||||
|
||||
// pi-agent 执行 intake(精简单步 WBS)
|
||||
// pi-agent 执行 intake(默认三步骤 WBS,工作区挂载 data/*/workspaces 供 runner 读取)
|
||||
taskStart := time.Now()
|
||||
taskBody, _ := json.Marshal(server.TaskRunRequestDTO{
|
||||
TaskKey: "intake",
|
||||
@@ -189,10 +170,15 @@ func TestWorkflowGovernance_PiAgent_PG_FullChain(t *testing.T) {
|
||||
AuthorID: creatorID,
|
||||
ReviewerID: reviewerID,
|
||||
ApproverID: approverID,
|
||||
CustomSteps: []squad.StepSpec{{
|
||||
ID: "step_01_intake", Role: "writer", Title: "Intake snapshot",
|
||||
SubWorkspace: "tasks/01_intake", Mode: "execute",
|
||||
Accept: []string{"Deliver output.md with project intake summary"},
|
||||
Fields: map[string]string{
|
||||
"customer_name": "PG PiAgent 治理链客户",
|
||||
"device_type": proj.DeviceType,
|
||||
"intended_use": "Tissue resection",
|
||||
"target_market": "NMPA Class III",
|
||||
},
|
||||
Materials: []bizdev.MaterialInput{{
|
||||
Filename: "project_brief.md",
|
||||
Content: []byte("# 项目简报\n\n- 产品:超声软组织手术系统\n- 目标:NMPA III 类注册路径摸底\n- 关键需求:双频刀头、灌注与负压吸引\n"),
|
||||
}},
|
||||
})
|
||||
reqTask, _ := http.NewRequest(http.MethodPost, ts.URL+"/api/v1/tasks/run", bytes.NewReader(taskBody))
|
||||
@@ -259,9 +245,9 @@ func TestWorkflowGovernance_PiAgent_PG_FullChain(t *testing.T) {
|
||||
}
|
||||
|
||||
advanceBody, _ := json.Marshal(projectinstance.AdvanceStageInput{
|
||||
StageKey: "biz_intake", ExpectedConfigRevision: activated.Instance.ConfigRevision,
|
||||
StageKey: "V0.1_triage", ExpectedConfigRevision: activated.Instance.ConfigRevision,
|
||||
})
|
||||
reqAdvance := httptest.NewRequest(http.MethodPost, fmt.Sprintf("/api/v1/workflow-instances/%d/stages/biz_intake/advance", instanceID), bytes.NewReader(advanceBody))
|
||||
reqAdvance := httptest.NewRequest(http.MethodPost, fmt.Sprintf("/api/v1/workflow-instances/%d/stages/V0.1_triage/advance", instanceID), bytes.NewReader(advanceBody))
|
||||
withBearerAuth(reqAdvance, creatorToken)
|
||||
recAdvance := httptest.NewRecorder()
|
||||
handler.ServeHTTP(recAdvance, reqAdvance)
|
||||
|
||||
Reference in New Issue
Block a user