mirror of
https://github.com/j3ssie/osmedeus.git
synced 2026-08-21 07:02:27 +02:00
- Add RunClient for submitting runs to server with priority support (low, normal, high, critical) - Implement --run-priority and --server-url CLI flags for server submission mode - Add RunPriority and RunMode fields to database Run model for persistence - Update CreateRunRequest with priority and run_mode validation - Implement runServerSubmission() to submit workflows with priority to server API - Comprehensive E2E tests for run endpoints with priority validation and multiple targets - Add foreach-preprocess test workflow with variable_pre_process support
345 lines
9.5 KiB
Go
345 lines
9.5 KiB
Go
package executor
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/j3ssie/osmedeus/v5/internal/core"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestForeachExecutor_VariablePreProcess(t *testing.T) {
|
|
// Create temp directory for test files
|
|
tmpDir := t.TempDir()
|
|
|
|
// Create test input file with URLs
|
|
inputFile := filepath.Join(tmpDir, "urls.txt")
|
|
err := os.WriteFile(inputFile, []byte(`https://example.com/path/file.php?id=1
|
|
https://example.com/api/v1/users
|
|
https://example.com/deep/nested/path/resource
|
|
`), 0644)
|
|
require.NoError(t, err)
|
|
|
|
// Create output file path
|
|
outputFile := filepath.Join(tmpDir, "output.txt")
|
|
|
|
// Create dispatcher
|
|
dispatcher := NewStepDispatcher()
|
|
|
|
// Create execution context
|
|
execCtx := core.NewExecutionContext("test-workflow", core.KindModule, "test-run-uuid", "test-target")
|
|
execCtx.SetVariable("Output", tmpDir)
|
|
|
|
// Test case 1: Basic pre-processing with get_parent_url
|
|
t.Run("get_parent_url pre-processing", func(t *testing.T) {
|
|
step := &core.Step{
|
|
Name: "test-preprocess",
|
|
Type: core.StepTypeForeach,
|
|
Input: inputFile,
|
|
Variable: "url",
|
|
VariablePreProcess: "get_parent_url([[url]])",
|
|
Threads: "1",
|
|
Step: &core.Step{
|
|
Type: core.StepTypeBash,
|
|
Command: "echo '[[url]]' >> " + outputFile,
|
|
},
|
|
}
|
|
|
|
// Clean output file
|
|
_ = os.Remove(outputFile)
|
|
|
|
ctx := context.Background()
|
|
result, err := dispatcher.Dispatch(ctx, step, execCtx)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, core.StepStatusSuccess, result.Status)
|
|
|
|
// Verify output contains parent URLs
|
|
output, err := os.ReadFile(outputFile)
|
|
require.NoError(t, err)
|
|
outputStr := string(output)
|
|
|
|
assert.Contains(t, outputStr, "https://example.com/path/")
|
|
assert.Contains(t, outputStr, "https://example.com/api/v1/")
|
|
assert.Contains(t, outputStr, "https://example.com/deep/nested/path/")
|
|
})
|
|
|
|
// Test case 2: No pre-processing (existing behavior unchanged)
|
|
t.Run("no pre-processing", func(t *testing.T) {
|
|
step := &core.Step{
|
|
Name: "test-no-preprocess",
|
|
Type: core.StepTypeForeach,
|
|
Input: inputFile,
|
|
Variable: "url",
|
|
Threads: "1",
|
|
Step: &core.Step{
|
|
Type: core.StepTypeBash,
|
|
Command: "echo '[[url]]' >> " + outputFile,
|
|
},
|
|
}
|
|
|
|
// Clean output file
|
|
_ = os.Remove(outputFile)
|
|
|
|
ctx := context.Background()
|
|
result, err := dispatcher.Dispatch(ctx, step, execCtx)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, core.StepStatusSuccess, result.Status)
|
|
|
|
// Verify output contains original URLs
|
|
output, err := os.ReadFile(outputFile)
|
|
require.NoError(t, err)
|
|
outputStr := string(output)
|
|
|
|
assert.Contains(t, outputStr, "https://example.com/path/file.php?id=1")
|
|
assert.Contains(t, outputStr, "https://example.com/api/v1/users")
|
|
})
|
|
|
|
// Test case 3: Pre-processing with trim
|
|
t.Run("trim pre-processing", func(t *testing.T) {
|
|
// Create input with whitespace
|
|
trimInputFile := filepath.Join(tmpDir, "trim-input.txt")
|
|
err := os.WriteFile(trimInputFile, []byte(` hello
|
|
world
|
|
`), 0644)
|
|
require.NoError(t, err)
|
|
|
|
step := &core.Step{
|
|
Name: "test-trim-preprocess",
|
|
Type: core.StepTypeForeach,
|
|
Input: trimInputFile,
|
|
Variable: "line",
|
|
VariablePreProcess: "trim([[line]])",
|
|
Threads: "1",
|
|
Step: &core.Step{
|
|
Type: core.StepTypeBash,
|
|
Command: "echo '[[line]]' >> " + outputFile,
|
|
},
|
|
}
|
|
|
|
// Clean output file
|
|
_ = os.Remove(outputFile)
|
|
|
|
ctx := context.Background()
|
|
result, err := dispatcher.Dispatch(ctx, step, execCtx)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, core.StepStatusSuccess, result.Status)
|
|
|
|
// Verify output contains trimmed values
|
|
output, err := os.ReadFile(outputFile)
|
|
require.NoError(t, err)
|
|
outputStr := string(output)
|
|
|
|
// The file iterator already trims lines, but the pre-process should work anyway
|
|
assert.Contains(t, outputStr, "hello")
|
|
assert.Contains(t, outputStr, "world")
|
|
})
|
|
|
|
// Test case 4: Pre-processing with parse_url
|
|
t.Run("parse_url pre-processing", func(t *testing.T) {
|
|
step := &core.Step{
|
|
Name: "test-parse-url-preprocess",
|
|
Type: core.StepTypeForeach,
|
|
Input: inputFile,
|
|
Variable: "url",
|
|
VariablePreProcess: "parse_url([[url]], '%d')", // Extract domain only
|
|
Threads: "1",
|
|
Step: &core.Step{
|
|
Type: core.StepTypeBash,
|
|
Command: "echo '[[url]]' >> " + outputFile,
|
|
},
|
|
}
|
|
|
|
// Clean output file
|
|
_ = os.Remove(outputFile)
|
|
|
|
ctx := context.Background()
|
|
result, err := dispatcher.Dispatch(ctx, step, execCtx)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, core.StepStatusSuccess, result.Status)
|
|
|
|
// Verify output contains just domains
|
|
output, err := os.ReadFile(outputFile)
|
|
require.NoError(t, err)
|
|
outputStr := string(output)
|
|
|
|
assert.Contains(t, outputStr, "example.com")
|
|
})
|
|
|
|
// Test case 5: Pre-processing with chained functions
|
|
t.Run("chained functions pre-processing", func(t *testing.T) {
|
|
step := &core.Step{
|
|
Name: "test-chained-preprocess",
|
|
Type: core.StepTypeForeach,
|
|
Input: inputFile,
|
|
Variable: "url",
|
|
VariablePreProcess: "to_lower_case(parse_url([[url]], '%d'))",
|
|
Threads: "1",
|
|
Step: &core.Step{
|
|
Type: core.StepTypeBash,
|
|
Command: "echo '[[url]]' >> " + outputFile,
|
|
},
|
|
}
|
|
|
|
// Clean output file
|
|
_ = os.Remove(outputFile)
|
|
|
|
ctx := context.Background()
|
|
result, err := dispatcher.Dispatch(ctx, step, execCtx)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, core.StepStatusSuccess, result.Status)
|
|
|
|
// Verify output
|
|
output, err := os.ReadFile(outputFile)
|
|
require.NoError(t, err)
|
|
outputStr := string(output)
|
|
|
|
assert.Contains(t, outputStr, "example.com")
|
|
})
|
|
}
|
|
|
|
func TestForeachExecutor_VariablePreProcess_ErrorHandling(t *testing.T) {
|
|
// Create temp directory for test files
|
|
tmpDir := t.TempDir()
|
|
|
|
// Create test input file
|
|
inputFile := filepath.Join(tmpDir, "input.txt")
|
|
err := os.WriteFile(inputFile, []byte(`line1
|
|
line2
|
|
`), 0644)
|
|
require.NoError(t, err)
|
|
|
|
outputFile := filepath.Join(tmpDir, "output.txt")
|
|
|
|
// Create dispatcher
|
|
dispatcher := NewStepDispatcher()
|
|
|
|
// Create execution context
|
|
execCtx := core.NewExecutionContext("test-workflow", core.KindModule, "test-run-uuid", "test-target")
|
|
|
|
// Test case: Invalid function should fall back to original value
|
|
t.Run("invalid function fallback", func(t *testing.T) {
|
|
step := &core.Step{
|
|
Name: "test-invalid-func",
|
|
Type: core.StepTypeForeach,
|
|
Input: inputFile,
|
|
Variable: "line",
|
|
VariablePreProcess: "invalid_nonexistent_function([[line]])",
|
|
Threads: "1",
|
|
Step: &core.Step{
|
|
Type: core.StepTypeBash,
|
|
Command: "echo '[[line]]' >> " + outputFile,
|
|
},
|
|
}
|
|
|
|
// Clean output file
|
|
_ = os.Remove(outputFile)
|
|
|
|
ctx := context.Background()
|
|
result, err := dispatcher.Dispatch(ctx, step, execCtx)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, core.StepStatusSuccess, result.Status)
|
|
|
|
// Should still produce output with original values since invalid function falls back
|
|
output, err := os.ReadFile(outputFile)
|
|
require.NoError(t, err)
|
|
outputStr := string(output)
|
|
|
|
assert.Contains(t, outputStr, "line1")
|
|
assert.Contains(t, outputStr, "line2")
|
|
})
|
|
}
|
|
|
|
func TestForeachExecutor_VariablePreProcess_SpecialChars(t *testing.T) {
|
|
// Create temp directory for test files
|
|
tmpDir := t.TempDir()
|
|
|
|
// Create test input file with URLs containing special characters
|
|
inputFile := filepath.Join(tmpDir, "special-urls.txt")
|
|
err := os.WriteFile(inputFile, []byte(`https://example.com/path?foo='bar'&baz=1
|
|
https://example.com/it's/a/test
|
|
`), 0644)
|
|
require.NoError(t, err)
|
|
|
|
outputFile := filepath.Join(tmpDir, "output.txt")
|
|
|
|
// Create dispatcher
|
|
dispatcher := NewStepDispatcher()
|
|
|
|
// Create execution context
|
|
execCtx := core.NewExecutionContext("test-workflow", core.KindModule, "test-run-uuid", "test-target")
|
|
|
|
t.Run("special characters in URL", func(t *testing.T) {
|
|
step := &core.Step{
|
|
Name: "test-special-chars",
|
|
Type: core.StepTypeForeach,
|
|
Input: inputFile,
|
|
Variable: "url",
|
|
VariablePreProcess: "get_parent_url([[url]])",
|
|
Threads: "1",
|
|
Step: &core.Step{
|
|
Type: core.StepTypeBash,
|
|
Command: "echo '[[url]]' >> " + outputFile,
|
|
},
|
|
}
|
|
|
|
// Clean output file
|
|
_ = os.Remove(outputFile)
|
|
|
|
ctx := context.Background()
|
|
result, err := dispatcher.Dispatch(ctx, step, execCtx)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, core.StepStatusSuccess, result.Status)
|
|
|
|
// Verify output contains parent URLs
|
|
output, err := os.ReadFile(outputFile)
|
|
require.NoError(t, err)
|
|
outputStr := string(output)
|
|
|
|
assert.Contains(t, outputStr, "https://example.com/")
|
|
})
|
|
}
|
|
|
|
func TestAutoQuoteForJS(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
input string
|
|
expected string
|
|
}{
|
|
{
|
|
name: "simple string",
|
|
input: "hello",
|
|
expected: "'hello'",
|
|
},
|
|
{
|
|
name: "string with single quote",
|
|
input: "it's a test",
|
|
expected: "'it\\'s a test'",
|
|
},
|
|
{
|
|
name: "URL",
|
|
input: "https://example.com/path?foo=bar",
|
|
expected: "'https://example.com/path?foo=bar'",
|
|
},
|
|
{
|
|
name: "URL with quotes",
|
|
input: "https://example.com/path?foo='bar'",
|
|
expected: "'https://example.com/path?foo=\\'bar\\''",
|
|
},
|
|
{
|
|
name: "empty string",
|
|
input: "",
|
|
expected: "''",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := autoQuoteForJS(tt.input)
|
|
assert.Equal(t, tt.expected, result)
|
|
})
|
|
}
|
|
}
|