package ezconf import ( "bytes" "os" "path/filepath" "strings" "testing" ) func TestPrintEnvVars(t *testing.T) { loader := New() loader.envVars = []EnvVar{ { Name: "LOG_LEVEL", Description: "Log level", Required: false, Default: "info", CurrentValue: "debug", }, { Name: "DATABASE_URL", Description: "Database connection", Required: true, Default: "", CurrentValue: "postgres://localhost/db", }, } // Test without values t.Run("without values", func(t *testing.T) { buf := &bytes.Buffer{} err := loader.PrintEnvVars(buf, false) if err != nil { t.Fatalf("PrintEnvVars failed: %v", err) } output := buf.String() if !strings.Contains(output, "LOG_LEVEL") { t.Error("output should contain LOG_LEVEL") } if !strings.Contains(output, "Log level") { t.Error("output should contain description") } if !strings.Contains(output, "(default: info)") { t.Error("output should contain default value") } if strings.Contains(output, "debug") { t.Error("output should not contain current value when showValues is false") } }) // Test with values t.Run("with values", func(t *testing.T) { buf := &bytes.Buffer{} err := loader.PrintEnvVars(buf, true) if err != nil { t.Fatalf("PrintEnvVars failed: %v", err) } output := buf.String() if !strings.Contains(output, "LOG_LEVEL=debug") { t.Error("output should contain LOG_LEVEL=debug") } if !strings.Contains(output, "DATABASE_URL=postgres://localhost/db") { t.Error("output should contain DATABASE_URL value") } if !strings.Contains(output, "(required)") { t.Error("output should indicate required variables") } }) } func TestGenerateEnvFile(t *testing.T) { loader := New() loader.envVars = []EnvVar{ { Name: "LOG_LEVEL", Description: "Log level", Required: false, Default: "info", CurrentValue: "debug", }, { Name: "DATABASE_URL", Description: "Database connection", Required: true, Default: "postgres://localhost/db", CurrentValue: "", }, } tempDir := t.TempDir() t.Run("generate with defaults", func(t *testing.T) { envFile := filepath.Join(tempDir, "test1.env") err := loader.GenerateEnvFile(envFile, false) if err != nil { t.Fatalf("GenerateEnvFile failed: %v", err) } content, err := os.ReadFile(envFile) if err != nil { t.Fatalf("failed to read generated file: %v", err) } output := string(content) if !strings.Contains(output, "LOG_LEVEL=info") { t.Error("expected default value for LOG_LEVEL") } if !strings.Contains(output, "# Log level") { t.Error("expected description comment") } if !strings.Contains(output, "# Database connection") { t.Error("expected DATABASE_URL description") } }) t.Run("generate with current values", func(t *testing.T) { envFile := filepath.Join(tempDir, "test2.env") err := loader.GenerateEnvFile(envFile, true) if err != nil { t.Fatalf("GenerateEnvFile failed: %v", err) } content, err := os.ReadFile(envFile) if err != nil { t.Fatalf("failed to read generated file: %v", err) } output := string(content) if !strings.Contains(output, "LOG_LEVEL=debug") { t.Error("expected current value for LOG_LEVEL") } // DATABASE_URL has no current value, should use default if !strings.Contains(output, "DATABASE_URL=postgres://localhost/db") { t.Error("expected default value for DATABASE_URL when current is empty") } }) t.Run("preserve untracked variables", func(t *testing.T) { envFile := filepath.Join(tempDir, "test3.env") // Create existing file with untracked variable existing := `# Existing file LOG_LEVEL=warn CUSTOM_VAR=custom_value ANOTHER_VAR=another_value ` if err := os.WriteFile(envFile, []byte(existing), 0644); err != nil { t.Fatalf("failed to create existing file: %v", err) } // Generate new file - should preserve untracked variables err := loader.GenerateEnvFile(envFile, false) if err != nil { t.Fatalf("GenerateEnvFile failed: %v", err) } content, err := os.ReadFile(envFile) if err != nil { t.Fatalf("failed to read generated file: %v", err) } output := string(content) // Should have tracked variables with new format if !strings.Contains(output, "LOG_LEVEL") { t.Error("expected LOG_LEVEL to be present") } if !strings.Contains(output, "DATABASE_URL") { t.Error("expected DATABASE_URL to be present") } // Should preserve untracked variables if !strings.Contains(output, "CUSTOM_VAR=custom_value") { t.Error("expected to preserve CUSTOM_VAR") } if !strings.Contains(output, "ANOTHER_VAR=another_value") { t.Error("expected to preserve ANOTHER_VAR") } // Should have untracked section header if !strings.Contains(output, "Untracked Variables") { t.Error("expected untracked variables section header") } }) } func TestUpdateEnvFile(t *testing.T) { loader := New() loader.envVars = []EnvVar{ { Name: "LOG_LEVEL", Description: "Log level", Default: "info", }, { Name: "NEW_VAR", Description: "New variable", Default: "new_default", }, } tempDir := t.TempDir() t.Run("update existing file", func(t *testing.T) { envFile := filepath.Join(tempDir, "existing.env") // Create existing file existing := `# Existing file LOG_LEVEL=debug OLD_VAR=old_value ` if err := os.WriteFile(envFile, []byte(existing), 0644); err != nil { t.Fatalf("failed to create existing file: %v", err) } err := loader.UpdateEnvFile(envFile, false) if err != nil { t.Fatalf("UpdateEnvFile failed: %v", err) } content, err := os.ReadFile(envFile) if err != nil { t.Fatalf("failed to read updated file: %v", err) } output := string(content) // Should preserve existing value if !strings.Contains(output, "LOG_LEVEL=debug") { t.Error("expected to preserve existing LOG_LEVEL value") } // Should keep old variable if !strings.Contains(output, "OLD_VAR=old_value") { t.Error("expected to preserve OLD_VAR") } // Should add new variable if !strings.Contains(output, "NEW_VAR=new_default") { t.Error("expected to add NEW_VAR") } }) t.Run("create if not exist", func(t *testing.T) { envFile := filepath.Join(tempDir, "new.env") err := loader.UpdateEnvFile(envFile, true) if err != nil { t.Fatalf("UpdateEnvFile failed: %v", err) } if _, err := os.Stat(envFile); os.IsNotExist(err) { t.Error("expected file to be created") } }) t.Run("error if not exist and no create", func(t *testing.T) { envFile := filepath.Join(tempDir, "nonexistent.env") err := loader.UpdateEnvFile(envFile, false) if err == nil { t.Error("expected error for nonexistent file") } }) } func TestParseEnvFile(t *testing.T) { tempDir := t.TempDir() envFile := filepath.Join(tempDir, "test.env") content := `# Comment line VAR1=value1 VAR2=value2 # Another comment VAR3=value3 EMPTY_VAR= ` if err := os.WriteFile(envFile, []byte(content), 0644); err != nil { t.Fatalf("failed to create test file: %v", err) } lines, err := parseEnvFile(envFile) if err != nil { t.Fatalf("parseEnvFile failed: %v", err) } varCount := 0 for _, line := range lines { if line.IsVar { varCount++ } } if varCount != 4 { t.Errorf("expected 4 variables, got %d", varCount) } // Check specific variables found := false for _, line := range lines { if line.IsVar && line.Key == "VAR1" && line.Value == "value1" { found = true break } } if !found { t.Error("expected to find VAR1=value1") } } func TestParseEnvFile_InvalidFile(t *testing.T) { _, err := parseEnvFile("/nonexistent/file.env") if err == nil { t.Error("expected error for nonexistent file") } } func TestPrintEnvVars_NoEnvVars(t *testing.T) { loader := New() buf := &bytes.Buffer{} err := loader.PrintEnvVars(buf, false) if err == nil { t.Error("expected error when no env vars are loaded") } if !strings.Contains(err.Error(), "did you call Load()") { t.Errorf("expected helpful error message, got: %v", err) } } func TestPrintEnvVarsStdout(t *testing.T) { loader := New() loader.envVars = []EnvVar{ { Name: "TEST_VAR", Description: "Test variable", Default: "test", }, } // This test just ensures it doesn't panic // We can't easily capture stdout in a unit test without redirecting it err := loader.PrintEnvVarsStdout(false) if err != nil { t.Errorf("PrintEnvVarsStdout(false) failed: %v", err) } err = loader.PrintEnvVarsStdout(true) if err != nil { t.Errorf("PrintEnvVarsStdout(true) failed: %v", err) } } func TestPrintEnvVarsStdout_NoEnvVars(t *testing.T) { loader := New() err := loader.PrintEnvVarsStdout(false) if err == nil { t.Error("expected error when no env vars are loaded") } }