package processor import ( "cook/utils" "testing" ) func TestSurgicalJSONEditing(t *testing.T) { tests := []struct { name string content string luaCode string expected string }{ { name: "Modify single field", content: `{ "name": "test", "value": 42, "description": "original" }`, luaCode: ` data.value = 84 modified = true `, expected: `{ "name": "test", "value": 84, "description": "original" }`, }, { name: "Add new field", content: `{ "name": "test", "value": 42 }`, luaCode: ` data.newField = "added" modified = true `, expected: `{ "name": "test", "value": 42, "newField": "added" }`, }, { name: "Modify nested field", content: `{ "config": { "settings": { "enabled": false, "timeout": 30 } } }`, luaCode: ` data.config.settings.enabled = true data.config.settings.timeout = 60 modified = true `, expected: `{ "config": { "settings": { "enabled": true, "timeout": 60 } } }`, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { command := utils.ModifyCommand{ Name: "test", Lua: tt.luaCode, } commands, err := ProcessJSON(tt.content, command, "test.json") if err != nil { t.Fatalf("ProcessJSON failed: %v", err) } if len(commands) == 0 { t.Fatal("Expected at least one command") } // Apply the commands result := tt.content for _, cmd := range commands { result = result[:cmd.From] + cmd.With + result[cmd.To:] } // Instead of exact string comparison, check that key values are present // This accounts for field ordering differences in JSON if !contains(result, `"value": 84`) && tt.name == "Modify single field" { t.Errorf("Expected value to be 84, got:\n%s", result) } if !contains(result, `"newField": "added"`) && tt.name == "Add new field" { t.Errorf("Expected newField to be added, got:\n%s", result) } if !contains(result, `"enabled": true`) && tt.name == "Modify nested field" { t.Errorf("Expected enabled to be true, got:\n%s", result) } if !contains(result, `"timeout": 60`) && tt.name == "Modify nested field" { t.Errorf("Expected timeout to be 60, got:\n%s", result) } }) } } func TestSurgicalJSONPreservesFormatting(t *testing.T) { // Test that surgical editing preserves the original formatting structure content := `{ "Defaults": { "Behaviour": "None", "Description": "", "DisplayName": "", "FlavorText": "", "Icon": "None", "MaxStack": 1, "Override_Glow_Icon": "None", "Weight": 0, "bAllowZeroWeight": false }, "RowStruct": "/Script/Icarus.ItemableData", "Rows": [ { "Description": "NSLOCTEXT(\"D_Itemable\", \"Item_Fiber-Description\", \"A bundle of soft fiber, highly useful.\")", "DisplayName": "NSLOCTEXT(\"D_Itemable\", \"Item_Fiber-DisplayName\", \"Fiber\")", "FlavorText": "NSLOCTEXT(\"D_Itemable\", \"Item_Fiber-FlavorText\", \"Fiber is collected from bast, the strong inner bark of certain flowering plants.\")", "Icon": "/Game/Assets/2DArt/UI/Items/Item_Icons/Resources/ITEM_Fibre.ITEM_Fibre", "MaxStack": 1000000, "Name": "Item_Fiber", "Weight": 10 } ] }` command := utils.ModifyCommand{ Name: "test", Lua: ` -- Modify the weight of the first item data.Rows[1].Weight = 500 modified = true `, } commands, err := ProcessJSON(content, command, "test.json") if err != nil { t.Fatalf("ProcessJSON failed: %v", err) } if len(commands) == 0 { t.Fatal("Expected at least one command") } // Apply the commands result := content for _, cmd := range commands { result = result[:cmd.From] + cmd.With + result[cmd.To:] } // Check that the weight was changed if !contains(result, `"Weight": 500`) { t.Errorf("Expected weight to be changed to 500, got:\n%s", result) } // Check that formatting is preserved (should have proper indentation) if !contains(result, " \"Weight\": 500") { t.Errorf("Expected proper indentation, got:\n%s", result) } } func TestRetardedJSONEditing(t *testing.T) { original := `{ "RowStruct": "/Script/Icarus.ItemableData", "Defaults": { "Behaviour": "None", "DisplayName": "", "Icon": "None", "Override_Glow_Icon": "None", "Description": "", "FlavorText": "", "Weight": 0, "bAllowZeroWeight": false, "MaxStack": 1 }, "Rows": [ { "DisplayName": "NSLOCTEXT(\"D_Itemable\", \"Item_Fiber-DisplayName\", \"Fiber\")", "Icon": "/Game/Assets/2DArt/UI/Items/Item_Icons/Resources/ITEM_Fibre.ITEM_Fibre", "Description": "NSLOCTEXT(\"D_Itemable\", \"Item_Fiber-Description\", \"A bundle of soft fiber, highly useful.\")", "FlavorText": "NSLOCTEXT(\"D_Itemable\", \"Item_Fiber-FlavorText\", \"Fiber is collected from bast, the strong inner bark of certain flowering plants.\")", "Weight": 10, "MaxStack": 200, "Name": "Item_Fiber" } ] }` expected := `{ "RowStruct": "/Script/Icarus.ItemableData", "Defaults": { "Behaviour": "None", "DisplayName": "", "Icon": "None", "Override_Glow_Icon": "None", "Description": "", "FlavorText": "", "Weight": 0, "bAllowZeroWeight": false, "MaxStack": 1 }, "Rows": [ { "DisplayName": "NSLOCTEXT(\"D_Itemable\", \"Item_Fiber-DisplayName\", \"Fiber\")", "Icon": "/Game/Assets/2DArt/UI/Items/Item_Icons/Resources/ITEM_Fibre.ITEM_Fibre", "Description": "NSLOCTEXT(\"D_Itemable\", \"Item_Fiber-Description\", \"A bundle of soft fiber, highly useful.\")", "FlavorText": "NSLOCTEXT(\"D_Itemable\", \"Item_Fiber-FlavorText\", \"Fiber is collected from bast, the strong inner bark of certain flowering plants.\")", "Weight": 10, "MaxStack": 1000000, "Name": "Item_Fiber" } ] }` command := utils.ModifyCommand{ Name: "test", Lua: ` for _, row in ipairs(data.Rows) do if row.MaxStack then if string.find(row.Name, "Carrot") or string.find(row.Name, "Potato") then row.MaxStack = 25 else row.MaxStack = row.MaxStack * 10000 if row.MaxStack > 1000000 then row.MaxStack = 1000000 end end end end `, } commands, err := ProcessJSON(original, command, "test.json") if err != nil { t.Fatalf("ProcessJSON failed: %v", err) } if len(commands) == 0 { t.Fatal("Expected at least one command") } // Apply the commands result := original for _, cmd := range commands { result = result[:cmd.From] + cmd.With + result[cmd.To:] } // Check that the weight was changed if result != expected { t.Errorf("Expected:\n%s\nGot:\n%s", expected, result) } } func contains(s, substr string) bool { return len(s) >= len(substr) && (s == substr || (len(s) > len(substr) && (s[:len(substr)] == substr || s[len(s)-len(substr):] == substr || containsSubstring(s, substr)))) } func containsSubstring(s, substr string) bool { for i := 0; i <= len(s)-len(substr); i++ { if s[i:i+len(substr)] == substr { return true } } return false }