package config import ( "testing" "github.com/spf13/viper" ) // TestDeploymentNameDefaultsWhenUnset covers the fallback DeploymentName // gives every caller (including render tests that never run cmd/start.go's // viper.SetDefault): unset resolves to the generic product name, not an // empty string. func TestDeploymentNameDefaultsWhenUnset(t *testing.T) { viper.Reset() if got := DeploymentName(); got != DefaultDeploymentName { t.Errorf("DeploymentName() with nothing configured = %q, want %q", got, DefaultDeploymentName) } } // TestDeploymentNameTrimsAndPrefersConfigured covers the configured-value // path (deployment-identity scenario: "Configured name appears identically // on both surfaces") and that surrounding whitespace from an operator's env // value doesn't leak into the rendered name. func TestDeploymentNameTrimsAndPrefersConfigured(t *testing.T) { viper.Reset() viper.Set("deployment-name", " Solidarity Web Collective ") if got, want := DeploymentName(), "Solidarity Web Collective"; got != want { t.Errorf("DeploymentName() = %q, want %q", got, want) } } // TestDeploymentNameWhitespaceOnlyFallsBack: an explicitly blank override // still resolves to a safe, non-blank name at read time — ValidateStart is // the boot-time gate that refuses this configuration outright (see // TestValidateStart_Errors's "blank deployment-name" case); this is defense // in depth for any caller that runs before or without validation. func TestDeploymentNameWhitespaceOnlyFallsBack(t *testing.T) { viper.Reset() viper.Set("deployment-name", " ") if got := DeploymentName(); got != DefaultDeploymentName { t.Errorf("DeploymentName() with whitespace-only override = %q, want %q", got, DefaultDeploymentName) } } // requiredGroupSpecs mirrors a two-key required-together group the way // Stripe/FedWiki/Discourse declare theirs (RequiredGroup ties the pair; // ValidateStart's validateRequiredGroups rejects a half-set group at boot, // so by the time RequiredKeysUnresolved runs the group is always all-set or // all-unset — but the function reports every unresolved member, not just // one, so a caller can name what's missing even from an invalid in-flight // state). var requiredGroupSpecs = []ConfigKey{ {Name: "acme-url", RequiredGroup: "Acme"}, {Name: "acme-token", Secret: true, RequiredGroup: "Acme"}, {Name: "acme-region", Default: "us", Usage: "not required"}, } func TestRequiredKeysUnresolved(t *testing.T) { t.Run("nothing set reports every required key", func(t *testing.T) { viper.Reset() got := RequiredKeysUnresolved(requiredGroupSpecs) want := []string{"acme-url", "acme-token"} if len(got) != len(want) || got[0] != want[0] || got[1] != want[1] { t.Errorf("RequiredKeysUnresolved = %v, want %v", got, want) } }) t.Run("fully configured reports nothing", func(t *testing.T) { viper.Reset() viper.Set("acme-url", "https://acme.example.com") viper.Set("acme-token", "sekret") if got := RequiredKeysUnresolved(requiredGroupSpecs); len(got) != 0 { t.Errorf("RequiredKeysUnresolved = %v, want none missing", got) } }) t.Run("a key with no RequiredGroup is never reported", func(t *testing.T) { viper.Reset() if got := RequiredKeysUnresolved([]ConfigKey{{Name: "acme-region", Default: "us"}}); len(got) != 0 { t.Errorf("RequiredKeysUnresolved = %v, want none (no RequiredGroup)", got) } }) t.Run("whitespace-only value counts as unresolved", func(t *testing.T) { viper.Reset() viper.Set("acme-url", " ") viper.Set("acme-token", "sekret") got := RequiredKeysUnresolved(requiredGroupSpecs) if len(got) != 1 || got[0] != "acme-url" { t.Errorf("RequiredKeysUnresolved = %v, want [acme-url]", got) } }) }