package config import ( "os" "path/filepath" "strings" "testing" "time" ) func TestDefaults(t *testing.T) { cfg, err := Load(LoadOptions{}) if err != nil { t.Fatalf("Load() error = %v", err) } if cfg.WeatherAPI.Units != "us" { t.Fatalf("Units = %q, want us", cfg.WeatherAPI.Units) } if cfg.WeatherAPI.Timezone != "America/Chicago" { t.Fatalf("Timezone = %q, want America/Chicago", cfg.WeatherAPI.Timezone) } if cfg.WeatherAPI.Format != "json" { t.Fatalf("Format = %q, want json", cfg.WeatherAPI.Format) } if cfg.Location.ID != "home" || cfg.Location.Name != "Brentwood" || cfg.Location.Region != "St. Louis Metro" { t.Fatalf("Location = %#v, want home/Brentwood/St. Louis Metro", cfg.Location) } if cfg.MissingSource.Default != MissingSourceWarn { t.Fatalf("MissingSource.Default = %q, want warn", cfg.MissingSource.Default) } } func TestLoadExampleConfig(t *testing.T) { cfg, err := LoadFile(filepath.Join("..", "..", "examples", "config.yml")) if err != nil { t.Fatalf("LoadFile() error = %v", err) } if cfg.WeatherAPI.BaseURL != "https://weather.api.rakestrawhome.com/" { t.Fatalf("BaseURL = %q, want configured example URL", cfg.WeatherAPI.BaseURL) } if cfg.WeatherAPI.Timeout != 15*time.Second { t.Fatalf("Timeout = %s, want 15s", cfg.WeatherAPI.Timeout) } if cfg.MissingSource.Sources["alerts"] != MissingSourceNone { t.Fatalf("alerts policy = %q, want none", cfg.MissingSource.Sources["alerts"]) } if cfg.Location.ID != "home" || cfg.Location.Name != "Brentwood" || cfg.Location.Region != "St. Louis Metro" { t.Fatalf("Location = %#v, want example location", cfg.Location) } } func TestLoadMinimalExampleConfig(t *testing.T) { cfg, err := LoadFile(filepath.Join("..", "..", "examples", "minimal-config.yml")) if err != nil { t.Fatalf("LoadFile() error = %v", err) } if cfg.WeatherAPI.BaseURL != "https://weather.api.example.com/" { t.Fatalf("BaseURL = %q, want example URL", cfg.WeatherAPI.BaseURL) } if cfg.WeatherAPI.Units != "us" { t.Fatalf("Units = %q, want default us", cfg.WeatherAPI.Units) } if cfg.Scriptorium.Binary != "scriptorium" { t.Fatalf("Scriptorium.Binary = %q, want default scriptorium", cfg.Scriptorium.Binary) } if cfg.Workspace.Root != "workspace" { t.Fatalf("Workspace.Root = %q, want default workspace", cfg.Workspace.Root) } if cfg.Location.Name != "Brentwood" { t.Fatalf("Location.Name = %q, want default Brentwood", cfg.Location.Name) } } func TestExplicitMissingConfigReturnsError(t *testing.T) { _, err := LoadFile(filepath.Join(t.TempDir(), "missing.yml")) if err == nil { t.Fatal("LoadFile() error = nil, want missing file error") } if !strings.Contains(err.Error(), "read config") { t.Fatalf("error = %q, want read config context", err.Error()) } } func TestInvalidConfigProducesActionableError(t *testing.T) { dir := t.TempDir() path := filepath.Join(dir, "config.yml") if err := os.WriteFile(path, []byte("missing_source:\n default: explode\n"), 0o600); err != nil { t.Fatalf("write config fixture: %v", err) } _, err := LoadFile(path) if err == nil { t.Fatal("LoadFile() error = nil, want validation error") } if !strings.Contains(err.Error(), "missing_source.default") { t.Fatalf("error = %q, want field path", err.Error()) } } func TestLoadAppliesOverrides(t *testing.T) { cfg, err := Load(LoadOptions{Units: "metric", Timezone: "+09:30"}) if err != nil { t.Fatalf("Load() error = %v", err) } if cfg.WeatherAPI.Units != "metric" { t.Fatalf("Units = %q, want metric", cfg.WeatherAPI.Units) } if cfg.WeatherAPI.Timezone != "+09:30" { t.Fatalf("Timezone = %q, want +09:30", cfg.WeatherAPI.Timezone) } }