mirror of
https://tangled.org/evan.jarrett.net/at-container-registry
synced 2026-08-31 23:36:57 +00:00
358 lines
8.5 KiB
Go
358 lines
8.5 KiB
Go
//go:build billing
|
|
|
|
package billing
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
func TestParseBillingConfig_Disabled(t *testing.T) {
|
|
yaml := []byte(`
|
|
billing:
|
|
enabled: false
|
|
`)
|
|
cfg, err := parseBillingConfig(yaml)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if cfg != nil {
|
|
t.Error("expected nil config when billing disabled")
|
|
}
|
|
}
|
|
|
|
func TestParseBillingConfig_NoBillingSection(t *testing.T) {
|
|
yaml := []byte(`
|
|
quota:
|
|
tiers:
|
|
deckhand:
|
|
quota: 5GB
|
|
`)
|
|
cfg, err := parseBillingConfig(yaml)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if cfg != nil {
|
|
t.Error("expected nil config when no billing section")
|
|
}
|
|
}
|
|
|
|
func TestParseBillingConfig_Enabled(t *testing.T) {
|
|
yaml := []byte(`
|
|
billing:
|
|
enabled: true
|
|
currency: usd
|
|
success_url: "{hold_url}/billing/success"
|
|
cancel_url: "{hold_url}/billing/cancel"
|
|
plankowner_crew_tier: bosun
|
|
tiers:
|
|
deckhand:
|
|
description: Starter tier
|
|
bosun:
|
|
description: Standard tier
|
|
stripe_price_monthly: price_bosun_monthly
|
|
stripe_price_yearly: price_bosun_yearly
|
|
`)
|
|
cfg, err := parseBillingConfig(yaml)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if cfg == nil {
|
|
t.Fatal("expected non-nil config")
|
|
}
|
|
|
|
if !cfg.Enabled {
|
|
t.Error("expected Enabled=true")
|
|
}
|
|
if cfg.Currency != "usd" {
|
|
t.Errorf("expected currency 'usd', got %q", cfg.Currency)
|
|
}
|
|
if cfg.PlankOwnerCrewTier != "bosun" {
|
|
t.Errorf("expected plankowner_crew_tier 'bosun', got %q", cfg.PlankOwnerCrewTier)
|
|
}
|
|
if cfg.SuccessURL != "{hold_url}/billing/success" {
|
|
t.Errorf("unexpected success_url: %q", cfg.SuccessURL)
|
|
}
|
|
|
|
// Check tier pricing
|
|
bosun := cfg.GetTierPricing("bosun")
|
|
if bosun == nil {
|
|
t.Fatal("expected bosun tier pricing")
|
|
}
|
|
if bosun.StripePriceMonthly != "price_bosun_monthly" {
|
|
t.Errorf("expected bosun monthly price 'price_bosun_monthly', got %q", bosun.StripePriceMonthly)
|
|
}
|
|
if bosun.StripePriceYearly != "price_bosun_yearly" {
|
|
t.Errorf("expected bosun yearly price 'price_bosun_yearly', got %q", bosun.StripePriceYearly)
|
|
}
|
|
if bosun.Description != "Standard tier" {
|
|
t.Errorf("expected bosun description 'Standard tier', got %q", bosun.Description)
|
|
}
|
|
|
|
// Deckhand has no prices
|
|
deckhand := cfg.GetTierPricing("deckhand")
|
|
if deckhand == nil {
|
|
t.Fatal("expected deckhand tier pricing entry")
|
|
}
|
|
if deckhand.StripePriceMonthly != "" {
|
|
t.Error("expected no monthly price for deckhand")
|
|
}
|
|
}
|
|
|
|
func TestParseBillingConfig_EnabledButNoPrices(t *testing.T) {
|
|
yaml := []byte(`
|
|
billing:
|
|
enabled: true
|
|
currency: usd
|
|
`)
|
|
cfg, err := parseBillingConfig(yaml)
|
|
if err == nil {
|
|
t.Error("expected error when billing enabled but no prices configured")
|
|
}
|
|
if cfg != nil {
|
|
t.Error("expected nil config on error")
|
|
}
|
|
}
|
|
|
|
func TestGetTierByPriceID(t *testing.T) {
|
|
cfg := &BillingConfig{
|
|
Tiers: map[string]BillingTierConfig{
|
|
"deckhand": {},
|
|
"bosun": {StripePriceMonthly: "price_m", StripePriceYearly: "price_y"},
|
|
},
|
|
}
|
|
|
|
if got := cfg.GetTierByPriceID("price_m"); got != "bosun" {
|
|
t.Errorf("expected 'bosun' for monthly price, got %q", got)
|
|
}
|
|
if got := cfg.GetTierByPriceID("price_y"); got != "bosun" {
|
|
t.Errorf("expected 'bosun' for yearly price, got %q", got)
|
|
}
|
|
if got := cfg.GetTierByPriceID("price_unknown"); got != "" {
|
|
t.Errorf("expected empty for unknown price, got %q", got)
|
|
}
|
|
if got := cfg.GetTierByPriceID(""); got != "" {
|
|
t.Errorf("expected empty for empty price, got %q", got)
|
|
}
|
|
|
|
// nil receiver
|
|
var nilCfg *BillingConfig
|
|
if got := nilCfg.GetTierByPriceID("price_m"); got != "" {
|
|
t.Errorf("expected empty from nil config, got %q", got)
|
|
}
|
|
}
|
|
|
|
func TestGetTierPricing_NilConfig(t *testing.T) {
|
|
var cfg *BillingConfig
|
|
if cfg.GetTierPricing("anything") != nil {
|
|
t.Error("expected nil from nil config")
|
|
}
|
|
}
|
|
|
|
func TestLoadBillingConfig_MissingFile(t *testing.T) {
|
|
cfg, err := LoadBillingConfig("/nonexistent/config.yaml")
|
|
if err != nil {
|
|
t.Fatalf("expected no error for missing file, got: %v", err)
|
|
}
|
|
if cfg != nil {
|
|
t.Error("expected nil config for missing file")
|
|
}
|
|
}
|
|
|
|
func TestLoadBillingConfig_EmptyPath(t *testing.T) {
|
|
cfg, err := LoadBillingConfig("")
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if cfg != nil {
|
|
t.Error("expected nil config for empty path")
|
|
}
|
|
}
|
|
|
|
func TestLoadBillingConfig_FromFile(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "config.yaml")
|
|
|
|
content := `
|
|
billing:
|
|
enabled: true
|
|
currency: usd
|
|
tiers:
|
|
bosun:
|
|
stripe_price_monthly: price_test
|
|
`
|
|
if err := os.WriteFile(path, []byte(content), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
cfg, err := LoadBillingConfig(path)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if cfg == nil {
|
|
t.Fatal("expected non-nil config")
|
|
}
|
|
if cfg.GetTierByPriceID("price_test") != "bosun" {
|
|
t.Error("expected bosun tier for price_test")
|
|
}
|
|
}
|
|
|
|
func TestParseBillingConfig_TopLevelTiers(t *testing.T) {
|
|
yaml := []byte(`
|
|
billing:
|
|
enabled: true
|
|
currency: usd
|
|
tiers:
|
|
deckhand:
|
|
description: "Starter tier"
|
|
bosun:
|
|
description: "Standard tier"
|
|
stripe_price_monthly: price_bosun_m
|
|
stripe_price_yearly: price_bosun_y
|
|
quartermaster:
|
|
description: "Pro tier"
|
|
stripe_price_monthly: price_qm_m
|
|
`)
|
|
cfg, err := parseBillingConfig(yaml)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if cfg == nil {
|
|
t.Fatal("expected non-nil config")
|
|
}
|
|
if len(cfg.Tiers) != 3 {
|
|
t.Errorf("expected 3 tiers, got %d", len(cfg.Tiers))
|
|
}
|
|
|
|
bosun := cfg.GetTierPricing("bosun")
|
|
if bosun == nil {
|
|
t.Fatal("expected bosun tier")
|
|
}
|
|
if bosun.Description != "Standard tier" {
|
|
t.Errorf("expected description 'Standard tier', got %q", bosun.Description)
|
|
}
|
|
if bosun.StripePriceMonthly != "price_bosun_m" {
|
|
t.Errorf("expected monthly price 'price_bosun_m', got %q", bosun.StripePriceMonthly)
|
|
}
|
|
if bosun.StripePriceYearly != "price_bosun_y" {
|
|
t.Errorf("expected yearly price 'price_bosun_y', got %q", bosun.StripePriceYearly)
|
|
}
|
|
|
|
qm := cfg.GetTierPricing("quartermaster")
|
|
if qm == nil {
|
|
t.Fatal("expected quartermaster tier")
|
|
}
|
|
if qm.StripePriceMonthly != "price_qm_m" {
|
|
t.Errorf("expected monthly price 'price_qm_m', got %q", qm.StripePriceMonthly)
|
|
}
|
|
|
|
deckhand := cfg.GetTierPricing("deckhand")
|
|
if deckhand == nil {
|
|
t.Fatal("expected deckhand tier")
|
|
}
|
|
if deckhand.Description != "Starter tier" {
|
|
t.Errorf("expected description 'Starter tier', got %q", deckhand.Description)
|
|
}
|
|
if deckhand.StripePriceMonthly != "" {
|
|
t.Error("expected no monthly price for deckhand")
|
|
}
|
|
}
|
|
|
|
func TestParseBillingConfig_PlankOwnerCrewTier(t *testing.T) {
|
|
yaml := []byte(`
|
|
billing:
|
|
enabled: true
|
|
currency: usd
|
|
plankowner_crew_tier: bosun
|
|
tiers:
|
|
bosun:
|
|
stripe_price_monthly: price_bosun_m
|
|
`)
|
|
cfg, err := parseBillingConfig(yaml)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if cfg == nil {
|
|
t.Fatal("expected non-nil config")
|
|
}
|
|
if cfg.PlankOwnerCrewTier != "bosun" {
|
|
t.Errorf("expected plankowner_crew_tier 'bosun', got %q", cfg.PlankOwnerCrewTier)
|
|
}
|
|
}
|
|
|
|
func TestParseBillingConfig_IgnoresQuotaSection(t *testing.T) {
|
|
// Billing parser should work even if quota section is missing entirely
|
|
yaml := []byte(`
|
|
billing:
|
|
enabled: true
|
|
currency: usd
|
|
tiers:
|
|
bosun:
|
|
stripe_price_monthly: price_bosun_m
|
|
`)
|
|
cfg, err := parseBillingConfig(yaml)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if cfg == nil {
|
|
t.Fatal("expected non-nil config")
|
|
}
|
|
|
|
// Also works with quota present but unrelated
|
|
yaml2 := []byte(`
|
|
quota:
|
|
tiers:
|
|
swabbie:
|
|
quota: 1GB
|
|
billing:
|
|
enabled: true
|
|
currency: usd
|
|
tiers:
|
|
bosun:
|
|
stripe_price_monthly: price_bosun_m
|
|
`)
|
|
cfg2, err := parseBillingConfig(yaml2)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if cfg2 == nil {
|
|
t.Fatal("expected non-nil config")
|
|
}
|
|
// Billing should only see its own tiers, not quota tiers
|
|
if cfg2.GetTierPricing("swabbie") != nil {
|
|
t.Error("billing should not contain quota-only tiers")
|
|
}
|
|
}
|
|
|
|
func TestParseBillingConfig_EmptyTiers(t *testing.T) {
|
|
// Billing enabled with explicit empty tiers
|
|
yaml := []byte(`
|
|
billing:
|
|
enabled: true
|
|
currency: usd
|
|
tiers: {}
|
|
`)
|
|
cfg, err := parseBillingConfig(yaml)
|
|
if err == nil {
|
|
t.Error("expected error when billing enabled with empty tiers")
|
|
}
|
|
if cfg != nil {
|
|
t.Error("expected nil config on error")
|
|
}
|
|
|
|
// Billing enabled with tiers omitted entirely
|
|
yaml2 := []byte(`
|
|
billing:
|
|
enabled: true
|
|
currency: usd
|
|
`)
|
|
cfg2, err := parseBillingConfig(yaml2)
|
|
if err == nil {
|
|
t.Error("expected error when billing enabled with no tiers")
|
|
}
|
|
if cfg2 != nil {
|
|
t.Error("expected nil config on error")
|
|
}
|
|
}
|