diff --git a/internal/config/config.go b/internal/config/config.go new file mode 100644 index 0000000..150110f --- /dev/null +++ b/internal/config/config.go @@ -0,0 +1,40 @@ +package config + +import ( + "os" + "time" +) + +// Config holds runtime configuration for the server, sourced from the +// environment. +type Config struct { + Addr string + ReadTimeout time.Duration + WriteTimeout time.Duration +} + +// Load reads configuration from environment variables, falling back to +// sensible defaults when a variable is unset. +func Load() Config { + return Config{ + Addr: getString("ADDR", ":8080"), + ReadTimeout: getDuration("READ_TIMEOUT", 5*time.Second), + WriteTimeout: getDuration("WRITE_TIMEOUT", 10*time.Second), + } +} + +func getString(key, fallback string) string { + if v := os.Getenv(key); v != "" { + return v + } + return fallback +} + +func getDuration(key string, fallback time.Duration) time.Duration { + if v := os.Getenv(key); v != "" { + if d, err := time.ParseDuration(v); err == nil { + return d + } + } + return fallback +} diff --git a/internal/config/config_test.go b/internal/config/config_test.go new file mode 100644 index 0000000..3ea96aa --- /dev/null +++ b/internal/config/config_test.go @@ -0,0 +1,49 @@ +package config + +import ( + "testing" + "time" +) + +func TestLoadDefaults(t *testing.T) { + t.Setenv("ADDR", "") + t.Setenv("READ_TIMEOUT", "") + t.Setenv("WRITE_TIMEOUT", "") + + cfg := Load() + if cfg.Addr != ":8080" { + t.Errorf("Addr = %q, want :8080", cfg.Addr) + } + if cfg.ReadTimeout != 5*time.Second { + t.Errorf("ReadTimeout = %v, want 5s", cfg.ReadTimeout) + } + if cfg.WriteTimeout != 10*time.Second { + t.Errorf("WriteTimeout = %v, want 10s", cfg.WriteTimeout) + } +} + +func TestLoadOverrides(t *testing.T) { + t.Setenv("ADDR", ":9090") + t.Setenv("READ_TIMEOUT", "2s") + t.Setenv("WRITE_TIMEOUT", "20s") + + cfg := Load() + if cfg.Addr != ":9090" { + t.Errorf("Addr = %q, want :9090", cfg.Addr) + } + if cfg.ReadTimeout != 2*time.Second { + t.Errorf("ReadTimeout = %v, want 2s", cfg.ReadTimeout) + } + if cfg.WriteTimeout != 20*time.Second { + t.Errorf("WriteTimeout = %v, want 20s", cfg.WriteTimeout) + } +} + +func TestLoadInvalidDurationFallsBack(t *testing.T) { + t.Setenv("READ_TIMEOUT", "not-a-duration") + + cfg := Load() + if cfg.ReadTimeout != 5*time.Second { + t.Errorf("ReadTimeout = %v, want fallback 5s", cfg.ReadTimeout) + } +}