diff --git a/main.go b/main.go index 850ccb3..501f338 100644 --- a/main.go +++ b/main.go @@ -45,7 +45,7 @@ func main() { sqlDB, _ := db.DB() defer sqlDB.Close() - secretKey := []byte(pkg.GetEnv("JWT_SECRET", "secret-key")) + secretKey := []byte(pkg.GetEnv("JWT_SECRET", "")) // Dependency wiring taskRepo := gorm_task.NewTaskRepository(db) taskSvc := task_service.NewTaskService(taskRepo) diff --git a/pkg/env.go b/pkg/env.go index 7f8376a..3df34d7 100644 --- a/pkg/env.go +++ b/pkg/env.go @@ -1,10 +1,26 @@ package pkg -import "os" +import ( + "fmt" + "log" + "os" + "taskflow/internal/common" +) func GetEnv(key, fallback string) string { if value := os.Getenv(key); value != "" { return value } + + // If fallback is empty, treat it as a required variable. + if fallback == "" { + // Fatal → exit with a clear error message + log.Fatal( + common.ErrorResponse{ + Message: fmt.Sprintf("missing required environment variable: %s", key), + }.Error(), + ) + } + return fallback } diff --git a/pkg/env_test.go b/pkg/env_test.go new file mode 100644 index 0000000..389afbb --- /dev/null +++ b/pkg/env_test.go @@ -0,0 +1,107 @@ +package pkg + +import ( + "os" + "os/exec" + "strings" + "testing" +) + +func TestGetEnv(t *testing.T) { + tests := []struct { + name string + key string + fallback string + envValue string + setEnv bool + expected string + }{ + { + name: "returns env value when set", + key: "TEST_VAR", + fallback: "default", + envValue: "from_env", + setEnv: true, + expected: "from_env", + }, + { + name: "returns env value with special characters", + key: "SPECIAL_VAR", + fallback: "default", + envValue: "value:with:colons", + setEnv: true, + expected: "value:with:colons", + }, + { + name: "returns fallback when env not set", + key: "UNSET_VAR", + fallback: "default_value", + setEnv: false, + expected: "default_value", + }, + { + name: "returns empty string from env when set to empty", + key: "EMPTY_VAR", + fallback: "default", + envValue: "", + setEnv: true, + expected: "default", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + os.Unsetenv(tt.key) + + if tt.setEnv { + os.Setenv(tt.key, tt.envValue) + defer os.Unsetenv(tt.key) + } + + result := GetEnv(tt.key, tt.fallback) + if result != tt.expected { + t.Errorf("GetEnv() = %q, want %q", result, tt.expected) + } + }) + } +} + +func TestGetEnv_MissingRequiredVariable(t *testing.T) { + if os.Getenv("TEST_SUBPROCESS") == "1" { + GetEnv("MISSING_REQUIRED_VAR", "") + return + } + + cmd := exec.Command(os.Args[0], "-test.run=TestGetEnv_MissingRequiredVariable") + cmd.Env = append(os.Environ(), "TEST_SUBPROCESS=1") + output, err := cmd.CombinedOutput() + + if err == nil { + t.Fatal("expected process to exit with error, but it succeeded") + } + + if exitErr, ok := err.(*exec.ExitError); !ok || exitErr.Success() { + t.Errorf("expected non-zero exit status, got: %v", err) + } + + outputStr := string(output) + if !strings.Contains(outputStr, "missing required environment variable") { + t.Errorf("expected error message to contain 'missing required environment variable', got: %s", outputStr) + } + if !strings.Contains(outputStr, "MISSING_REQUIRED_VAR") { + t.Errorf("expected error message to contain 'MISSING_REQUIRED_VAR', got: %s", outputStr) + } +} + +func TestGetEnv_EmptyFallbackWithSetEnv(t *testing.T) { + key := "SET_VAR_EMPTY_FALLBACK" + value := "actual_value" + + os.Setenv(key, value) + defer os.Unsetenv(key) + + result := GetEnv(key, "") + if result != value { + t.Errorf("GetEnv() = %q, want %q", result, value) + } +}