package config import ( "os" "reflect" "testing" ) // TestLoad verifies we can successfully load configuration values from a Lua file. func TestLoad(t *testing.T) { // Create a temporary config file content := ` -- Basic configuration values host = "localhost" port = 8080 debug = true pi = 3.14159 ` configFile := createTempLuaFile(t, content) defer os.Remove(configFile) // Load the config cfg, err := Load(configFile) if err != nil { t.Fatalf("Failed to load config: %v", err) } // Verify values were loaded correctly if host := cfg.GetString("host", ""); host != "localhost" { t.Errorf("Expected host to be 'localhost', got '%s'", host) } if port := cfg.GetInt("port", 0); port != 8080 { t.Errorf("Expected port to be 8080, got %d", port) } if debug := cfg.GetBool("debug", false); !debug { t.Errorf("Expected debug to be true") } if pi := cfg.GetFloat("pi", 0); pi != 3.14159 { t.Errorf("Expected pi to be 3.14159, got %f", pi) } } // TestLoadErrors ensures the package properly handles loading errors. func TestLoadErrors(t *testing.T) { // Test with non-existent file _, err := Load("nonexistent.lua") if err == nil { t.Error("Expected error when loading non-existent file, got nil") } // Test with invalid Lua content := ` -- This is invalid Lua host = "localhost port = 8080) ` configFile := createTempLuaFile(t, content) defer os.Remove(configFile) _, err = Load(configFile) if err == nil { t.Error("Expected error when loading invalid Lua, got nil") } } // TestLocalVsGlobal verifies only global variables are exported, not locals. func TestLocalVsGlobal(t *testing.T) { // Create a temporary config file with both local and global variables content := ` -- Local variables should not be exported local local_var = "hidden" -- Global variables should be exported global_var = "visible" -- A function that uses both function test_func() return local_var .. " " .. global_var end ` configFile := createTempLuaFile(t, content) defer os.Remove(configFile) // Load the config cfg, err := Load(configFile) if err != nil { t.Fatalf("Failed to load config: %v", err) } // Check that global_var exists if globalVar := cfg.GetString("global_var", ""); globalVar != "visible" { t.Errorf("Expected global_var to be 'visible', got '%s'", globalVar) } // Check that local_var does not exist if localVar := cfg.GetString("local_var", "default"); localVar != "default" { t.Errorf("Expected local_var to use default, got '%s'", localVar) } // Check that functions are not exported if val := cfg.Get("test_func"); val != nil { t.Errorf("Expected function to not be exported, got %v", val) } } // TestArrayHandling verifies correct handling of Lua arrays. func TestArrayHandling(t *testing.T) { // Create a temporary config file with arrays content := ` -- Numeric array numbers = {10, 20, 30, 40, 50} -- String array strings = {"apple", "banana", "cherry"} -- Mixed array mixed = {1, "two", true, 4.5} ` configFile := createTempLuaFile(t, content) defer os.Remove(configFile) // Load the config cfg, err := Load(configFile) if err != nil { t.Fatalf("Failed to load config: %v", err) } // Test GetIntArray intArray := cfg.GetIntArray("numbers") expectedInts := []int{10, 20, 30, 40, 50} if !reflect.DeepEqual(intArray, expectedInts) { t.Errorf("Expected int array %v, got %v", expectedInts, intArray) } // Test GetStringArray strArray := cfg.GetStringArray("strings") expectedStrs := []string{"apple", "banana", "cherry"} if !reflect.DeepEqual(strArray, expectedStrs) { t.Errorf("Expected string array %v, got %v", expectedStrs, strArray) } // Test GetArray with mixed types mixedArray := cfg.GetArray("mixed") if len(mixedArray) != 4 { t.Errorf("Expected mixed array length 4, got %d", len(mixedArray)) // Skip further tests if array is empty to avoid panic return } // Check types - carefully to avoid panics if len(mixedArray) > 0 { if num, ok := mixedArray[0].(float64); !ok || num != 1 { t.Errorf("Expected first element to be 1, got %v", mixedArray[0]) } } if len(mixedArray) > 1 { if str, ok := mixedArray[1].(string); !ok || str != "two" { t.Errorf("Expected second element to be 'two', got %v", mixedArray[1]) } } } // TestComplexTable tests handling of complex nested tables. func TestComplexTable(t *testing.T) { // Create a temporary config file with complex tables content := ` -- Nested table structure server = { host = "localhost", port = 8080, settings = { timeout = 30, retries = 3 } } -- Table with mixed array and map elements mixed_table = { list = {1, 2, 3}, mapping = { a = "apple", b = "banana" } } ` configFile := createTempLuaFile(t, content) defer os.Remove(configFile) // Load the config cfg, err := Load(configFile) if err != nil { t.Fatalf("Failed to load config: %v", err) } // Test getting nested values serverMap := cfg.GetMap("server") if serverMap == nil { t.Fatal("Expected server map to exist") } // Check first level values if host, ok := serverMap["host"].(string); !ok || host != "localhost" { t.Errorf("Expected server.host to be 'localhost', got %v", serverMap["host"]) } if port, ok := serverMap["port"].(float64); !ok || port != 8080 { t.Errorf("Expected server.port to be 8080, got %v", serverMap["port"]) } // Check nested settings settings, ok := serverMap["settings"].(map[string]any) if !ok { t.Fatal("Expected server.settings to be a map") } if timeout, ok := settings["timeout"].(float64); !ok || timeout != 30 { t.Errorf("Expected server.settings.timeout to be 30, got %v", settings["timeout"]) } } // TestDefaultValues verifies default values work correctly when keys don't exist. func TestDefaultValues(t *testing.T) { // Create a temporary config file content := ` -- Just one value existing = "value" ` configFile := createTempLuaFile(t, content) defer os.Remove(configFile) // Load the config cfg, err := Load(configFile) if err != nil { t.Fatalf("Failed to load config: %v", err) } // Test defaults for non-existent keys if val := cfg.GetString("nonexistent", "default"); val != "default" { t.Errorf("Expected default string, got '%s'", val) } if val := cfg.GetInt("nonexistent", 42); val != 42 { t.Errorf("Expected default int 42, got %d", val) } if val := cfg.GetFloat("nonexistent", 3.14); val != 3.14 { t.Errorf("Expected default float 3.14, got %f", val) } if val := cfg.GetBool("nonexistent", true); !val { t.Errorf("Expected default bool true, got false") } } // TestModifyConfig tests the ability to modify configuration values. func TestModifyConfig(t *testing.T) { // Create a config manually cfg := New() // Set some values cfg.Set("host", "localhost") cfg.Set("port", 8080) // Verify the values were set if host := cfg.GetString("host", ""); host != "localhost" { t.Errorf("Expected host to be 'localhost', got '%s'", host) } if port := cfg.GetInt("port", 0); port != 8080 { t.Errorf("Expected port to be 8080, got %d", port) } // Modify a value cfg.Set("host", "127.0.0.1") // Verify the change if host := cfg.GetString("host", ""); host != "127.0.0.1" { t.Errorf("Expected modified host to be '127.0.0.1', got '%s'", host) } } // TestTypeConversion tests the type conversion in getter methods. func TestTypeConversion(t *testing.T) { // Create a temporary config file with values that need conversion content := ` -- Numbers that can be integers int_as_float = 42.0 -- Floats that should remain floats float_val = 3.14159 ` configFile := createTempLuaFile(t, content) defer os.Remove(configFile) // Load the config cfg, err := Load(configFile) if err != nil { t.Fatalf("Failed to load config: %v", err) } // Test GetInt with a float value if val := cfg.GetInt("int_as_float", 0); val != 42 { t.Errorf("Expected int 42, got %d", val) } // Test GetFloat with an int value if val := cfg.GetFloat("int_as_float", 0); val != 42.0 { t.Errorf("Expected float 42.0, got %f", val) } // Test incorrect type handling cfg.Set("string_val", "not a number") if val := cfg.GetInt("string_val", 99); val != 99 { t.Errorf("Expected default int 99 for string value, got %d", val) } if val := cfg.GetFloat("string_val", 99.9); val != 99.9 { t.Errorf("Expected default float 99.9 for string value, got %f", val) } if val := cfg.GetBool("float_val", false); val != false { t.Errorf("Expected default false for non-bool value, got true") } } // Helper function to create a temporary Lua file with content func createTempLuaFile(t *testing.T, content string) string { t.Helper() tempFile, err := os.CreateTemp("", "config-test-*.lua") if err != nil { t.Fatalf("Failed to create temp file: %v", err) } if _, err := tempFile.WriteString(content); err != nil { os.Remove(tempFile.Name()) t.Fatalf("Failed to write to temp file: %v", err) } if err := tempFile.Close(); err != nil { os.Remove(tempFile.Name()) t.Fatalf("Failed to close temp file: %v", err) } return tempFile.Name() }