Files

481 lines
10 KiB
Go

package lib
import (
"context"
"testing"
"time"
)
func TestEval_SimpleExpression(t *testing.T) {
result, err := Eval(`1 + 1`, nil)
if err != nil {
t.Fatalf("Eval failed: %v", err)
}
// Goja returns int64 for integer expressions
if val, ok := result.(int64); !ok || val != 2 {
t.Errorf("Expected 2, got %v", result)
}
}
func TestEval_EmptyExpression(t *testing.T) {
_, err := Eval("", nil)
if err != ErrEmptyExpression {
t.Errorf("Expected ErrEmptyExpression, got %v", err)
}
}
func TestEval_StringFunction(t *testing.T) {
result, err := Eval(`trim(" hello ")`, nil)
if err != nil {
t.Fatalf("Eval failed: %v", err)
}
if result != "hello" {
t.Errorf("Expected 'hello', got %v", result)
}
}
func TestEval_WithContext(t *testing.T) {
result, err := Eval(`trim(input)`, &EvalOptions{
Context: map[string]interface{}{"input": " test "},
})
if err != nil {
t.Fatalf("Eval failed: %v", err)
}
if result != "test" {
t.Errorf("Expected 'test', got %v", result)
}
}
func TestEvalCondition_True(t *testing.T) {
result, err := EvalCondition(`1 < 2`, nil)
if err != nil {
t.Fatalf("EvalCondition failed: %v", err)
}
if !result {
t.Error("Expected true, got false")
}
}
func TestEvalCondition_False(t *testing.T) {
result, err := EvalCondition(`1 > 2`, nil)
if err != nil {
t.Fatalf("EvalCondition failed: %v", err)
}
if result {
t.Error("Expected false, got true")
}
}
func TestEvalCondition_Empty(t *testing.T) {
_, err := EvalCondition("", nil)
if err != ErrEmptyExpression {
t.Errorf("Expected ErrEmptyExpression, got %v", err)
}
}
func TestEvalCondition_WithContext(t *testing.T) {
result, err := EvalCondition(`value > 5`, &EvalOptions{
Context: map[string]interface{}{"value": 10},
})
if err != nil {
t.Fatalf("EvalCondition failed: %v", err)
}
if !result {
t.Error("Expected true, got false")
}
}
func TestEvalFunction(t *testing.T) {
result, err := EvalFunction(`1 + 2`)
if err != nil {
t.Fatalf("EvalFunction failed: %v", err)
}
// Goja returns int64 for integer expressions
if val, ok := result.(int64); !ok || val != 3 {
t.Errorf("Expected 3, got %v", result)
}
}
func TestEvalFunctionWithContext(t *testing.T) {
result, err := EvalFunctionWithContext(`split(text, ",")`, map[string]interface{}{
"text": "a,b,c",
})
if err != nil {
t.Fatalf("EvalFunctionWithContext failed: %v", err)
}
// Otto's split returns []string
arr, ok := result.([]string)
if !ok {
t.Fatalf("Expected []string, got %T", result)
}
if len(arr) != 3 {
t.Errorf("Expected 3 elements, got %d", len(arr))
}
}
func TestParseWorkflow_Valid(t *testing.T) {
yaml := `
name: test-workflow
kind: module
steps:
- name: echo
type: bash
command: echo hello
`
workflow, err := ParseWorkflow(yaml)
if err != nil {
t.Fatalf("ParseWorkflow failed: %v", err)
}
if workflow.Name != "test-workflow" {
t.Errorf("Expected name 'test-workflow', got %q", workflow.Name)
}
if !workflow.IsModule() {
t.Error("Expected workflow to be a module")
}
if len(workflow.Steps) != 1 {
t.Errorf("Expected 1 step, got %d", len(workflow.Steps))
}
}
func TestParseWorkflow_Empty(t *testing.T) {
_, err := ParseWorkflow("")
if err != ErrEmptyWorkflow {
t.Errorf("Expected ErrEmptyWorkflow, got %v", err)
}
}
func TestParseWorkflow_Invalid(t *testing.T) {
_, err := ParseWorkflow("invalid: yaml: content: :")
if err == nil {
t.Error("Expected error for invalid YAML")
}
if !IsParseError(err) {
t.Errorf("Expected ParseError, got %T", err)
}
}
func TestValidateWorkflow_Valid(t *testing.T) {
yaml := `
name: test-workflow
kind: module
steps:
- name: echo
type: bash
command: echo hello
`
err := ValidateWorkflow(yaml)
if err != nil {
t.Fatalf("ValidateWorkflow failed: %v", err)
}
}
func TestValidateWorkflow_MissingName(t *testing.T) {
yaml := `
kind: module
steps:
- name: echo
type: bash
command: echo hello
`
err := ValidateWorkflow(yaml)
if err == nil {
t.Error("Expected error for missing name")
}
if !IsValidationError(err) {
t.Errorf("Expected ValidationError, got %T", err)
}
}
func TestValidateWorkflow_InvalidKind(t *testing.T) {
yaml := `
name: test
kind: invalid
steps:
- name: echo
type: bash
command: echo hello
`
err := ValidateWorkflow(yaml)
if err == nil {
t.Error("Expected error for invalid kind")
}
if !IsValidationError(err) {
t.Errorf("Expected ValidationError, got %T", err)
}
}
func TestRun_EmptyTarget(t *testing.T) {
_, err := Run("", "workflow: yaml", nil)
if err != ErrEmptyTarget {
t.Errorf("Expected ErrEmptyTarget, got %v", err)
}
}
func TestRun_EmptyWorkflow(t *testing.T) {
_, err := Run("target", "", nil)
if err != ErrEmptyWorkflow {
t.Errorf("Expected ErrEmptyWorkflow, got %v", err)
}
}
func TestRun_InvalidYAML(t *testing.T) {
_, err := Run("target", "invalid: yaml: :", nil)
if err == nil {
t.Error("Expected error for invalid YAML")
}
if !IsParseError(err) {
t.Errorf("Expected ParseError, got %T", err)
}
}
func TestRun_NotModule(t *testing.T) {
yaml := `
name: test-flow
kind: flow
modules:
- name: m1
path: ./modules/m1.yaml
`
_, err := Run("target", yaml, nil)
if err != ErrNotModule {
t.Errorf("Expected ErrNotModule, got %v", err)
}
}
func TestRun_SimpleWorkflow(t *testing.T) {
yaml := `
name: simple-echo
kind: module
steps:
- name: echo
type: bash
command: echo "hello world"
`
result, err := Run("test.com", yaml, &RunOptions{
Silent: true,
DisableWorkflowState: true,
DisableDatabase: true,
})
if err != nil {
t.Fatalf("Run failed: %v", err)
}
if result.Status != "completed" {
t.Errorf("Expected status 'completed', got %q", result.Status)
}
if result.WorkflowName != "simple-echo" {
t.Errorf("Expected workflow name 'simple-echo', got %q", result.WorkflowName)
}
if result.Target != "test.com" {
t.Errorf("Expected target 'test.com', got %q", result.Target)
}
if len(result.Steps) != 1 {
t.Errorf("Expected 1 step result, got %d", len(result.Steps))
}
}
func TestRunWithContext_Cancellation(t *testing.T) {
yaml := `
name: slow-workflow
kind: module
steps:
- name: slow
type: bash
command: sleep 10
`
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancel()
result, err := RunWithContext(ctx, "test.com", yaml, &RunOptions{
Silent: true,
DisableWorkflowState: true,
DisableDatabase: true,
})
// The workflow should be cancelled
if err == nil && result != nil && result.Status != "cancelled" {
t.Error("Expected cancellation or error")
}
}
func TestRunOptions_WithMethods(t *testing.T) {
opts := DefaultRunOptions()
// Test WithParams
params := map[string]string{"key": "value"}
newOpts := opts.WithParams(params)
if newOpts.Params["key"] != "value" {
t.Error("WithParams failed")
}
// Test WithTactic
newOpts = opts.WithTactic("aggressive")
if newOpts.Tactic != "aggressive" {
t.Error("WithTactic failed")
}
// Test WithDryRun
newOpts = opts.WithDryRun(true)
if !newOpts.DryRun {
t.Error("WithDryRun failed")
}
// Test WithVerbose
newOpts = opts.WithVerbose(true)
if !newOpts.Verbose {
t.Error("WithVerbose failed")
}
// Test WithSilent
newOpts = opts.WithSilent(false)
if newOpts.Silent {
t.Error("WithSilent failed")
}
}
func TestEvalOptions_WithMethods(t *testing.T) {
opts := DefaultEvalOptions()
// Test WithContext
ctx := map[string]interface{}{"key": "value"}
newOpts := opts.WithContext(ctx)
if newOpts.Context["key"] != "value" {
t.Error("WithContext failed")
}
// Test WithTarget
newOpts = opts.WithTarget("test.com")
if newOpts.Target != "test.com" {
t.Error("WithTarget failed")
}
}
func TestRunResult_Helpers(t *testing.T) {
result := &RunResult{
Status: "completed",
Exports: map[string]interface{}{
"stringVal": "hello",
"boolVal": true,
},
Steps: []*StepResult{
{Status: "success"},
{Status: "success"},
{Status: "skipped"},
{Status: "failed"},
},
}
if !result.IsSuccess() {
t.Error("Expected IsSuccess to be true")
}
if result.IsFailed() {
t.Error("Expected IsFailed to be false")
}
// Test GetExport
if v, ok := result.GetExport("stringVal"); !ok || v != "hello" {
t.Error("GetExport failed for stringVal")
}
// Test GetExportString
if v, ok := result.GetExportString("stringVal"); !ok || v != "hello" {
t.Error("GetExportString failed")
}
// Test GetExportBool
if v, ok := result.GetExportBool("boolVal"); !ok || !v {
t.Error("GetExportBool failed")
}
// Test step counts
if result.SuccessfulSteps() != 2 {
t.Errorf("Expected 2 successful steps, got %d", result.SuccessfulSteps())
}
if result.FailedSteps() != 1 {
t.Errorf("Expected 1 failed step, got %d", result.FailedSteps())
}
if result.SkippedSteps() != 1 {
t.Errorf("Expected 1 skipped step, got %d", result.SkippedSteps())
}
}
func TestStepResult_Helpers(t *testing.T) {
step := &StepResult{
Status: "success",
Exports: map[string]interface{}{
"output": "value",
},
}
if !step.IsSuccess() {
t.Error("Expected IsSuccess to be true")
}
if step.IsFailed() {
t.Error("Expected IsFailed to be false")
}
if step.IsSkipped() {
t.Error("Expected IsSkipped to be false")
}
if v, ok := step.GetExport("output"); !ok || v != "value" {
t.Error("GetExport failed")
}
}
func TestErrorTypes(t *testing.T) {
// Test ParseError
parseErr := NewParseError("test message", nil)
if parseErr.Error() != "parse error: test message" {
t.Errorf("Unexpected ParseError message: %s", parseErr.Error())
}
// Test ValidationError
validErr := NewValidationError("field", "message")
if validErr.Error() != "validation error in field 'field': message" {
t.Errorf("Unexpected ValidationError message: %s", validErr.Error())
}
// Test ExecutionError
execErr := NewExecutionError("step", "bash", "failed")
if execErr.Error() != "execution error at step 'step' (bash): failed" {
t.Errorf("Unexpected ExecutionError message: %s", execErr.Error())
}
// Test Is* functions
if !IsParseError(parseErr) {
t.Error("IsParseError should return true")
}
if !IsValidationError(validErr) {
t.Error("IsValidationError should return true")
}
if !IsExecutionError(execErr) {
t.Error("IsExecutionError should return true")
}
}