Files
golib/ezconf/output_test.go
2026-01-21 19:23:12 +11:00

406 lines
9.7 KiB
Go

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")
}
}
func TestPrintEnvVars_AfterParseEnvVars(t *testing.T) {
loader := New()
// Add some env vars manually to simulate ParseEnvVars
loader.envVars = []EnvVar{
{
Name: "LOG_LEVEL",
Description: "Log level for the application",
Required: false,
Default: "info",
CurrentValue: "",
},
{
Name: "DATABASE_URL",
Description: "Database connection string",
Required: true,
Default: "",
CurrentValue: "",
},
}
// Test that PrintEnvVars works after ParseEnvVars (without Load)
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, "DATABASE_URL") {
t.Error("output should contain DATABASE_URL")
}
if !strings.Contains(output, "(required)") {
t.Error("output should indicate required variables")
}
if !strings.Contains(output, "(default: info)") {
t.Error("output should contain default value")
}
}