package tests import ( "fmt" "testing" "git.fsdpf.net/go/condflow" "git.fsdpf.net/go/condflow/tests/fixtures" "git.fsdpf.net/go/condition" "git.fsdpf.net/go/contracts/base" "git.fsdpf.net/go/reflux" "git.fsdpf.net/go/reflux/fieldx" "git.fsdpf.net/go/req" "git.fsdpf.net/go/req/resx" "github.com/samber/do/v2" "github.com/stretchr/testify/suite" ) // CondFlowTestSuite 条件流测试套件 type CondFlowTestSuite struct { suite.Suite app do.Injector flow *condflow.CondFlow executor condflow.ActionInvoker } // SetupTest 在每个测试前运行 func (s *CondFlowTestSuite) SetupTest() { // 初始化 DI 容器 s.app = do.New() // 创建 executor approveRes := resx.New(s.app, "Approval", "approvals", resx.WithUuid("550e8400-e29b-41d4-a716-446655440001"), resx.WithName("审批"), resx.WithFields( resx.NewResField("id", "", resx.FieldWithName("ID"), resx.FieldWithDataType(req.ResInteger)), resx.NewResField("created_user", "", resx.FieldWithName("创建者"), resx.FieldWithDefault("00000000-0000-0000-0000-000000000000"), resx.FieldWithDataType(req.ResString)), resx.NewResField("owned_user", "", resx.FieldWithName("拥有者"), resx.FieldWithDefault("00000000-0000-0000-0000-000000000000"), resx.FieldWithDataType(req.ResString)), resx.NewResField("created_at", "", resx.FieldWithName("创建时间"), resx.FieldWithDefault("sql:CURRENT_TIMESTAMP"), resx.FieldWithDataType(req.ResTimestamp)), resx.NewResField("updated_at", "", resx.FieldWithName("更新时间"), resx.FieldWithDefault("sql:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"), resx.FieldWithDataType(req.ResTimestamp)), resx.NewResField("uuid", "", resx.FieldWithName("UUID"), resx.FieldWithDefault("sql:uuid()"), resx.FieldWithDataType(req.ResString)), resx.NewResField("status", "", resx.FieldWithName("状态"), resx.FieldWithDefault("pending"), resx.FieldWithDataType(req.ResString)), resx.NewResField("amount", "", resx.FieldWithName("审批金额"), resx.FieldWithDefault("0"), resx.FieldWithDataType(req.ResDecimal)), resx.NewResField("approver_role", "", resx.FieldWithName("审批人角色"), resx.FieldWithDataType(req.ResString)), resx.NewResField("approval_method", "", resx.FieldWithName("审批方式"), resx.FieldWithDataType(req.ResString)), ), ) s.executor = condflow.NewActionInvoker(fixtures.NewApproveExecutor(approveRes), nil) do.ProvideNamedValue(s.app, fmt.Sprintf("%s/ApproveExecutor", s.executor.Res().GetCode()), s.executor) // 通过字符串验证预期的 action 是否注册成功 expectedActions := []string{ "AutoApprove", "Manager", "Director", "Reject", } for _, actionName := range expectedActions { _, err := s.executor.GetAction(actionName) s.Require().NoError(err, "获取 action '%s' 失败", actionName) } // 创建条件流 s.flow = condflow.New("test-flow") } // TearDownTest 在每个测试后运行 func (s *CondFlowTestSuite) TearDownTest() { if s.app != nil { s.app.Shutdown() } } // TestSmallAmountAutoApproval 测试小额自动审批 func (s *CondFlowTestSuite) TestSmallAmountAutoApproval() { // 创建条件: 金额 <= 1000 cond := condition.New(condition.Describe("小额自动审批")) cond.SetExpr(condition.NewExpr("@", "amount", condition.Operator(condition.LE), condition.Token("1000", condition.STRING))) config, _ := fieldx.SchemaFromMap(map[string]any{ "notify": map[string]any{ "type": "string", "value": "true", }, "message": map[string]any{ "type": "string", "value": "申请已自动通过", }, }) // 创建条件分支,使用 action 名称字符串 condCase := condflow.NewCondCase("auto-approve-small", cond, "Approval/ApproveExecutor@AutoApprove"). WithPriority(100). WithActionConfig(config) // 添加到流程 s.flow.AddCase(condCase) // 准备输入数据 input := reflux.New(map[string]any{ "amount": 500.0, "user": map[string]any{ "id": 1, "credit_score": 80, }, }) // 执行流程 err := s.flow.Run(s.app, input, base.GetSystemUser()) s.NoError(err) } // TestManagerApproval 测试经理审批 func (s *CondFlowTestSuite) TestManagerApproval() { // 创建条件: 1000 < 金额 <= 10000 cond := condition.New(condition.Describe("经理审批")) cond.SetExpr(condition.NewExpr("@", "amount", condition.Operator(condition.GT), condition.Token("1000", condition.STRING))). SetExpr(condition.NewExpr("@", "amount", condition.Operator(condition.LE), condition.Token("10000", condition.STRING))) config, _ := fieldx.SchemaFromMap(map[string]any{ "approver_role": map[string]any{ "type": "string", "value": "manager", }, "timeout_hours": map[string]any{ "type": "string", "value": "24", }, }) // 创建条件分支,使用 action 名称字符串 condCase := condflow.NewCondCase("manager-approval", cond, "Approval/ApproveExecutor@Manager"). WithPriority(90). WithActionConfig(config) // 添加到流程 s.flow.AddCase(condCase) // 准备输入数据 input := reflux.New(map[string]any{ "amount": 5000.0, "user": map[string]any{ "id": 1, "credit_score": 80, }, }) // 执行流程 err := s.flow.Run(s.app, input, base.GetSystemUser()) s.NoError(err) } // TestLowCreditReject 测试低信用评分拒绝 func (s *CondFlowTestSuite) TestLowCreditReject() { // 创建条件: 用户信用评分 < 60 cond := condition.New(condition.Describe("信用不足拒绝")) cond.SetExpr(condition.NewExpr("@", "user->>'credit_score'", condition.Operator(condition.LT), condition.Token("60", condition.STRING))) config, _ := fieldx.SchemaFromMap(map[string]any{ "reason": map[string]any{ "type": "string", "value": "信用评分不足", }, "notify": map[string]any{ "type": "string", "value": "true", }, "suggest_retry": map[string]any{ "type": "string", "value": "false", }, }) // 创建条件分支,使用 action 名称字符串 condCase := condflow.NewCondCase("reject-low-credit", cond, "Approval/ApproveExecutor@Reject"). WithPriority(200). WithActionConfig(config) // 添加到流程 s.flow.AddCase(condCase) // 准备输入数据 input := reflux.New(map[string]any{ "amount": 500.0, "user": map[string]any{ "id": 1, "credit_score": 50, }, }) // 执行流程 err := s.flow.Run(s.app, input, base.GetSystemUser()) s.NoError(err) } // TestMultipleCases 测试多个条件分支 func (s *CondFlowTestSuite) TestMultipleCases() { // Case 1: 小额自动审批 case1Cond := condition.New(condition.Describe("小额自动审批")) case1Cond.SetExpr(condition.NewExpr("@", "amount", condition.Operator(condition.LE), condition.Token("1000", condition.STRING))) case1 := condflow.NewCondCase("auto-approve-small", case1Cond, "Approval/ApproveExecutor@AutoApprove"). WithPriority(100) // Case 2: 经理审批 case2Cond := condition.New(condition.Describe("经理审批")) case2Cond.SetExpr(condition.NewExpr("@", "amount", condition.Operator(condition.GT), condition.Token("1000", condition.STRING))). SetExpr(condition.NewExpr("@", "amount", condition.Operator(condition.LE), condition.Token("10000", condition.STRING))) case2 := condflow.NewCondCase("manager-approval", case2Cond, "Approval/ApproveExecutor@Manager"). WithPriority(90) // Case 3: 总监审批 case3Cond := condition.New(condition.Describe("总监审批")) case3Cond.SetExpr(condition.NewExpr("@", "amount", condition.Operator(condition.GT), condition.Token("10000", condition.STRING))) case3 := condflow.NewCondCase("director-approval", case3Cond, "Approval/ApproveExecutor@Director"). WithPriority(80) // Case 4: 低信用拒绝(最高优先级) case4Cond := condition.New(condition.Describe("信用不足拒绝")) case4Cond.SetExpr(condition.NewExpr("@", "user->>'credit_score'", condition.Operator(condition.LT), condition.Token("60", condition.STRING))) case4Config, _ := fieldx.SchemaFromMap(map[string]any{ "reason": map[string]any{ "type": "string", "value": "信用评分不足", }, }) case4 := condflow.NewCondCase("reject-low-credit", case4Cond, "Approval/ApproveExecutor@Reject"). WithPriority(200). WithActionConfig(case4Config) // 添加所有条件分支 s.flow.AddCase(case4). AddCase(case1). AddCase(case2). AddCase(case3) // 测试不同的输入数据 testCases := []struct { name string input map[string]any expect string }{ { name: "小额正常用户", input: map[string]any{ "amount": 500.0, "user": map[string]any{ "id": 1, "credit_score": 80, }, }, expect: "auto-approve", }, { name: "中额正常用户", input: map[string]any{ "amount": 5000.0, "user": map[string]any{ "id": 1, "credit_score": 80, }, }, expect: "manager-approve", }, { name: "大额正常用户", input: map[string]any{ "amount": 15000.0, "user": map[string]any{ "id": 1, "credit_score": 80, }, }, expect: "director-approve", }, { name: "低信用用户", input: map[string]any{ "amount": 500.0, "user": map[string]any{ "id": 1, "credit_score": 50, }, }, expect: "reject", }, } for _, tc := range testCases { s.Run(tc.name, func() { input := reflux.New(tc.input) err := s.flow.Run(s.app, input, base.GetSystemUser()) s.NoError(err) }) } } // TestSuite 运行测试套件 func TestCondFlow(t *testing.T) { suite.Run(t, new(CondFlowTestSuite)) }