Files
Agent-System/internal/server/phase5_finalize_fault_pg_test.go
T
weichen 70882918e5 test(pg): isolate PG integration tests from shared dev database
修复 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。
2026-09-22 01:10:17 +00:00

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
}