Files
osmedeus/internal/functions/util_functions_test.go

468 lines
13 KiB
Go

package functions
import (
"bytes"
"io"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestExecCmd(t *testing.T) {
runtime := NewOttoRuntime()
t.Run("simple echo command", func(t *testing.T) {
result, err := runtime.Execute(`exec_cmd("echo hello")`, nil)
require.NoError(t, err)
assert.Equal(t, "hello", result)
})
t.Run("command with pipe", func(t *testing.T) {
result, err := runtime.Execute(`exec_cmd("echo 'hello world' | cut -d' ' -f2")`, nil)
require.NoError(t, err)
assert.Equal(t, "world", result)
})
t.Run("empty command returns empty string", func(t *testing.T) {
result, err := runtime.Execute(`exec_cmd("")`, nil)
require.NoError(t, err)
assert.Equal(t, "", result)
})
t.Run("invalid command returns empty string", func(t *testing.T) {
result, err := runtime.Execute(`exec_cmd("nonexistentcmd12345")`, nil)
require.NoError(t, err)
assert.Equal(t, "", result)
})
t.Run("command with newlines trimmed", func(t *testing.T) {
result, err := runtime.Execute(`exec_cmd("printf 'hello\\n'")`, nil)
require.NoError(t, err)
assert.Equal(t, "hello", result)
})
}
func TestCutWithDelim(t *testing.T) {
runtime := NewOttoRuntime()
t.Run("extract first field", func(t *testing.T) {
result, err := runtime.Execute(`cut_with_delim("a,b,c", ",", 1)`, nil)
require.NoError(t, err)
assert.Equal(t, "a", result)
})
t.Run("extract middle field", func(t *testing.T) {
result, err := runtime.Execute(`cut_with_delim("a,b,c", ",", 2)`, nil)
require.NoError(t, err)
assert.Equal(t, "b", result)
})
t.Run("extract last field", func(t *testing.T) {
result, err := runtime.Execute(`cut_with_delim("a,b,c", ",", 3)`, nil)
require.NoError(t, err)
assert.Equal(t, "c", result)
})
t.Run("field out of range returns empty", func(t *testing.T) {
result, err := runtime.Execute(`cut_with_delim("a,b,c", ",", 5)`, nil)
require.NoError(t, err)
assert.Equal(t, "", result)
})
t.Run("field zero returns empty", func(t *testing.T) {
result, err := runtime.Execute(`cut_with_delim("a,b,c", ",", 0)`, nil)
require.NoError(t, err)
assert.Equal(t, "", result)
})
t.Run("negative field returns empty", func(t *testing.T) {
result, err := runtime.Execute(`cut_with_delim("a,b,c", ",", -1)`, nil)
require.NoError(t, err)
assert.Equal(t, "", result)
})
t.Run("different delimiter", func(t *testing.T) {
result, err := runtime.Execute(`cut_with_delim("a:b:c", ":", 2)`, nil)
require.NoError(t, err)
assert.Equal(t, "b", result)
})
t.Run("multi-char delimiter", func(t *testing.T) {
result, err := runtime.Execute(`cut_with_delim("a::b::c", "::", 2)`, nil)
require.NoError(t, err)
assert.Equal(t, "b", result)
})
t.Run("empty input returns empty", func(t *testing.T) {
result, err := runtime.Execute(`cut_with_delim("", ",", 1)`, nil)
require.NoError(t, err)
// Empty string split by delimiter gives [""], so field 1 is ""
assert.Equal(t, "", result)
})
t.Run("real-world URL parsing", func(t *testing.T) {
result, err := runtime.Execute(`cut_with_delim("https://example.com/path", "/", 3)`, nil)
require.NoError(t, err)
assert.Equal(t, "example.com", result)
})
}
func TestLogDebug(t *testing.T) {
runtime := NewOttoRuntime()
t.Run("logs debug message with prefix", func(t *testing.T) {
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
_, err := runtime.Execute(`log_debug("test debug message")`, nil)
require.NoError(t, err)
_ = w.Close()
var buf bytes.Buffer
_, _ = io.Copy(&buf, r)
os.Stdout = oldStdout
output := buf.String()
assert.Contains(t, output, "[DEBUG]")
assert.Contains(t, output, "test debug message")
})
t.Run("empty message still logs prefix", func(t *testing.T) {
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
_, err := runtime.Execute(`log_debug("")`, nil)
require.NoError(t, err)
_ = w.Close()
var buf bytes.Buffer
_, _ = io.Copy(&buf, r)
os.Stdout = oldStdout
output := buf.String()
assert.Contains(t, output, "[DEBUG]")
})
}
func TestLogInfo(t *testing.T) {
runtime := NewOttoRuntime()
t.Run("logs info message with prefix", func(t *testing.T) {
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
_, err := runtime.Execute(`log_info("test info message")`, nil)
require.NoError(t, err)
_ = w.Close()
var buf bytes.Buffer
_, _ = io.Copy(&buf, r)
os.Stdout = oldStdout
output := buf.String()
assert.Contains(t, output, "[INFO]")
assert.Contains(t, output, "test info message")
})
t.Run("empty message still logs prefix", func(t *testing.T) {
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
_, err := runtime.Execute(`log_info("")`, nil)
require.NoError(t, err)
_ = w.Close()
var buf bytes.Buffer
_, _ = io.Copy(&buf, r)
os.Stdout = oldStdout
output := buf.String()
assert.Contains(t, output, "[INFO]")
})
}
func TestLogWarn(t *testing.T) {
runtime := NewOttoRuntime()
t.Run("logs warn message with prefix", func(t *testing.T) {
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
_, err := runtime.Execute(`log_warn("test warn message")`, nil)
require.NoError(t, err)
_ = w.Close()
var buf bytes.Buffer
_, _ = io.Copy(&buf, r)
os.Stdout = oldStdout
output := buf.String()
assert.Contains(t, output, "[WARN]")
assert.Contains(t, output, "test warn message")
})
t.Run("empty message still logs prefix", func(t *testing.T) {
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
_, err := runtime.Execute(`log_warn("")`, nil)
require.NoError(t, err)
_ = w.Close()
var buf bytes.Buffer
_, _ = io.Copy(&buf, r)
os.Stdout = oldStdout
output := buf.String()
assert.Contains(t, output, "[WARN]")
})
}
func TestRmRF(t *testing.T) {
runtime := NewOttoRuntime()
t.Run("remove file", func(t *testing.T) {
tmpDir := t.TempDir()
p := filepath.Join(tmpDir, "a.txt")
err := os.WriteFile(p, []byte("x"), 0644)
require.NoError(t, err)
result, err := runtime.Execute(`rm_rf("`+p+`")`, nil)
require.NoError(t, err)
assert.Equal(t, true, result)
_, statErr := os.Stat(p)
assert.Error(t, statErr)
})
t.Run("remove folder", func(t *testing.T) {
tmpDir := t.TempDir()
folder := filepath.Join(tmpDir, "dir")
err := os.MkdirAll(folder, 0755)
require.NoError(t, err)
err = os.WriteFile(filepath.Join(folder, "a.txt"), []byte("x"), 0644)
require.NoError(t, err)
result, err := runtime.Execute(`rm_rf("`+folder+`")`, nil)
require.NoError(t, err)
assert.Equal(t, true, result)
_, statErr := os.Stat(folder)
assert.Error(t, statErr)
})
}
func TestRemoveAllExcept(t *testing.T) {
runtime := NewOttoRuntime()
t.Run("keep root file", func(t *testing.T) {
root := t.TempDir()
keep := filepath.Join(root, "keep.txt")
require.NoError(t, os.WriteFile(keep, []byte("keep"), 0644))
require.NoError(t, os.WriteFile(filepath.Join(root, "a.txt"), []byte("a"), 0644))
require.NoError(t, os.MkdirAll(filepath.Join(root, "sub"), 0755))
require.NoError(t, os.WriteFile(filepath.Join(root, "sub", "b.txt"), []byte("b"), 0644))
result, err := runtime.Execute(`remove_all_except("`+root+`", "keep.txt")`, nil)
require.NoError(t, err)
assert.Equal(t, true, result)
_, err = os.Stat(keep)
assert.NoError(t, err)
_, err = os.Stat(filepath.Join(root, "a.txt"))
assert.Error(t, err)
_, err = os.Stat(filepath.Join(root, "sub"))
assert.Error(t, err)
})
t.Run("keep nested file", func(t *testing.T) {
root := t.TempDir()
require.NoError(t, os.MkdirAll(filepath.Join(root, "sub"), 0755))
keep := filepath.Join(root, "sub", "keep.txt")
require.NoError(t, os.WriteFile(keep, []byte("keep"), 0644))
require.NoError(t, os.WriteFile(filepath.Join(root, "sub", "remove.txt"), []byte("x"), 0644))
require.NoError(t, os.MkdirAll(filepath.Join(root, "other"), 0755))
require.NoError(t, os.WriteFile(filepath.Join(root, "other", "x.txt"), []byte("x"), 0644))
result, err := runtime.Execute(`remove_all_except("`+root+`", "sub/keep.txt")`, nil)
require.NoError(t, err)
assert.Equal(t, true, result)
_, err = os.Stat(keep)
assert.NoError(t, err)
_, err = os.Stat(filepath.Join(root, "sub", "remove.txt"))
assert.Error(t, err)
_, err = os.Stat(filepath.Join(root, "other"))
assert.Error(t, err)
})
}
func TestLogError(t *testing.T) {
runtime := NewOttoRuntime()
t.Run("logs error message with prefix", func(t *testing.T) {
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
_, err := runtime.Execute(`log_error("test error message")`, nil)
require.NoError(t, err)
_ = w.Close()
var buf bytes.Buffer
_, _ = io.Copy(&buf, r)
os.Stdout = oldStdout
output := buf.String()
assert.Contains(t, output, "[ERROR]")
assert.Contains(t, output, "test error message")
})
t.Run("empty message still logs prefix", func(t *testing.T) {
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
_, err := runtime.Execute(`log_error("")`, nil)
require.NoError(t, err)
_ = w.Close()
var buf bytes.Buffer
_, _ = io.Copy(&buf, r)
os.Stdout = oldStdout
output := buf.String()
assert.Contains(t, output, "[ERROR]")
})
}
func TestMoveFile(t *testing.T) {
runtime := NewOttoRuntime()
t.Run("move file within same directory", func(t *testing.T) {
tmpDir := t.TempDir()
source := filepath.Join(tmpDir, "source.txt")
dest := filepath.Join(tmpDir, "dest.txt")
content := "test content for move"
err := os.WriteFile(source, []byte(content), 0644)
require.NoError(t, err)
result, err := runtime.Execute(`moveFile("`+source+`", "`+dest+`")`, nil)
require.NoError(t, err)
assert.Equal(t, true, result)
// Verify source no longer exists
_, statErr := os.Stat(source)
assert.True(t, os.IsNotExist(statErr))
// Verify dest exists with correct content
destContent, err := os.ReadFile(dest)
require.NoError(t, err)
assert.Equal(t, content, string(destContent))
})
t.Run("move file to new directory (creates directory)", func(t *testing.T) {
tmpDir := t.TempDir()
source := filepath.Join(tmpDir, "source.txt")
dest := filepath.Join(tmpDir, "subdir", "nested", "dest.txt")
content := "test content for nested move"
err := os.WriteFile(source, []byte(content), 0644)
require.NoError(t, err)
result, err := runtime.Execute(`moveFile("`+source+`", "`+dest+`")`, nil)
require.NoError(t, err)
assert.Equal(t, true, result)
// Verify source no longer exists
_, statErr := os.Stat(source)
assert.True(t, os.IsNotExist(statErr))
// Verify dest exists with correct content
destContent, err := os.ReadFile(dest)
require.NoError(t, err)
assert.Equal(t, content, string(destContent))
})
t.Run("move non-existent source fails", func(t *testing.T) {
tmpDir := t.TempDir()
source := filepath.Join(tmpDir, "nonexistent.txt")
dest := filepath.Join(tmpDir, "dest.txt")
result, err := runtime.Execute(`moveFile("`+source+`", "`+dest+`")`, nil)
require.NoError(t, err)
assert.Equal(t, false, result)
})
t.Run("move directory fails (file only)", func(t *testing.T) {
tmpDir := t.TempDir()
sourceDir := filepath.Join(tmpDir, "sourcedir")
dest := filepath.Join(tmpDir, "dest")
err := os.MkdirAll(sourceDir, 0755)
require.NoError(t, err)
result, err := runtime.Execute(`moveFile("`+sourceDir+`", "`+dest+`")`, nil)
require.NoError(t, err)
assert.Equal(t, false, result)
// Verify source directory still exists
info, statErr := os.Stat(sourceDir)
require.NoError(t, statErr)
assert.True(t, info.IsDir())
})
t.Run("empty source argument fails", func(t *testing.T) {
tmpDir := t.TempDir()
dest := filepath.Join(tmpDir, "dest.txt")
result, err := runtime.Execute(`moveFile("", "`+dest+`")`, nil)
require.NoError(t, err)
assert.Equal(t, false, result)
})
t.Run("empty dest argument fails", func(t *testing.T) {
tmpDir := t.TempDir()
source := filepath.Join(tmpDir, "source.txt")
err := os.WriteFile(source, []byte("content"), 0644)
require.NoError(t, err)
result, err := runtime.Execute(`moveFile("`+source+`", "")`, nil)
require.NoError(t, err)
assert.Equal(t, false, result)
// Verify source still exists
_, statErr := os.Stat(source)
require.NoError(t, statErr)
})
t.Run("preserves file permissions", func(t *testing.T) {
tmpDir := t.TempDir()
source := filepath.Join(tmpDir, "source.txt")
dest := filepath.Join(tmpDir, "dest.txt")
content := "test content"
err := os.WriteFile(source, []byte(content), 0755)
require.NoError(t, err)
result, err := runtime.Execute(`moveFile("`+source+`", "`+dest+`")`, nil)
require.NoError(t, err)
assert.Equal(t, true, result)
// Verify dest has executable permission
info, statErr := os.Stat(dest)
require.NoError(t, statErr)
// Check that execute bit is set (at least for owner)
assert.True(t, info.Mode()&0100 != 0, "expected executable permission to be preserved")
})
}