package ezconf import ( "os" "strings" "testing" ) // testConfig is a Config struct used by multiple tests type testConfig struct { LogLevel string `ezconf:"LOG_LEVEL,description:Log level for the application,default:info"` LogOutput string `ezconf:"LOG_OUTPUT,description:Output destination,default:console"` DatabaseURL string `ezconf:"DATABASE_URL,description:Database connection string,required"` } func TestNew(t *testing.T) { loader := New() if loader == nil { t.Fatal("New() returned nil") } if loader.configFuncs == nil { t.Error("configFuncs map is nil") } if loader.configStructs == nil { t.Error("configStructs slice is nil") } if loader.extraEnvVars == nil { t.Error("extraEnvVars slice is nil") } if loader.configs == nil { t.Error("configs map is nil") } } func TestAddConfigFunc(t *testing.T) { loader := New() testFunc := func() (interface{}, error) { return "test config", nil } err := loader.AddConfigFunc("test", testFunc) if err != nil { t.Errorf("AddConfigFunc failed: %v", err) } if len(loader.configFuncs) != 1 { t.Errorf("expected 1 config func, got %d", len(loader.configFuncs)) } } func TestAddConfigFunc_NilFunction(t *testing.T) { loader := New() err := loader.AddConfigFunc("test", nil) if err == nil { t.Error("expected error for nil function") } } func TestAddConfigFunc_EmptyName(t *testing.T) { loader := New() testFunc := func() (interface{}, error) { return "test config", nil } err := loader.AddConfigFunc("", testFunc) if err == nil { t.Error("expected error for empty name") } } func TestAddConfigStruct(t *testing.T) { loader := New() err := loader.AddConfigStruct(&testConfig{}, "Test") if err != nil { t.Errorf("AddConfigStruct failed: %v", err) } if len(loader.configStructs) != 1 { t.Errorf("expected 1 config struct, got %d", len(loader.configStructs)) } } func TestAddConfigStruct_NilPointer(t *testing.T) { loader := New() err := loader.AddConfigStruct(nil, "Test") if err == nil { t.Error("expected error for nil pointer") } } func TestAddConfigStruct_EmptyGroupName(t *testing.T) { loader := New() err := loader.AddConfigStruct(&testConfig{}, "") if err != nil { t.Errorf("AddConfigStruct failed: %v", err) } // Should default to "Other" if loader.configStructs[0].groupName != "Other" { t.Errorf("expected group name 'Other', got %s", loader.configStructs[0].groupName) } } func TestAddEnvVar(t *testing.T) { loader := New() envVar := EnvVar{ Name: "TEST_VAR", Description: "Test variable", Required: true, Default: "default_value", } loader.AddEnvVar(envVar) if len(loader.extraEnvVars) != 1 { t.Errorf("expected 1 extra env var, got %d", len(loader.extraEnvVars)) } if loader.extraEnvVars[0].Name != "TEST_VAR" { t.Errorf("expected TEST_VAR, got %s", loader.extraEnvVars[0].Name) } } func TestLoad(t *testing.T) { loader := New() // Add a test config function testCfg := struct { Value string }{Value: "test"} loader.AddConfigFunc("test", func() (interface{}, error) { return testCfg, nil }) // Add config struct for tag parsing loader.AddConfigStruct(&testConfig{}, "Test") // Add an extra env var loader.AddEnvVar(EnvVar{ Name: "EXTRA_VAR", Description: "Extra test variable", Default: "extra", }) err := loader.Load() if err != nil { t.Fatalf("Load failed: %v", err) } // Check that config was loaded cfg, ok := loader.GetConfig("test") if !ok { t.Error("test config not loaded") } if cfg == nil { t.Error("test config is nil") } // Check that env vars were extracted envVars := loader.GetEnvVars() if len(envVars) == 0 { t.Error("expected at least one env var") } // Check for extra var foundExtra := false for _, ev := range envVars { if ev.Name == "EXTRA_VAR" { foundExtra = true break } } if !foundExtra { t.Error("extra env var not found") } } func TestLoad_ConfigFuncError(t *testing.T) { loader := New() loader.AddConfigFunc("error", func() (interface{}, error) { return nil, os.ErrNotExist }) err := loader.Load() if err == nil { t.Error("expected error from failing config func") } } func TestGetConfig(t *testing.T) { loader := New() testCfg := "test config" loader.configs["test"] = testCfg cfg, ok := loader.GetConfig("test") if !ok { t.Error("expected to find test config") } if cfg != testCfg { t.Error("config value mismatch") } // Test non-existent config _, ok = loader.GetConfig("nonexistent") if ok { t.Error("expected not to find nonexistent config") } } func TestGetAllConfigs(t *testing.T) { loader := New() loader.configs["test1"] = "config1" loader.configs["test2"] = "config2" allConfigs := loader.GetAllConfigs() if len(allConfigs) != 2 { t.Errorf("expected 2 configs, got %d", len(allConfigs)) } if allConfigs["test1"] != "config1" { t.Error("test1 config mismatch") } if allConfigs["test2"] != "config2" { t.Error("test2 config mismatch") } } func TestGetEnvVars(t *testing.T) { loader := New() loader.envVars = []EnvVar{ {Name: "VAR1", Description: "Variable 1"}, {Name: "VAR2", Description: "Variable 2"}, } envVars := loader.GetEnvVars() if len(envVars) != 2 { t.Errorf("expected 2 env vars, got %d", len(envVars)) } } func TestParseEnvVars(t *testing.T) { loader := New() // Add a test config function loader.AddConfigFunc("test", func() (interface{}, error) { return "test config", nil }) // Add config struct for tag parsing loader.AddConfigStruct(&testConfig{}, "Test") // Add an extra env var loader.AddEnvVar(EnvVar{ Name: "EXTRA_VAR", Description: "Extra test variable", Default: "extra", }) err := loader.ParseEnvVars() if err != nil { t.Fatalf("ParseEnvVars failed: %v", err) } // Check that env vars were extracted envVars := loader.GetEnvVars() if len(envVars) == 0 { t.Error("expected at least one env var") } // Check for extra var foundExtra := false for _, ev := range envVars { if ev.Name == "EXTRA_VAR" { foundExtra = true break } } if !foundExtra { t.Error("extra env var not found") } // Check that configs are NOT loaded (should be empty) configs := loader.GetAllConfigs() if len(configs) != 0 { t.Errorf("expected no configs loaded after ParseEnvVars, got %d", len(configs)) } } func TestLoadConfigs(t *testing.T) { loader := New() // Add a test config function testCfg := struct { Value string }{Value: "test"} loader.AddConfigFunc("test", func() (interface{}, error) { return testCfg, nil }) // Manually set some env vars (simulating ParseEnvVars already called) loader.envVars = []EnvVar{ {Name: "TEST_VAR", Description: "Test variable"}, } err := loader.LoadConfigs() if err != nil { t.Fatalf("LoadConfigs failed: %v", err) } // Check that config was loaded cfg, ok := loader.GetConfig("test") if !ok { t.Error("test config not loaded") } if cfg == nil { t.Error("test config is nil") } _ = cfg // Use the variable to avoid unused variable error // Check that env vars are NOT modified (should remain as set) envVars := loader.GetEnvVars() if len(envVars) != 1 { t.Errorf("expected 1 env var, got %d", len(envVars)) } } func TestLoadConfigs_Error(t *testing.T) { loader := New() loader.AddConfigFunc("error", func() (interface{}, error) { return nil, os.ErrNotExist }) err := loader.LoadConfigs() if err == nil { t.Error("expected error from failing config func") } } func TestParseEnvVars_Then_LoadConfigs(t *testing.T) { loader := New() // Add a test config function testCfg := struct { Value string }{Value: "test"} loader.AddConfigFunc("test", func() (interface{}, error) { return testCfg, nil }) // Add config struct for tag parsing loader.AddConfigStruct(&testConfig{}, "Test") // Add an extra env var loader.AddEnvVar(EnvVar{ Name: "EXTRA_VAR", Description: "Extra test variable", Default: "extra", }) // First parse env vars err := loader.ParseEnvVars() if err != nil { t.Fatalf("ParseEnvVars failed: %v", err) } // Check env vars are extracted but configs are not loaded envVars := loader.GetEnvVars() if len(envVars) == 0 { t.Error("expected env vars to be extracted") } configs := loader.GetAllConfigs() if len(configs) != 0 { t.Error("expected no configs loaded yet") } // Then load configs err = loader.LoadConfigs() if err != nil { t.Fatalf("LoadConfigs failed: %v", err) } // Check both env vars and configs are loaded _, ok := loader.GetConfig("test") if !ok { t.Error("test config not loaded after LoadConfigs") } configs = loader.GetAllConfigs() if len(configs) != 1 { t.Errorf("expected 1 config loaded, got %d", len(configs)) } } func TestParseEnvVars_GroupName(t *testing.T) { loader := New() loader.AddConfigStruct(&testConfig{}, "MyGroup") err := loader.ParseEnvVars() if err != nil { t.Fatalf("ParseEnvVars failed: %v", err) } envVars := loader.GetEnvVars() for _, ev := range envVars { if ev.Group != "MyGroup" { t.Errorf("expected group 'MyGroup', got '%s' for var %s", ev.Group, ev.Name) } } } func TestParseEnvVars_CurrentValues(t *testing.T) { loader := New() loader.AddConfigStruct(&testConfig{}, "Test") // Set an env var t.Setenv("LOG_LEVEL", "debug") err := loader.ParseEnvVars() if err != nil { t.Fatalf("ParseEnvVars failed: %v", err) } envVars := loader.GetEnvVars() for _, ev := range envVars { if ev.Name == "LOG_LEVEL" { if ev.CurrentValue != "debug" { t.Errorf("expected CurrentValue 'debug', got '%s'", ev.CurrentValue) } return } } t.Error("LOG_LEVEL not found in env vars") } func TestParseEnvVars_GenerateEnvFile_Integration(t *testing.T) { loader := New() // Add config struct for tag parsing loader.AddConfigStruct(&testConfig{}, "Test") // Parse env vars if err := loader.ParseEnvVars(); err != nil { t.Fatalf("ParseEnvVars failed: %v", err) } envVars := loader.GetEnvVars() if len(envVars) == 0 { t.Error("expected env vars from config struct") } // Now test that we can generate an env file without calling Load() tempDir := t.TempDir() envFile := tempDir + "/test-generated.env" err := loader.GenerateEnvFile(envFile, false) if err != nil { t.Fatalf("GenerateEnvFile failed: %v", err) } // Verify the file was created and contains expected content content, err := os.ReadFile(envFile) if err != nil { t.Fatalf("failed to read generated file: %v", err) } output := string(content) if !strings.Contains(output, "# Environment Configuration") { t.Error("expected header in generated file") } // Should contain environment variables from config struct foundVar := false for _, ev := range envVars { if strings.Contains(output, ev.Name) { foundVar = true break } } if !foundVar { t.Error("expected to find at least one environment variable in generated file") } t.Logf("Successfully generated env file with %d variables", len(envVars)) }