4 Commits

Author SHA1 Message Date
346afdd143 Fix some shit 2025-10-23 00:19:07 +02:00
48729cdfa4 Add isolate tests 2025-10-23 00:16:35 +02:00
b9574f0106 "Fix" isolate commands fucking each other (hallucinate) 2025-10-23 00:13:14 +02:00
635ca463c0 Hallucinate fixes to some tests 2025-10-21 10:38:39 +02:00
4 changed files with 387 additions and 49 deletions

335
isolate_test.go Normal file
View File

@@ -0,0 +1,335 @@
package main
import (
"os"
"path/filepath"
"testing"
"cook/utils"
logger "git.site.quack-lab.dev/dave/cylogger"
"github.com/stretchr/testify/assert"
)
func TestIsolateCommandsSequentialExecution(t *testing.T) {
// Create a temporary directory for testing
tmpDir, err := os.MkdirTemp("", "isolate-sequential-test")
if err != nil {
t.Fatalf("Failed to create temp dir: %v", err)
}
defer os.RemoveAll(tmpDir)
// Create test file content
testContent := `BEGIN
block1 content with value 42
END
Some other content
BEGIN
block2 content with value 100
END
More content
BEGIN
block3 content with value 200
END`
testFile := filepath.Join(tmpDir, "test.txt")
err = os.WriteFile(testFile, []byte(testContent), 0644)
if err != nil {
t.Fatalf("Failed to write test file: %v", err)
}
// Change to temp directory
origDir, _ := os.Getwd()
defer os.Chdir(origDir)
os.Chdir(tmpDir)
// Create isolate commands that work sequentially on the same block
// First command: 42 -> 84
// Second command: 84 -> 168 (works on result of first command)
// Third command: 168 -> 336 (works on result of second command)
commands := []utils.ModifyCommand{
{
Name: "MultiplyFirst",
Regex: `BEGIN\n(?P<block>.*?value 42.*?)\nEND`,
Lua: `replacement = "BEGIN\n" .. string.gsub(block, "42", "84") .. "\nEND"; return true`,
Files: []string{"test.txt"},
Isolate: true,
},
{
Name: "MultiplySecond",
Regex: `BEGIN\nblock1 content with value (?P<value>!num)\nEND`,
Lua: `value = "168"; return true`,
Files: []string{"test.txt"},
Isolate: true,
},
{
Name: "MultiplyThird",
Regex: `BEGIN\nblock1 content with value (?P<value>!num)\nEND`,
Lua: `value = "336"; return true`,
Files: []string{"test.txt"},
Isolate: true,
},
}
// Associate files with commands
files := []string{"test.txt"}
associations, err := utils.AssociateFilesWithCommands(files, commands)
if err != nil {
t.Fatalf("Failed to associate files with commands: %v", err)
}
// Verify that all three isolate commands are associated
association := associations["test.txt"]
assert.Len(t, association.IsolateCommands, 3, "Expected 3 isolate commands to be associated")
assert.Len(t, association.Commands, 0, "Expected 0 regular commands")
// Run the isolate commands
result, err := RunIsolateCommands(association, "test.txt", testContent)
if err != nil && err != NothingToDo {
t.Fatalf("Failed to run isolate commands: %v", err)
}
// Verify that all isolate commands were applied sequentially
// First command: 42 -> 84
// Second command: 84 -> 168 (works on result of first)
// Third command: 168 -> 336 (works on result of second)
assert.Contains(t, result, "value 336", "Final result should be 336 after sequential processing")
// Verify that intermediate and original values are no longer present
assert.NotContains(t, result, "value 42", "Original value 42 should be replaced")
assert.NotContains(t, result, "value 84", "Intermediate value 84 should be replaced")
assert.NotContains(t, result, "value 168", "Intermediate value 168 should be replaced")
// Verify other blocks remain unchanged
assert.Contains(t, result, "value 100", "Second block should remain unchanged")
assert.Contains(t, result, "value 200", "Third block should remain unchanged")
t.Logf("Original content:\n%s\n", testContent)
t.Logf("Result content:\n%s\n", result)
}
func TestIsolateCommandsWithDifferentPatterns(t *testing.T) {
// Create a temporary directory for testing
tmpDir, err := os.MkdirTemp("", "isolate-different-patterns-test")
if err != nil {
t.Fatalf("Failed to create temp dir: %v", err)
}
defer os.RemoveAll(tmpDir)
// Create test file content with distinct patterns
testContent := `SECTION1
value = 10
END_SECTION1
SECTION2
value = 20
END_SECTION2`
testFile := filepath.Join(tmpDir, "test.txt")
err = os.WriteFile(testFile, []byte(testContent), 0644)
if err != nil {
t.Fatalf("Failed to write test file: %v", err)
}
// Change to temp directory
origDir, _ := os.Getwd()
defer os.Chdir(origDir)
os.Chdir(tmpDir)
// Create isolate commands with different patterns on the same content
commands := []utils.ModifyCommand{
{
Name: "UpdateSection1",
Regex: `SECTION1.*?value = (?P<value>!num).*?END_SECTION1`,
Lua: `value = "100"; return true`,
Files: []string{"test.txt"},
Isolate: true,
},
{
Name: "UpdateSection2",
Regex: `SECTION2.*?value = (?P<value>!num).*?END_SECTION2`,
Lua: `value = "200"; return true`,
Files: []string{"test.txt"},
Isolate: true,
},
}
// Associate files with commands
files := []string{"test.txt"}
associations, err := utils.AssociateFilesWithCommands(files, commands)
if err != nil {
t.Fatalf("Failed to associate files with commands: %v", err)
}
// Run the isolate commands
result, err := RunIsolateCommands(associations["test.txt"], "test.txt", testContent)
if err != nil && err != NothingToDo {
t.Fatalf("Failed to run isolate commands: %v", err)
}
// Verify that both isolate commands were applied
assert.Contains(t, result, "value = 100", "Section1 should be updated")
assert.Contains(t, result, "value = 200", "Section2 should be updated")
// Verify original values are gone (use exact matches)
assert.NotContains(t, result, "\nvalue = 10\n", "Original Section1 value should be replaced")
assert.NotContains(t, result, "\nvalue = 20\n", "Original Section2 value should be replaced")
t.Logf("Original content:\n%s\n", testContent)
t.Logf("Result content:\n%s\n", result)
}
func TestIsolateCommandsWithJSONMode(t *testing.T) {
// Create a temporary directory for testing
tmpDir, err := os.MkdirTemp("", "isolate-json-test")
if err != nil {
t.Fatalf("Failed to create temp dir: %v", err)
}
defer os.RemoveAll(tmpDir)
// Create test JSON content
testContent := `{
"section1": {
"value": 42
},
"section2": {
"value": 100
}
}`
testFile := filepath.Join(tmpDir, "test.json")
err = os.WriteFile(testFile, []byte(testContent), 0644)
if err != nil {
t.Fatalf("Failed to write test file: %v", err)
}
// Change to temp directory
origDir, _ := os.Getwd()
defer os.Chdir(origDir)
os.Chdir(tmpDir)
// Create isolate commands with JSON mode
commands := []utils.ModifyCommand{
{
Name: "UpdateJSONFirst",
JSON: true,
Lua: `data.section1.value = data.section1.value * 2; return true`,
Files: []string{"test.json"},
Isolate: true,
},
{
Name: "UpdateJSONSecond",
JSON: true,
Lua: `data.section2.value = data.section2.value * 3; return true`,
Files: []string{"test.json"},
Isolate: true,
},
}
// Associate files with commands
files := []string{"test.json"}
associations, err := utils.AssociateFilesWithCommands(files, commands)
if err != nil {
t.Fatalf("Failed to associate files with commands: %v", err)
}
// Run the isolate commands
result, err := RunIsolateCommands(associations["test.json"], "test.json", testContent)
if err != nil && err != NothingToDo {
t.Fatalf("Failed to run isolate commands: %v", err)
}
// Verify that both JSON isolate commands were applied
assert.Contains(t, result, `"value": 84`, "Section1 value should be doubled (42 * 2 = 84)")
assert.Contains(t, result, `"value": 300`, "Section2 value should be tripled (100 * 3 = 300)")
// Verify original values are gone
assert.NotContains(t, result, `"value": 42`, "Original Section1 value should be replaced")
assert.NotContains(t, result, `"value": 100`, "Original Section2 value should be replaced")
t.Logf("Original content:\n%s\n", testContent)
t.Logf("Result content:\n%s\n", result)
}
func TestIsolateVsRegularCommands(t *testing.T) {
// Create a temporary directory for testing
tmpDir, err := os.MkdirTemp("", "isolate-regular-test")
if err != nil {
t.Fatalf("Failed to create temp dir: %v", err)
}
defer os.RemoveAll(tmpDir)
// Create test file with distinct sections
testContent := `ISOLATE_SECTION
value = 5
END_ISOLATE
REGULAR_SECTION
value = 10
END_REGULAR`
testFile := filepath.Join(tmpDir, "test.txt")
err = os.WriteFile(testFile, []byte(testContent), 0644)
if err != nil {
t.Fatalf("Failed to write test file: %v", err)
}
// Change to temp directory
origDir, _ := os.Getwd()
defer os.Chdir(origDir)
os.Chdir(tmpDir)
// Create both isolate and regular commands
commands := []utils.ModifyCommand{
{
Name: "IsolateMultiply",
Regex: `ISOLATE_SECTION.*?value = (?P<value>!num).*?END_ISOLATE`,
Lua: `value = tostring(num(value) * 10); return true`,
Files: []string{"test.txt"},
Isolate: true,
},
{
Name: "RegularMultiply",
Regex: `value = (?P<value>!num)`,
Lua: `value = tostring(num(value) + 100); return true`,
Files: []string{"test.txt"},
},
}
// Associate files with commands
files := []string{"test.txt"}
associations, err := utils.AssociateFilesWithCommands(files, commands)
if err != nil {
t.Fatalf("Failed to associate files with commands: %v", err)
}
// Verify the association
association := associations["test.txt"]
assert.Len(t, association.IsolateCommands, 1, "Expected 1 isolate command")
assert.Len(t, association.Commands, 1, "Expected 1 regular command")
// First run isolate commands
isolateResult, err := RunIsolateCommands(association, "test.txt", testContent)
if err != nil && err != NothingToDo {
t.Fatalf("Failed to run isolate commands: %v", err)
}
// Verify isolate command result
assert.Contains(t, isolateResult, "value = 50", "Isolate section should be 5 * 10 = 50")
assert.Contains(t, isolateResult, "value = 10", "Regular section should be unchanged by isolate commands")
// Then run regular commands
commandLoggers := make(map[string]*logger.Logger)
finalResult, err := RunOtherCommands("test.txt", isolateResult, association, commandLoggers)
if err != nil && err != NothingToDo {
t.Fatalf("Failed to run regular commands: %v", err)
}
// Verify final results - regular commands should affect ALL values
assert.Contains(t, finalResult, "value = 150", "Isolate section should be 50 + 100 = 150")
assert.Contains(t, finalResult, "value = 110", "Regular section should be 10 + 100 = 110")
t.Logf("Original content:\n%s\n", testContent)
t.Logf("After isolate commands:\n%s\n", isolateResult)
t.Logf("Final result:\n%s\n", finalResult)
}

32
main.go
View File

@@ -642,11 +642,13 @@ func RunIsolateCommands(association utils.FileCommandAssociation, file string, f
runIsolateCommandsLogger.Trace("File data before isolate modifications: %s", utils.LimitString(fileDataStr, 200))
anythingDone := false
currentFileData := fileDataStr
for _, isolateCommand := range association.IsolateCommands {
// Check if this isolate command should use JSON mode
if isolateCommand.JSON || *utils.JSON {
runIsolateCommandsLogger.Debug("Begin processing file with JSON isolate command %q", isolateCommand.Name)
modifications, err := processor.ProcessJSON(fileDataStr, isolateCommand, file)
modifications, err := processor.ProcessJSON(currentFileData, isolateCommand, file)
if err != nil {
runIsolateCommandsLogger.Error("Failed to process file with JSON isolate command %q: %v", isolateCommand.Name, err)
continue
@@ -661,15 +663,21 @@ func RunIsolateCommands(association utils.FileCommandAssociation, file string, f
runIsolateCommandsLogger.Debug("Executing %d JSON isolate modifications for file", len(modifications))
runIsolateCommandsLogger.Trace("JSON isolate modifications: %v", modifications)
var count int
fileDataStr, count = utils.ExecuteModifications(modifications, fileDataStr)
runIsolateCommandsLogger.Trace("File data after JSON isolate modifications: %s", utils.LimitString(fileDataStr, 200))
currentFileData, count = utils.ExecuteModifications(modifications, currentFileData)
runIsolateCommandsLogger.Trace("File data after JSON isolate modifications: %s", utils.LimitString(currentFileData, 200))
atomic.AddInt64(&stats.TotalModifications, int64(count))
cmdCount, ok := stats.ModificationsPerCommand.Load(isolateCommand.Name)
if !ok {
stats.ModificationsPerCommand.Store(isolateCommand.Name, 0)
cmdCount = 0
}
stats.ModificationsPerCommand.Store(isolateCommand.Name, cmdCount.(int)+len(modifications))
runIsolateCommandsLogger.Info("Executed %d JSON isolate modifications for file", count)
} else {
// Regular regex processing for isolate commands
runIsolateCommandsLogger.Debug("Begin processing file with isolate command %q", isolateCommand.Regex)
patterns := isolateCommand.Regexes
if len(patterns) == 0 {
patterns = []string{isolateCommand.Regex}
@@ -677,7 +685,8 @@ func RunIsolateCommands(association utils.FileCommandAssociation, file string, f
for idx, pattern := range patterns {
tmpCmd := isolateCommand
tmpCmd.Regex = pattern
modifications, err := processor.ProcessRegex(fileDataStr, tmpCmd, file)
runIsolateCommandsLogger.Debug("Begin processing file with isolate command %q (pattern %d/%d)", isolateCommand.Name, idx+1, len(patterns))
modifications, err := processor.ProcessRegex(currentFileData, tmpCmd, file)
if err != nil {
runIsolateCommandsLogger.Error("Failed to process file with isolate command %q (pattern %d/%d): %v", isolateCommand.Name, idx+1, len(patterns), err)
continue
@@ -692,11 +701,18 @@ func RunIsolateCommands(association utils.FileCommandAssociation, file string, f
runIsolateCommandsLogger.Debug("Executing %d isolate modifications for file", len(modifications))
runIsolateCommandsLogger.Trace("Isolate modifications: %v", modifications)
var count int
fileDataStr, count = utils.ExecuteModifications(modifications, fileDataStr)
runIsolateCommandsLogger.Trace("File data after isolate modifications: %s", utils.LimitString(fileDataStr, 200))
currentFileData, count = utils.ExecuteModifications(modifications, currentFileData)
runIsolateCommandsLogger.Trace("File data after isolate modifications: %s", utils.LimitString(currentFileData, 200))
atomic.AddInt64(&stats.TotalModifications, int64(count))
cmdCount, ok := stats.ModificationsPerCommand.Load(isolateCommand.Name)
if !ok {
stats.ModificationsPerCommand.Store(isolateCommand.Name, 0)
cmdCount = 0
}
stats.ModificationsPerCommand.Store(isolateCommand.Name, cmdCount.(int)+len(modifications))
runIsolateCommandsLogger.Info("Executed %d isolate modifications for file", count)
}
}
@@ -705,5 +721,5 @@ func RunIsolateCommands(association utils.FileCommandAssociation, file string, f
runIsolateCommandsLogger.Debug("No isolate modifications were made for file")
return fileDataStr, NothingToDo
}
return fileDataStr, nil
return currentFileData, nil
}

View File

@@ -487,8 +487,8 @@ func EvalRegex(L *lua.LState) int {
evalRegexLogger := processorLogger.WithPrefix("evalRegex")
evalRegexLogger.Debug("Lua evalRegex function called")
input := L.ToString(1)
pattern := L.ToString(2)
pattern := L.ToString(1)
input := L.ToString(2)
evalRegexLogger.Debug("Pattern: %q, Input: %q", pattern, input)
@@ -496,11 +496,12 @@ func EvalRegex(L *lua.LState) int {
matches := re.FindStringSubmatch(input)
evalRegexLogger.Debug("Go regex matches: %v (count: %d)", matches, len(matches))
evalRegexLogger.Debug("Matches is nil: %t", matches == nil)
if len(matches) > 0 {
matchesTable := L.NewTable()
for i, match := range matches {
matchesTable.RawSetInt(i, lua.LString(match))
matchesTable.RawSetString(fmt.Sprintf("%d", i), lua.LString(match))
evalRegexLogger.Debug("Set table[%d] = %q", i, match)
}
L.Push(matchesTable)

View File

@@ -21,7 +21,7 @@ func TestEvalRegex_CaptureGroupsReturned(t *testing.T) {
result := processor.EvalRegex(L)
assert.Equal(t, 0, result, "Expected return value to be 0")
assert.Equal(t, 1, result, "Expected return value to be 1 (one value pushed to Lua stack)")
out := L.Get(-1)
tbl, ok := out.(*lua.LTable)
@@ -35,26 +35,19 @@ func TestEvalRegex_CaptureGroupsReturned(t *testing.T) {
}
}
// Happy Path: Function returns an empty Lua table when regex pattern does not match input string.
func TestEvalRegex_NoMatchReturnsEmptyTable(t *testing.T) {
// Happy Path: Function returns nil when regex pattern does not match input string.
func TestEvalRegex_NoMatchReturnsNil(t *testing.T) {
L := lua.NewState()
defer L.Close()
L.Push(lua.LString(`(foo)(bar)`))
L.Push(lua.LString("no-match-here"))
result := processor.EvalRegex(L)
assert.Equal(t, 0, result)
assert.Equal(t, 1, result, "Expected return value to be 1 (one value pushed to Lua stack)")
out := L.Get(-1)
tbl, ok := out.(*lua.LTable)
if !ok {
t.Fatalf("Expected Lua table, got %T", out)
}
count := 0
tbl.ForEach(func(k, v lua.LValue) {
count++
})
assert.Zero(t, count, "Expected no items in the table for non-matching input")
// Should be nil when no matches found
assert.Equal(t, lua.LNil, out, "Expected nil when no matches found")
}
// Happy Path: Function handles patterns with no capture groups by returning the full match in the Lua table.
@@ -67,7 +60,7 @@ func TestEvalRegex_NoCaptureGroups(t *testing.T) {
L.Push(lua.LString(input))
result := processor.EvalRegex(L)
assert.Equal(t, 0, result)
assert.Equal(t, 1, result, "Expected return value to be 1 (one value pushed to Lua stack)")
out := L.Get(-1)
tbl, ok := out.(*lua.LTable)
@@ -84,7 +77,7 @@ func TestEvalRegex_NoCaptureGroups(t *testing.T) {
assert.Equal(t, 1, count)
}
// Edge Case: Function panics or errors when given an invalid regex pattern.
// Edge Case: Function handles invalid regex pattern by letting regexp.MustCompile panic (which is expected behavior)
func TestEvalRegex_InvalidPattern(t *testing.T) {
L := lua.NewState()
defer L.Close()
@@ -92,15 +85,13 @@ func TestEvalRegex_InvalidPattern(t *testing.T) {
L.Push(lua.LString(pattern))
L.Push(lua.LString("someinput"))
defer func() {
if r := recover(); r == nil {
t.Error("Expected panic for invalid regex pattern, but did not panic")
}
}()
processor.EvalRegex(L)
// This should panic due to invalid regex pattern
assert.Panics(t, func() {
processor.EvalRegex(L)
}, "Expected panic for invalid regex pattern")
}
// Edge Case: Function returns an empty Lua table when input string is empty.
// Edge Case: Function returns nil when input string is empty and pattern doesn't match.
func TestEvalRegex_EmptyInputString(t *testing.T) {
L := lua.NewState()
defer L.Close()
@@ -108,19 +99,11 @@ func TestEvalRegex_EmptyInputString(t *testing.T) {
L.Push(lua.LString(""))
result := processor.EvalRegex(L)
assert.Equal(t, 0, result)
assert.Equal(t, 1, result, "Expected return value to be 1 (one value pushed to Lua stack)")
out := L.Get(-1)
tbl, ok := out.(*lua.LTable)
if !ok {
t.Fatalf("Expected Lua table, got %T", out)
}
// Should be empty
count := 0
tbl.ForEach(func(k, v lua.LValue) {
count++
})
assert.Zero(t, count, "Expected empty table when input is empty")
// Should be nil when no matches found
assert.Equal(t, lua.LNil, out, "Expected nil when input is empty and pattern doesn't match")
}
// Edge Case: Function handles nil or missing arguments gracefully without causing a runtime panic.
@@ -138,7 +121,7 @@ func TestEvalRegex_MissingArguments(t *testing.T) {
}
func TestEvalComplexRegex(t *testing.T) {
// 23:47:35.567068 processor.go:369 [g:22 ] [LUA] Pistol_Round ^((Bulk_)?(Pistol|Rifle).*?Round.*?)$
// Test complex regex pattern with multiple capture groups
L := lua.NewState()
defer L.Close()
pattern := `^((Bulk_)?(Pistol|Rifle).*?Round.*?)$`
@@ -153,10 +136,13 @@ func TestEvalComplexRegex(t *testing.T) {
if !ok {
t.Fatalf("Expected Lua table, got %T", out)
}
count := 0
// Pattern should match: ["Pistol_Round", "Pistol_Round", "", "Pistol"]
// This creates 4 elements in the matches array, not 1
expectedCount := 4
actualCount := 0
tbl.ForEach(func(k, v lua.LValue) {
fmt.Println(k, v)
count++
actualCount++
})
assert.Equal(t, 1, count)
assert.Equal(t, expectedCount, actualCount, "Expected %d matches for pattern %q with input %q", expectedCount, pattern, input)
}