package config import ( "os" "testing" "github.com/spf13/afero" ) func TestNewManager(t *testing.T) { manager := NewManager() if manager == nil { t.Fatal("NewManager returned nil") } if manager.config == nil { t.Error("Manager config map is nil") } if manager.loader == nil { t.Error("Manager loader is nil") } if manager.resolvedCache == nil { t.Error("Manager resolvedCache is nil") } if manager.fs == nil { t.Error("Manager fs is nil") } } func TestLoader_FindConfigFile(t *testing.T) { // Create an in-memory filesystem for testing fs := afero.NewMemMapFs() loader := NewLoader(fs) // Create a config file in the filesystem configContent := ` environments: test: config: testKey: testValue secrets: testSecret: secretValue configDefaults: defaultKey: defaultValue ` // Create the file in the current directory if err := afero.WriteFile(fs, "config.yaml", []byte(configContent), 0644); err != nil { t.Fatalf("Failed to write test config: %v", err) } // Test finding the config file foundPath, err := loader.FindConfigFile("config.yaml") if err != nil { t.Errorf("FindConfigFile failed: %v", err) } // In memory fs, the path should be exactly what we created if foundPath != "config.yaml" { t.Errorf("Expected config.yaml, got %s", foundPath) } } func TestLoader_LoadYAML(t *testing.T) { fs := afero.NewMemMapFs() loader := NewLoader(fs) // Create a test config file testConfig := ` environments: test: config: testKey: testValue configDefaults: defaultKey: defaultValue ` if err := afero.WriteFile(fs, "test-config.yaml", []byte(testConfig), 0644); err != nil { t.Fatalf("Failed to write test config: %v", err) } // Load the YAML config, err := loader.LoadYAML("test-config.yaml") if err != nil { t.Fatalf("LoadYAML failed: %v", err) } // Verify the structure envs, ok := config["environments"].(map[string]interface{}) if !ok { t.Fatal("environments not found or wrong type") } testEnv, ok := envs["test"].(map[string]interface{}) if !ok { t.Fatal("test environment not found") } testConfig2, ok := testEnv["config"].(map[string]interface{}) if !ok { t.Fatal("test config not found") } if testConfig2["testKey"] != "testValue" { t.Errorf("Expected testKey=testValue, got %v", testConfig2["testKey"]) } } func TestResolver_ResolveEnv(t *testing.T) { fs := afero.NewMemMapFs() resolver := NewResolver("", "", fs) // Set a test environment variable os.Setenv("TEST_CONFIG_VAR", "test-value") defer os.Unsetenv("TEST_CONFIG_VAR") // Test resolving environment variable result := resolver.Resolve("$ENV:TEST_CONFIG_VAR") if result != "test-value" { t.Errorf("Expected 'test-value', got %v", result) } // Test non-existent env var result = resolver.Resolve("$ENV:NON_EXISTENT_VAR") if result != nil { t.Errorf("Expected nil for non-existent env var, got %v", result) } } func TestResolver_ResolveFile(t *testing.T) { fs := afero.NewMemMapFs() resolver := NewResolver("", "", fs) // Create a test file secretContent := "my-secret-value" if err := afero.WriteFile(fs, "/test-secret.txt", []byte(secretContent+"\n"), 0644); err != nil { t.Fatalf("Failed to write test file: %v", err) } // Test resolving file result := resolver.Resolve("$FILE:/test-secret.txt") if result != secretContent { t.Errorf("Expected '%s', got %v", secretContent, result) } // Test non-existent file result = resolver.Resolve("$FILE:/non/existent/file") if result != nil { t.Errorf("Expected nil for non-existent file, got %v", result) } } func TestManager_GetAndSet(t *testing.T) { // Create an in-memory filesystem fs := afero.NewMemMapFs() // Create a test config file testConfig := ` environments: dev: config: apiURL: http://dev.example.com timeout: 30 debug: true secrets: apiKey: dev-key-123 prod: config: apiURL: https://prod.example.com timeout: 10 debug: false secrets: apiKey: $ENV:PROD_API_KEY configDefaults: appName: TestApp timeout: 20 port: 8080 ` if err := afero.WriteFile(fs, "config.yaml", []byte(testConfig), 0644); err != nil { t.Fatalf("Failed to write test config: %v", err) } // Create manager and set the filesystem manager := NewManager() manager.SetFs(fs) // Load config should find the file automatically manager.SetEnvironment("dev") // Test getting config values if v := manager.Get("apiURL", ""); v != "http://dev.example.com" { t.Errorf("Expected dev apiURL, got %v", v) } if v := manager.Get("timeout", 0); v != 30 { t.Errorf("Expected timeout=30, got %v", v) } if v := manager.Get("debug", false); v != true { t.Errorf("Expected debug=true, got %v", v) } // Test default values if v := manager.Get("appName", ""); v != "TestApp" { t.Errorf("Expected appName from defaults, got %v", v) } // Test getting secrets if v := manager.GetSecret("apiKey", ""); v != "dev-key-123" { t.Errorf("Expected dev apiKey, got %v", v) } // Switch to prod environment manager.SetEnvironment("prod") if v := manager.Get("apiURL", ""); v != "https://prod.example.com" { t.Errorf("Expected prod apiURL, got %v", v) } // Test environment variable resolution in secrets os.Setenv("PROD_API_KEY", "prod-key-456") defer os.Unsetenv("PROD_API_KEY") if v := manager.GetSecret("apiKey", ""); v != "prod-key-456" { t.Errorf("Expected resolved env var for apiKey, got %v", v) } } func TestGlobalAPI(t *testing.T) { // Create an in-memory filesystem fs := afero.NewMemMapFs() // Create a test config file testConfig := ` environments: test: config: stringVal: hello intVal: 42 boolVal: true secrets: secret1: test-secret configDefaults: defaultString: world ` if err := afero.WriteFile(fs, "config.yaml", []byte(testConfig), 0644); err != nil { t.Fatalf("Failed to write test config: %v", err) } // Use the global API with the test filesystem SetFs(fs) SetEnvironment("test") // Test type-safe getters if v := GetString("stringVal"); v != "hello" { t.Errorf("Expected 'hello', got %v", v) } if v := GetInt("intVal"); v != 42 { t.Errorf("Expected 42, got %v", v) } if v := GetBool("boolVal"); v != true { t.Errorf("Expected true, got %v", v) } if v := GetSecretString("secret1"); v != "test-secret" { t.Errorf("Expected 'test-secret', got %v", v) } // Test defaults if v := GetString("defaultString"); v != "world" { t.Errorf("Expected 'world', got %v", v) } } func TestManager_SetFs(t *testing.T) { // Create manager with default OS filesystem manager := NewManager() // Create an in-memory filesystem memFs := afero.NewMemMapFs() // Write a config file to the memory fs testConfig := ` environments: test: config: testKey: fromMemory configDefaults: defaultKey: memoryDefault ` if err := afero.WriteFile(memFs, "config.yaml", []byte(testConfig), 0644); err != nil { t.Fatalf("Failed to write test config: %v", err) } // Set the filesystem manager.SetFs(memFs) manager.SetEnvironment("test") // Test that it reads from the memory filesystem if v := manager.Get("testKey", ""); v != "fromMemory" { t.Errorf("Expected 'fromMemory', got %v", v) } if v := manager.Get("defaultKey", ""); v != "memoryDefault" { t.Errorf("Expected 'memoryDefault', got %v", v) } }