mirror of
https://github.com/j3ssie/osmedeus.git
synced 2026-08-17 21:25:49 +02:00
481 lines
10 KiB
Go
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")
|
|
}
|
|
}
|