mirror of
https://github.com/j3ssie/osmedeus.git
synced 2026-09-03 07:38:46 +02:00
- Add AgentExecutor implementing LLM-based agentic loop with tool calling, max iterations, and stop conditions - Introduce agent preset tools (bash, file_exists, http_get, run_module, etc.) with extensible registry pattern - Add sub-agent spawning capability via spawn_agent tool call with recursive depth limits and validation - Implement ToolExecutor for custom tool execution with template rendering and error handling - Add agent session persistence and memory management with sliding window configuration - Create comprehensive E2E test suite covering 15+ agent workflow scenarios (minimal, custom tools, planning, multi-goal, structured output, tracing hooks, file tools, orchestration, Python tools, sub-agents, nested sub-agents, and validation) - Add agent-and-llm test data directory with 17 YAML workflow fixtures - Update integration tests to include agent workflow directories - Add AgentTool and AgentConfig types with validation for duplicate names and unknown presets - Implement LLM streaming test utilities - Update documentation (CLAUDE.md, HACKING.md, README.md) with agent features and CLI examples
323 lines
10 KiB
Go
323 lines
10 KiB
Go
package functions
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestRemoveBlankLines(t *testing.T) {
|
|
runtime := NewOttoRuntime()
|
|
|
|
t.Run("removes blank lines from file", func(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
testFile := filepath.Join(tmpDir, "test.txt")
|
|
content := "line1\n\nline2\n \nline3\n\n"
|
|
err := os.WriteFile(testFile, []byte(content), 0644)
|
|
require.NoError(t, err)
|
|
|
|
result, err := runtime.Execute(`remove_blank_lines("`+testFile+`")`, nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, true, result)
|
|
|
|
// Read back the file and verify
|
|
data, err := os.ReadFile(testFile)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "line1\nline2\nline3\n", string(data))
|
|
})
|
|
|
|
t.Run("handles file with only blank lines", func(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
testFile := filepath.Join(tmpDir, "blanks.txt")
|
|
content := "\n\n \n\t\n"
|
|
err := os.WriteFile(testFile, []byte(content), 0644)
|
|
require.NoError(t, err)
|
|
|
|
result, err := runtime.Execute(`remove_blank_lines("`+testFile+`")`, nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, true, result)
|
|
|
|
// File should be empty (no trailing newline since no lines)
|
|
data, err := os.ReadFile(testFile)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "", string(data))
|
|
})
|
|
|
|
t.Run("handles file with no blank lines", func(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
testFile := filepath.Join(tmpDir, "noblanks.txt")
|
|
content := "line1\nline2\nline3\n"
|
|
err := os.WriteFile(testFile, []byte(content), 0644)
|
|
require.NoError(t, err)
|
|
|
|
result, err := runtime.Execute(`remove_blank_lines("`+testFile+`")`, nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, true, result)
|
|
|
|
// File should be unchanged
|
|
data, err := os.ReadFile(testFile)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "line1\nline2\nline3\n", string(data))
|
|
})
|
|
|
|
t.Run("empty path returns false", func(t *testing.T) {
|
|
result, err := runtime.Execute(`remove_blank_lines("")`, nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, false, result)
|
|
})
|
|
|
|
t.Run("non-existent file returns false", func(t *testing.T) {
|
|
result, err := runtime.Execute(`remove_blank_lines("/nonexistent/file.txt")`, nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, false, result)
|
|
})
|
|
|
|
t.Run("directory path returns false", func(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
|
|
result, err := runtime.Execute(`remove_blank_lines("`+tmpDir+`")`, nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, false, result)
|
|
})
|
|
|
|
t.Run("preserves whitespace-only lines at start/middle", func(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
testFile := filepath.Join(tmpDir, "whitespace.txt")
|
|
// Lines with content should be preserved, blank/whitespace-only removed
|
|
content := " line with leading spaces\n\n\tmiddle with tab\n \nlast line\n"
|
|
err := os.WriteFile(testFile, []byte(content), 0644)
|
|
require.NoError(t, err)
|
|
|
|
result, err := runtime.Execute(`remove_blank_lines("`+testFile+`")`, nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, true, result)
|
|
|
|
data, err := os.ReadFile(testFile)
|
|
require.NoError(t, err)
|
|
// Lines with actual content (even with leading whitespace) should be kept
|
|
assert.Equal(t, " line with leading spaces\n\tmiddle with tab\nlast line\n", string(data))
|
|
})
|
|
}
|
|
|
|
func TestChunkFile(t *testing.T) {
|
|
runtime := NewOttoRuntime()
|
|
|
|
t.Run("splits file into equal chunks", func(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
input := filepath.Join(tmpDir, "urls.txt")
|
|
output := filepath.Join(tmpDir, "chunks.txt")
|
|
|
|
// Write 6 lines
|
|
content := "line1\nline2\nline3\nline4\nline5\nline6\n"
|
|
require.NoError(t, os.WriteFile(input, []byte(content), 0644))
|
|
|
|
result, err := runtime.Execute(`chunk_file("`+input+`", 2, "`+output+`")`, nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, true, result)
|
|
|
|
// Verify manifest
|
|
manifest, err := os.ReadFile(output)
|
|
require.NoError(t, err)
|
|
lines := strings.Split(strings.TrimSpace(string(manifest)), "\n")
|
|
assert.Len(t, lines, 3, "should have 3 chunks for 6 lines with chunk_size=2")
|
|
|
|
// Verify chunk 0
|
|
chunk0, err := os.ReadFile(lines[0])
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "line1\nline2\n", string(chunk0))
|
|
|
|
// Verify chunk 1
|
|
chunk1, err := os.ReadFile(lines[1])
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "line3\nline4\n", string(chunk1))
|
|
|
|
// Verify chunk 2
|
|
chunk2, err := os.ReadFile(lines[2])
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "line5\nline6\n", string(chunk2))
|
|
})
|
|
|
|
t.Run("handles uneven split (last chunk smaller)", func(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
input := filepath.Join(tmpDir, "data.txt")
|
|
output := filepath.Join(tmpDir, "chunks.txt")
|
|
|
|
// Write 5 lines
|
|
content := "a\nb\nc\nd\ne\n"
|
|
require.NoError(t, os.WriteFile(input, []byte(content), 0644))
|
|
|
|
result, err := runtime.Execute(`chunk_file("`+input+`", 3, "`+output+`")`, nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, true, result)
|
|
|
|
manifest, err := os.ReadFile(output)
|
|
require.NoError(t, err)
|
|
lines := strings.Split(strings.TrimSpace(string(manifest)), "\n")
|
|
assert.Len(t, lines, 2, "should have 2 chunks for 5 lines with chunk_size=3")
|
|
|
|
// First chunk has 3 lines
|
|
chunk0, err := os.ReadFile(lines[0])
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "a\nb\nc\n", string(chunk0))
|
|
|
|
// Second chunk has 2 lines
|
|
chunk1, err := os.ReadFile(lines[1])
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "d\ne\n", string(chunk1))
|
|
})
|
|
|
|
t.Run("skips blank lines", func(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
input := filepath.Join(tmpDir, "mixed.txt")
|
|
output := filepath.Join(tmpDir, "chunks.txt")
|
|
|
|
content := "line1\n\nline2\n \nline3\n\n"
|
|
require.NoError(t, os.WriteFile(input, []byte(content), 0644))
|
|
|
|
result, err := runtime.Execute(`chunk_file("`+input+`", 2, "`+output+`")`, nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, true, result)
|
|
|
|
manifest, err := os.ReadFile(output)
|
|
require.NoError(t, err)
|
|
lines := strings.Split(strings.TrimSpace(string(manifest)), "\n")
|
|
assert.Len(t, lines, 2, "should have 2 chunks for 3 non-blank lines with chunk_size=2")
|
|
|
|
chunk0, err := os.ReadFile(lines[0])
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "line1\nline2\n", string(chunk0))
|
|
|
|
chunk1, err := os.ReadFile(lines[1])
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "line3\n", string(chunk1))
|
|
})
|
|
|
|
t.Run("preserves extension in chunk names", func(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
input := filepath.Join(tmpDir, "urls.txt")
|
|
output := filepath.Join(tmpDir, "chunks.txt")
|
|
|
|
content := "url1\nurl2\nurl3\n"
|
|
require.NoError(t, os.WriteFile(input, []byte(content), 0644))
|
|
|
|
result, err := runtime.Execute(`chunk_file("`+input+`", 2, "`+output+`")`, nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, true, result)
|
|
|
|
manifest, err := os.ReadFile(output)
|
|
require.NoError(t, err)
|
|
lines := strings.Split(strings.TrimSpace(string(manifest)), "\n")
|
|
|
|
assert.Contains(t, lines[0], "urls_part_0.txt")
|
|
assert.Contains(t, lines[1], "urls_part_1.txt")
|
|
})
|
|
|
|
t.Run("empty file produces empty manifest", func(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
input := filepath.Join(tmpDir, "empty.txt")
|
|
output := filepath.Join(tmpDir, "chunks.txt")
|
|
|
|
require.NoError(t, os.WriteFile(input, []byte(""), 0644))
|
|
|
|
result, err := runtime.Execute(`chunk_file("`+input+`", 10, "`+output+`")`, nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, true, result)
|
|
|
|
manifest, err := os.ReadFile(output)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "", string(manifest))
|
|
})
|
|
|
|
t.Run("file with only blank lines produces empty manifest", func(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
input := filepath.Join(tmpDir, "blanks.txt")
|
|
output := filepath.Join(tmpDir, "chunks.txt")
|
|
|
|
require.NoError(t, os.WriteFile(input, []byte("\n\n \n\n"), 0644))
|
|
|
|
result, err := runtime.Execute(`chunk_file("`+input+`", 5, "`+output+`")`, nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, true, result)
|
|
|
|
manifest, err := os.ReadFile(output)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "", string(manifest))
|
|
})
|
|
|
|
t.Run("chunk size larger than file creates single chunk", func(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
input := filepath.Join(tmpDir, "small.txt")
|
|
output := filepath.Join(tmpDir, "chunks.txt")
|
|
|
|
content := "a\nb\nc\n"
|
|
require.NoError(t, os.WriteFile(input, []byte(content), 0644))
|
|
|
|
result, err := runtime.Execute(`chunk_file("`+input+`", 100, "`+output+`")`, nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, true, result)
|
|
|
|
manifest, err := os.ReadFile(output)
|
|
require.NoError(t, err)
|
|
lines := strings.Split(strings.TrimSpace(string(manifest)), "\n")
|
|
assert.Len(t, lines, 1, "should have 1 chunk when chunk_size > total lines")
|
|
|
|
chunk, err := os.ReadFile(lines[0])
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "a\nb\nc\n", string(chunk))
|
|
})
|
|
|
|
t.Run("empty input path returns false", func(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
output := filepath.Join(tmpDir, "chunks.txt")
|
|
|
|
result, err := runtime.Execute(`chunk_file("", 10, "`+output+`")`, nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, false, result)
|
|
})
|
|
|
|
t.Run("empty output path returns false", func(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
input := filepath.Join(tmpDir, "input.txt")
|
|
require.NoError(t, os.WriteFile(input, []byte("line\n"), 0644))
|
|
|
|
result, err := runtime.Execute(`chunk_file("`+input+`", 10, "")`, nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, false, result)
|
|
})
|
|
|
|
t.Run("chunk size zero returns false", func(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
input := filepath.Join(tmpDir, "input.txt")
|
|
output := filepath.Join(tmpDir, "chunks.txt")
|
|
require.NoError(t, os.WriteFile(input, []byte("line\n"), 0644))
|
|
|
|
result, err := runtime.Execute(`chunk_file("`+input+`", 0, "`+output+`")`, nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, false, result)
|
|
})
|
|
|
|
t.Run("negative chunk size returns false", func(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
input := filepath.Join(tmpDir, "input.txt")
|
|
output := filepath.Join(tmpDir, "chunks.txt")
|
|
require.NoError(t, os.WriteFile(input, []byte("line\n"), 0644))
|
|
|
|
result, err := runtime.Execute(`chunk_file("`+input+`", -5, "`+output+`")`, nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, false, result)
|
|
})
|
|
|
|
t.Run("non-existent input file returns false", func(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
output := filepath.Join(tmpDir, "chunks.txt")
|
|
|
|
result, err := runtime.Execute(`chunk_file("/nonexistent/input.txt", 10, "`+output+`")`, nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, false, result)
|
|
})
|
|
}
|