package config import ( "os" "path/filepath" "strings" "testing" ) func noSystemConfig(t *testing.T) { t.Helper() old := systemConfigPath systemConfigPath = filepath.Join(t.TempDir(), "absent.conf") t.Cleanup(func() { systemConfigPath = old }) } func writeConf(t *testing.T, body string) string { t.Helper() path := filepath.Join(t.TempDir(), "openstore.conf") if err := os.WriteFile(path, []byte(body), 0o644); err != nil { t.Fatalf("write conf: %v", err) } return path } func TestLoadMissingFileUsesDefaults(t *testing.T) { noSystemConfig(t) cfg, err := Load(filepath.Join(t.TempDir(), "nonexistent.conf")) if err != nil { t.Fatalf("load with no config file failed: %v", err) } if cfg.Serve.Addr != ":3000" { t.Errorf("addr: got %q, want \":3000\"", cfg.Serve.Addr) } if cfg.Instance.Name != "Openstore" { t.Errorf("name: got %q, want \"Openstore\"", cfg.Instance.Name) } } func TestLoadFullFileOverridesDefaults(t *testing.T) { noSystemConfig(t) cfg, err := Load("testdata/full.conf") if err != nil { t.Fatalf("load: %v", err) } tests := []struct { field string got string want string }{ {"addr", cfg.Serve.Addr, "127.0.0.1:8080"}, {"name", cfg.Instance.Name, "Full Store"}, {"contact-email", cfg.Instance.ContactEmail, "full@store.com"}, {"base-url", cfg.Instance.BaseURL, "https://full.store.com"}, } for _, tt := range tests { if tt.got != tt.want { t.Errorf("%s: got %q, want %q", tt.field, tt.got, tt.want) } } } func TestLoadPartialFileKeepsDefaults(t *testing.T) { noSystemConfig(t) cfg, err := Load("testdata/partial.conf") if err != nil { t.Fatalf("load: %v", err) } if cfg.Instance.Name != "Only Name Set" { t.Errorf("name: got %q, want \"Only Name Set\"", cfg.Instance.Name) } if cfg.Serve.Addr != ":3000" { t.Errorf("unset addr should keep default, got %q", cfg.Serve.Addr) } if cfg.Instance.BaseURL != "https://www.example.com" { t.Errorf("unset base-url should keep default, got %q", cfg.Instance.BaseURL) } } func TestLoadUserConfigWinsOverSystem(t *testing.T) { old := systemConfigPath systemConfigPath = "testdata/system.conf" t.Cleanup(func() { systemConfigPath = old }) cfg, err := Load("testdata/partial.conf") if err != nil { t.Fatalf("load: %v", err) } if cfg.Instance.Name != "Only Name Set" { t.Errorf("user config should win, got name %q", cfg.Instance.Name) } if cfg.Instance.ContactEmail != "system@store.com" { t.Errorf("key absent from user config should fall back to system, got %q", cfg.Instance.ContactEmail) } if cfg.Serve.Addr != "127.0.0.1:9999" { t.Errorf("section absent from user config should fall back to system, got %q", cfg.Serve.Addr) } } func TestLoadMalformedFile(t *testing.T) { noSystemConfig(t) if _, err := Load("testdata/malformed.conf"); err == nil { t.Error("malformed config file loaded without error") } } func TestLoadRejectsBadBaseURL(t *testing.T) { noSystemConfig(t) tests := map[string]string{ "not a url": "garbage", "no scheme": "example.com/path", "scheme only": "https://", "path only": "/listings", "bad url syntax": "://x", } for name, badURL := range tests { t.Run(name, func(t *testing.T) { path := writeConf(t, "[instance]\nbase-url = "+badURL+"\n") _, err := Load(path) if err == nil { t.Fatalf("bad base-url %q accepted", badURL) } if !strings.Contains(err.Error(), "base-url") { t.Errorf("error should mention base-url, got: %v", err) } }) } } func TestLoadEmptyValueKeepsDefault(t *testing.T) { noSystemConfig(t) path := writeConf(t, "[instance]\nname = \nbase-url = \n") cfg, err := Load(path) if err != nil { t.Fatalf("load: %v", err) } if cfg.Instance.Name != "Openstore" { t.Errorf("empty value should leave default, got name %q", cfg.Instance.Name) } if cfg.Instance.BaseURL != "https://www.example.com" { t.Errorf("empty value should leave default, got base-url %q", cfg.Instance.BaseURL) } } func TestLoadAcceptsGoodBaseURL(t *testing.T) { noSystemConfig(t) urls := []string{ "https://example.com", "http://example.com", "https://example.com/subpath", "https://sub.example.com:8443", } for _, u := range urls { t.Run(u, func(t *testing.T) { path := writeConf(t, "[instance]\nbase-url = "+u+"\n") cfg, err := Load(path) if err != nil { t.Fatalf("good base-url %q rejected: %v", u, err) } if cfg.Instance.BaseURL != u { t.Errorf("base-url: got %q, want %q", cfg.Instance.BaseURL, u) } }) } } func TestDefaultPath(t *testing.T) { t.Setenv("XDG_CONFIG_HOME", "/tmp/xdg-test") path, err := DefaultPath() if err != nil { t.Fatalf("DefaultPath: %v", err) } want := filepath.Join("/tmp/xdg-test", "openstore", "openstore.conf") if path != want { t.Errorf("got %q, want %q", path, want) } } func TestDefaultPathWithoutConfigDir(t *testing.T) { t.Setenv("XDG_CONFIG_HOME", "") t.Setenv("HOME", "") if _, err := DefaultPath(); err == nil { t.Error("DefaultPath returned no error with no config dir available") } }