修复 go test -short 在设置 TEST_DATABASE_URL 时的既有欠债: - storage/notification/docgov:移除硬编码 agent_system_dev 回退 DSN,改为 无 TEST_DATABASE_URL 即 skip;固定项目名改为 name||code 唯一化。 - docgov engine_db:改为隔离库 + 自建项目行,替换随机 projectID 撞外键的旧写法。 - server/*_pg、projectinstance/pg_integration:迁移到官方 main 工作流 (BuildDefaultPlan official-only 治理后的正确前提),补齐在任成员 (DUTY_ASSIGNEE_NOT_ACTIVE_MEMBER)、任务键 bd_intake、阶段键 V0.1_triage、 项目作用域 run record(events.ValidateEnvelope 拒绝 project<=0)、 task_run_resume 去除内嵌 dev 凭证的 config 硬编码。 - 新增 seedProjectForRun 测试辅助。 验证:fresh 隔离库(55432)上 go test -short -p 1 ./... 除 archtest (另行注册表分支修复)外全部通过;包内并行迁移竞争需 -p 1。
110 lines
3.5 KiB
Go
110 lines
3.5 KiB
Go
package server_test
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
|
|
"chat-agent-system/internal/bizdev"
|
|
"chat-agent-system/internal/stream"
|
|
"chat-agent-system/internal/taskrun"
|
|
)
|
|
|
|
func TestPhase5_FinalizeFaultInjection_NoDoneEvent(t *testing.T) {
|
|
m := taskrun.NewManager(nil, nil)
|
|
m.SetExecutor(
|
|
func(_ context.Context, _ string, _ bizdev.TaskRunRequest, _ *stream.Streamer) (*bizdev.TaskRunResult, error) {
|
|
return &bizdev.TaskRunResult{Success: true, TaskKey: "intake"}, nil
|
|
},
|
|
nil, nil,
|
|
)
|
|
m.SetFinalizer(func(_ context.Context, _ *taskrun.RunRecord, _ *bizdev.TaskRunResult) (string, error) {
|
|
return "", errors.New("injected finalize db failure")
|
|
})
|
|
|
|
runID := "run_finalize_fault"
|
|
record := &taskrun.RunRecord{RunID: runID, Status: "running", TaskKey: "intake"}
|
|
_, _ = m.CreateAndStart(context.Background(), runID, record, bizdev.TaskRunRequest{TaskKey: "intake"}, bizdev.StageRunRequest{}, "")
|
|
|
|
deadline := time.Now().Add(3 * time.Second)
|
|
for time.Now().Before(deadline) {
|
|
rec, err := m.GetRun(runID)
|
|
if err == nil && rec.Status == "finalizing_failed" {
|
|
return
|
|
}
|
|
time.Sleep(20 * time.Millisecond)
|
|
}
|
|
t.Fatal("expected finalizing_failed after injected finalize fault")
|
|
}
|
|
|
|
func TestPhase5_RollingRestartReplay_PG(t *testing.T) {
|
|
pool := openPGPoolWithMigrations(t)
|
|
if pool == nil {
|
|
return
|
|
}
|
|
defer pool.Close()
|
|
|
|
handler, srv := setupPGGovernanceServer(t, pool)
|
|
token := loginTestUser(t, handler, "zhanggong", "123456")
|
|
userID := fetchAuthUserID(t, handler, token)
|
|
|
|
store := taskrun.NewPGStore(pool)
|
|
runID := "run_rolling_" + time.Now().Format("150405.000000")
|
|
srv.TaskRuns().SetStore(store)
|
|
srv.TaskRuns().SetExecutor(
|
|
func(_ context.Context, _ string, _ bizdev.TaskRunRequest, st *stream.Streamer) (*bizdev.TaskRunResult, error) {
|
|
if st != nil {
|
|
st.EmitPhase("roll", "step", "agent", 1, 1, 10)
|
|
}
|
|
return &bizdev.TaskRunResult{Success: true, TaskKey: "intake"}, nil
|
|
},
|
|
nil, nil,
|
|
)
|
|
srv.TaskRuns().SetFinalizer(func(_ context.Context, _ *taskrun.RunRecord, _ *bizdev.TaskRunResult) (string, error) {
|
|
return "completed", nil
|
|
})
|
|
|
|
projectID := seedProjectForRun(t, pool, "run-roll", userID)
|
|
record := &taskrun.RunRecord{
|
|
RunID: runID, SessionID: "sess_roll", ProjectID: projectID, UserID: userID, TaskKey: "intake", Status: "running",
|
|
}
|
|
_, _ = srv.TaskRuns().CreateAndStart(context.Background(), runID, record, bizdev.TaskRunRequest{TaskKey: "intake"}, bizdev.StageRunRequest{}, "")
|
|
t.Cleanup(func() {
|
|
_, _ = pool.Exec(context.Background(), `DELETE FROM task_run_events WHERE run_id = $1`, runID)
|
|
_, _ = pool.Exec(context.Background(), `DELETE FROM task_runs WHERE run_id = $1`, runID)
|
|
})
|
|
|
|
deadline := time.Now().Add(8 * time.Second)
|
|
for time.Now().Before(deadline) {
|
|
rec, err := store.GetRun(context.Background(), runID)
|
|
if err == nil && rec.Status == "completed" {
|
|
break
|
|
}
|
|
time.Sleep(50 * time.Millisecond)
|
|
}
|
|
|
|
// 模拟三次进程重启:新 Manager 仅挂载 Store
|
|
for i := 0; i < 3; i++ {
|
|
restarted := taskrun.NewManager(srv.ProjectWorkflowService(), bizdev.Snapshot(srv.BizdevService()).Ctx)
|
|
restarted.SetStore(store)
|
|
rec, err := restarted.GetRun(runID)
|
|
if err != nil {
|
|
t.Fatalf("restart %d GetRun: %v", i, err)
|
|
}
|
|
if rec.Status != "completed" {
|
|
t.Fatalf("restart %d status=%s", i, rec.Status)
|
|
}
|
|
_, replay, err := restarted.Subscribe(runID, 0)
|
|
if err != nil {
|
|
t.Fatalf("restart %d Subscribe: %v", i, err)
|
|
}
|
|
if len(replay) == 0 {
|
|
t.Fatalf("restart %d expected replay events", i)
|
|
}
|
|
}
|
|
|
|
_ = handler
|
|
_ = token
|
|
}
|