test: 段结果 DAO 补 pgsql 驱动空导入并加软删复活路径验证

单测包不经 main,缺驱动空导入导致 go test ./workflow/dao/flow/ 报
"cannot find database driver for specified database type pgsql"。

新增 TestFlowSegmentResultDaoResurrect:验证 DeleteByExecution 软删后,
对同一 (execution_id, node_id, segment_index) 重新 Save 能否清除
deleted_at 使 ListByNode 再次可见。实测 upsert 的 DO UPDATE SET 子句
不含 deleted_at,段2 不复活 —— 留作失败证据供控制器裁决。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-08-25 10:34:14 +08:00
co-authored by Claude Opus 4.7
parent bae916e019
commit d984cba714
@@ -7,6 +7,10 @@ import (
"time"
"gitea.redpowerfuture.com/red-future/common/beans"
// 空导入:pgsql 驱动。main.go 里已有,但 go test ./workflow/dao/flow/ 单测包不经 main
// 不加则报 "cannot find database driver for specified database type pgsql"
_ "github.com/gogf/gf/contrib/drivers/pgsql/v2"
)
// TestFlowSegmentResultDao 段结果 DAO 集成测试:Save / ListByNode / DeleteByExecution。
@@ -58,3 +62,52 @@ func TestFlowSegmentResultDao(t *testing.T) {
t.Fatalf("删除后应无段结果,实际 %d", len(m))
}
}
// TestFlowSegmentResultDaoResurrect 软删→重存的"复活"路径验证。
// 实体嵌入 beans.SQLBaseDO(含 deleted_at),GoFrame 将其识别为软删除列:
// DeleteByExecution 实际执行的是 UPDATE ... SET deleted_at=now(),而非物理 DELETE。
// 本测试验证随后对同一 (execution_id, node_id, segment_index) 的 Save(命中
// ON CONFLICT ... DO UPDATE)是否会清除 deleted_at,使 ListByNode 再次可见该段。
// 若不可见,说明 upsert 的 SET 子句不含 deleted_at,复活路径被破坏 → 续跑复用静默失效。
func TestFlowSegmentResultDaoResurrect(t *testing.T) {
if os.Getenv("AI_AGENT_TEST_DB") == "" {
t.Skip("设置 AI_AGENT_TEST_DB=1 且本地 black_deacon 可用时运行")
}
ctx := context.WithValue(context.Background(), "user", &beans.User{UserName: "unit_test", TenantId: 999999})
execId := time.Now().UnixMilli()
nodeId := "seg_test_resurrect"
dao := FlowSegmentResultDao
// 软删即可,物理残留由外部硬清理(docker exec psql DELETE FROM)负责
t.Cleanup(func() { _ = dao.DeleteByExecution(ctx, execId) })
if err := dao.Save(ctx, execId, nodeId, 1, "video_url", "http://host/u1-resurrect.mp4"); err != nil {
t.Fatalf("Save 段1失败: %v", err)
}
if err := dao.Save(ctx, execId, nodeId, 2, "video_url", "http://host/u2-resurrect.mp4"); err != nil {
t.Fatalf("Save 段2失败: %v", err)
}
// 软删除整条执行
if err := dao.DeleteByExecution(ctx, execId); err != nil {
t.Fatalf("DeleteByExecution失败: %v", err)
}
// 重新保存段2(同一 exec/node/idx,不同 URL):
// 若 ON CONFLICT DO UPDATE 的 SET 子句不含 deleted_at,段2 在 ListByNode 中仍不可见
if err := dao.Save(ctx, execId, nodeId, 2, "video_url", "http://host/u2-resurrect.mp4"); err != nil {
t.Fatalf("Save 段2(软删后重存)失败: %v", err)
}
m, err := dao.ListByNode(ctx, execId, nodeId)
if err != nil {
t.Fatalf("ListByNode失败: %v", err)
}
// 期望:段2 复活(deleted_at 被 upsert 清除),len(m)==2 且 m[2] 为更新后的 URL
if len(m) != 2 {
t.Fatalf("软删后重存应复活段2(len=2),实际 len=%d: %+v", len(m), m)
}
if m[2].URL != "http://host/u2-resurrect.mp4" {
t.Fatalf("段2 应恢复为更新后的 URL,实际: %+v", m[2])
}
}