Files
ai-agent/workflow/dao/flow/flow_segment_result_dao_test.go
T
19904408334andClaude Opus 4.7 d984cba714 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>
2026-08-25 10:34:14 +08:00

114 lines
4.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package flow
import (
"context"
"os"
"testing"
"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。
// 依赖本地 black_deacon + 用户信息注入;默认跳过,设置 AI_AGENT_TEST_DB=1 时运行。
func TestFlowSegmentResultDao(t *testing.T) {
if os.Getenv("AI_AGENT_TEST_DB") == "" {
t.Skip("设置 AI_AGENT_TEST_DB=1 且本地 black_deacon 可用时运行")
}
// gfdb Insert/Select 钩子从 ctx 读用户信息补 tenant_id/creator 并追加租户过滤
ctx := context.WithValue(context.Background(), "user", &beans.User{UserName: "unit_test", TenantId: 999999})
execId := time.Now().UnixMilli()
nodeId := "seg_test_node"
dao := FlowSegmentResultDao
if err := dao.Save(ctx, execId, nodeId, 1, "video_url", "http://host/u1.mp4"); err != nil {
t.Fatalf("Save 段1失败: %v", err)
}
if err := dao.Save(ctx, execId, nodeId, 2, "video_url", "http://host/u2.mp4"); err != nil {
t.Fatalf("Save 段2失败: %v", err)
}
// 唯一键冲突 → 更新不报错
if err := dao.Save(ctx, execId, nodeId, 2, "video_url", "http://host/u2-new.mp4"); err != nil {
t.Fatalf("Save 段2(冲突更新)失败: %v", err)
}
m, err := dao.ListByNode(ctx, execId, nodeId)
if err != nil {
t.Fatalf("ListByNode失败: %v", err)
}
if len(m) != 2 {
t.Fatalf("期望 2 段,实际 %d", len(m))
}
if m[1].URL != "http://host/u1.mp4" || m[2].URL != "http://host/u2-new.mp4" {
t.Fatalf("段内容不符: %+v", m)
}
if m[2].Key != "video_url" {
t.Fatalf("段2 key 不符: %s", m[2].Key)
}
if err := dao.DeleteByExecution(ctx, execId); err != nil {
t.Fatalf("DeleteByExecution失败: %v", err)
}
m, err = dao.ListByNode(ctx, execId, nodeId)
if err != nil {
t.Fatalf("删除后 ListByNode失败: %v", err)
}
if len(m) != 0 {
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])
}
}