273 lines
7.2 KiB
Go
273 lines
7.2 KiB
Go
package tests
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"fmt"
|
||
|
|
"testing"
|
||
|
|
|
||
|
|
"github.com/rockliang/poimen/workflows/internal/routing"
|
||
|
|
"github.com/rockliang/poimen/workflows/statemachine"
|
||
|
|
"github.com/stretchr/testify/require"
|
||
|
|
"go.temporal.io/sdk/testsuite"
|
||
|
|
)
|
||
|
|
|
||
|
|
func TestRoutingWorkflow_SimpleWorkflow(t *testing.T) {
|
||
|
|
testSuite := &testsuite.WorkflowTestSuite{}
|
||
|
|
env := testSuite.NewTestWorkflowEnvironment()
|
||
|
|
|
||
|
|
// Register mock activity
|
||
|
|
env.RegisterActivity(mockCloneRepoActivity)
|
||
|
|
|
||
|
|
// Create simple workflow spec
|
||
|
|
spec := &routing.WorkflowSpec{
|
||
|
|
Name: "test-workflow",
|
||
|
|
Input: map[string]interface{}{
|
||
|
|
"repo": "https://github.com/test/repo",
|
||
|
|
"branch": "main",
|
||
|
|
},
|
||
|
|
States: []routing.State{
|
||
|
|
{
|
||
|
|
Name: "Clone",
|
||
|
|
Type: routing.StateTypeTask,
|
||
|
|
Resource: "mockCloneRepoActivity",
|
||
|
|
Parameters: map[string]interface{}{
|
||
|
|
"repo": "${input.repo}",
|
||
|
|
"branch": "${input.branch}",
|
||
|
|
},
|
||
|
|
Timeout: "5m",
|
||
|
|
Retry: &routing.RetryPolicy{
|
||
|
|
MaxAttempts: 2,
|
||
|
|
BackoffRate: 1.5,
|
||
|
|
InitialInterval: "1s",
|
||
|
|
},
|
||
|
|
End: true,
|
||
|
|
},
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
input := statemachine.RoutingWorkflowInput{Spec: spec}
|
||
|
|
|
||
|
|
env.ExecuteWorkflow(statemachine.RoutingWorkflow, input)
|
||
|
|
|
||
|
|
require.True(t, env.IsWorkflowCompleted())
|
||
|
|
require.NoError(t, env.GetWorkflowError())
|
||
|
|
|
||
|
|
var output statemachine.RoutingWorkflowOutput
|
||
|
|
require.NoError(t, env.GetWorkflowResult(&output))
|
||
|
|
require.Equal(t, "COMPLETED", output.Status)
|
||
|
|
require.NotNil(t, output.FinalOutput)
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestRoutingWorkflow_MultiStepWorkflow(t *testing.T) {
|
||
|
|
testSuite := &testsuite.WorkflowTestSuite{}
|
||
|
|
env := testSuite.NewTestWorkflowEnvironment()
|
||
|
|
|
||
|
|
// Register mock activities
|
||
|
|
env.RegisterActivity(mockCloneRepoActivity)
|
||
|
|
env.RegisterActivity(mockAnalyzeActivity)
|
||
|
|
|
||
|
|
// Create multi-step workflow spec
|
||
|
|
spec := &routing.WorkflowSpec{
|
||
|
|
Name: "multi-step-workflow",
|
||
|
|
Input: map[string]interface{}{
|
||
|
|
"repo": "https://github.com/test/repo",
|
||
|
|
},
|
||
|
|
States: []routing.State{
|
||
|
|
{
|
||
|
|
Name: "Clone",
|
||
|
|
Type: routing.StateTypeTask,
|
||
|
|
Resource: "mockCloneRepoActivity",
|
||
|
|
Parameters: map[string]interface{}{
|
||
|
|
"repo": "${input.repo}",
|
||
|
|
},
|
||
|
|
Timeout: "5m",
|
||
|
|
Next: "Analyze",
|
||
|
|
},
|
||
|
|
{
|
||
|
|
Name: "Analyze",
|
||
|
|
Type: routing.StateTypeTask,
|
||
|
|
Resource: "mockAnalyzeActivity",
|
||
|
|
Parameters: map[string]interface{}{
|
||
|
|
"path": "${Clone.output.path}",
|
||
|
|
},
|
||
|
|
Timeout: "10m",
|
||
|
|
End: true,
|
||
|
|
},
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
input := statemachine.RoutingWorkflowInput{Spec: spec}
|
||
|
|
|
||
|
|
env.ExecuteWorkflow(statemachine.RoutingWorkflow, input)
|
||
|
|
|
||
|
|
require.True(t, env.IsWorkflowCompleted())
|
||
|
|
require.NoError(t, env.GetWorkflowError())
|
||
|
|
|
||
|
|
var output statemachine.RoutingWorkflowOutput
|
||
|
|
require.NoError(t, env.GetWorkflowResult(&output))
|
||
|
|
t.Logf("Output: %+v", output)
|
||
|
|
t.Logf("Error: %s", output.Error)
|
||
|
|
require.Equal(t, "COMPLETED", output.Status)
|
||
|
|
require.Contains(t, output.StepResults, "Clone")
|
||
|
|
require.Contains(t, output.StepResults, "Analyze")
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestRoutingWorkflow_PassState(t *testing.T) {
|
||
|
|
testSuite := &testsuite.WorkflowTestSuite{}
|
||
|
|
env := testSuite.NewTestWorkflowEnvironment()
|
||
|
|
|
||
|
|
// Create workflow with Pass state
|
||
|
|
spec := &routing.WorkflowSpec{
|
||
|
|
Name: "pass-state-workflow",
|
||
|
|
Input: map[string]interface{}{},
|
||
|
|
States: []routing.State{
|
||
|
|
{
|
||
|
|
Name: "StaticResult",
|
||
|
|
Type: routing.StateTypePass,
|
||
|
|
Result: map[string]interface{}{"status": "ok", "message": "static result"},
|
||
|
|
End: true,
|
||
|
|
},
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
input := statemachine.RoutingWorkflowInput{Spec: spec}
|
||
|
|
|
||
|
|
env.ExecuteWorkflow(statemachine.RoutingWorkflow, input)
|
||
|
|
|
||
|
|
require.True(t, env.IsWorkflowCompleted())
|
||
|
|
require.NoError(t, env.GetWorkflowError())
|
||
|
|
|
||
|
|
var output statemachine.RoutingWorkflowOutput
|
||
|
|
require.NoError(t, env.GetWorkflowResult(&output))
|
||
|
|
require.Equal(t, "COMPLETED", output.Status)
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestRoutingWorkflow_FailState(t *testing.T) {
|
||
|
|
testSuite := &testsuite.WorkflowTestSuite{}
|
||
|
|
env := testSuite.NewTestWorkflowEnvironment()
|
||
|
|
|
||
|
|
// Create workflow with Fail state
|
||
|
|
spec := &routing.WorkflowSpec{
|
||
|
|
Name: "fail-state-workflow",
|
||
|
|
Input: map[string]interface{}{},
|
||
|
|
States: []routing.State{
|
||
|
|
{
|
||
|
|
Name: "HandleError",
|
||
|
|
Type: routing.StateTypeFail,
|
||
|
|
Error: "WorkflowError",
|
||
|
|
Cause: "Something went wrong",
|
||
|
|
},
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
input := statemachine.RoutingWorkflowInput{Spec: spec}
|
||
|
|
|
||
|
|
env.ExecuteWorkflow(statemachine.RoutingWorkflow, input)
|
||
|
|
|
||
|
|
require.True(t, env.IsWorkflowCompleted())
|
||
|
|
require.NoError(t, env.GetWorkflowError())
|
||
|
|
|
||
|
|
var output statemachine.RoutingWorkflowOutput
|
||
|
|
require.NoError(t, env.GetWorkflowResult(&output))
|
||
|
|
require.Equal(t, "FAILED", output.Status)
|
||
|
|
require.Contains(t, output.Error, "WorkflowError")
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestRoutingWorkflow_ErrorCatch(t *testing.T) {
|
||
|
|
testSuite := &testsuite.WorkflowTestSuite{}
|
||
|
|
env := testSuite.NewTestWorkflowEnvironment()
|
||
|
|
|
||
|
|
// Register mock activities
|
||
|
|
env.RegisterActivity(mockFailingActivity)
|
||
|
|
|
||
|
|
// Create workflow with error handling
|
||
|
|
spec := &routing.WorkflowSpec{
|
||
|
|
Name: "error-catch-workflow",
|
||
|
|
Input: map[string]interface{}{},
|
||
|
|
States: []routing.State{
|
||
|
|
{
|
||
|
|
Name: "FlakyStep",
|
||
|
|
Type: routing.StateTypeTask,
|
||
|
|
Resource: "mockFailingActivity",
|
||
|
|
Parameters: map[string]interface{}{},
|
||
|
|
Timeout: "1m",
|
||
|
|
Retry: &routing.RetryPolicy{
|
||
|
|
MaxAttempts: 1,
|
||
|
|
BackoffRate: 1.0,
|
||
|
|
InitialInterval: "1s",
|
||
|
|
},
|
||
|
|
Catch: []routing.CatchClause{
|
||
|
|
{
|
||
|
|
ErrorEquals: []string{"ActivityError"},
|
||
|
|
Next: "HandleError",
|
||
|
|
},
|
||
|
|
},
|
||
|
|
Next: "Success",
|
||
|
|
},
|
||
|
|
{
|
||
|
|
Name: "Success",
|
||
|
|
Type: routing.StateTypePass,
|
||
|
|
Result: "success",
|
||
|
|
End: true,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
Name: "HandleError",
|
||
|
|
Type: routing.StateTypeFail,
|
||
|
|
Error: "CaughtError",
|
||
|
|
Cause: "Activity failed and was caught",
|
||
|
|
},
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
input := statemachine.RoutingWorkflowInput{Spec: spec}
|
||
|
|
|
||
|
|
env.ExecuteWorkflow(statemachine.RoutingWorkflow, input)
|
||
|
|
|
||
|
|
require.True(t, env.IsWorkflowCompleted())
|
||
|
|
require.NoError(t, env.GetWorkflowError())
|
||
|
|
|
||
|
|
var output statemachine.RoutingWorkflowOutput
|
||
|
|
require.NoError(t, env.GetWorkflowResult(&output))
|
||
|
|
require.Equal(t, "FAILED", output.Status)
|
||
|
|
require.Contains(t, output.Error, "CaughtError")
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestRoutingWorkflow_EmptySpec(t *testing.T) {
|
||
|
|
testSuite := &testsuite.WorkflowTestSuite{}
|
||
|
|
env := testSuite.NewTestWorkflowEnvironment()
|
||
|
|
|
||
|
|
// Empty spec
|
||
|
|
input := statemachine.RoutingWorkflowInput{Spec: nil}
|
||
|
|
|
||
|
|
env.ExecuteWorkflow(statemachine.RoutingWorkflow, input)
|
||
|
|
|
||
|
|
require.True(t, env.IsWorkflowCompleted())
|
||
|
|
require.NoError(t, env.GetWorkflowError())
|
||
|
|
|
||
|
|
var output statemachine.RoutingWorkflowOutput
|
||
|
|
require.NoError(t, env.GetWorkflowResult(&output))
|
||
|
|
require.Equal(t, "FAILED", output.Status)
|
||
|
|
require.Contains(t, output.Error, "empty")
|
||
|
|
}
|
||
|
|
|
||
|
|
// Mock activities
|
||
|
|
func mockCloneRepoActivity(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) {
|
||
|
|
return map[string]interface{}{
|
||
|
|
"path": "/tmp/cloned-repo",
|
||
|
|
"commit": "abc123",
|
||
|
|
"branch": "main",
|
||
|
|
}, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func mockAnalyzeActivity(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) {
|
||
|
|
return map[string]interface{}{
|
||
|
|
"quality": 0.85,
|
||
|
|
"issues": []string{},
|
||
|
|
"summary": "Code analysis complete",
|
||
|
|
}, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func mockFailingActivity(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) {
|
||
|
|
return nil, fmt.Errorf("mock activity failure")
|
||
|
|
}
|