init: 条件流引擎初始提交
This commit is contained in:
@@ -0,0 +1,309 @@
|
||||
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))
|
||||
}
|
||||
Reference in New Issue
Block a user