Files
gochat/backend/internal/service/enterprise_billing_worker_test.go
T

131 lines
5.5 KiB
Go

package service
import (
"context"
"fmt"
"io"
"net/http"
"net/http/httptest"
"net/url"
"sync"
"testing"
"github.com/gochat/gochat/internal/model"
"github.com/gochat/gochat/internal/repository"
"github.com/gochat/gochat/internal/worker"
"github.com/stretchr/testify/require"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
func TestEnterpriseBillingWorkerCreatesBRLCustomerAndSubscription(t *testing.T) {
db, err := gorm.Open(sqlite.Open(fmt.Sprintf("file:%s?mode=memory&cache=private", t.Name())), &gorm.Config{})
require.NoError(t, err)
require.NoError(t, db.AutoMigrate(&model.Account{}, &model.User{}, &model.AccountUser{}, &model.InstallationConfig{}, &model.BackgroundJob{}))
t.Cleanup(func() {
sqlDB, _ := db.DB()
_ = sqlDB.Close()
})
account := &model.Account{Name: "Acme Brasil", Locale: "pt_BR", Active: true, FeatureFlags: `{"sla":true}`}
admin := &model.User{Name: "Admin", Email: "admin@example.com", Password: "hashed", Active: true}
require.NoError(t, db.Create(account).Error)
require.NoError(t, db.Create(admin).Error)
require.NoError(t, db.Create(&model.AccountUser{AccountID: account.ID, UserID: admin.ID, Role: "administrator"}).Error)
require.NoError(t, db.Create(&model.InstallationConfig{Name: "ENABLE_MULTI_CURRENCY_BILLING", Value: "true"}).Error)
require.NoError(t, db.Create(&model.InstallationConfig{
Name: "CHATWOOT_CLOUD_PLANS",
Value: `[{"name":"Hacker","product_id":"prod_default","price_ids":{"usd":["price_usd"],"brl":["price_brl"]},"default_quantity":2}]`,
}).Error)
var mu sync.Mutex
requests := map[string]url.Values{}
stripe := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
body, readErr := io.ReadAll(r.Body)
require.NoError(t, readErr)
values, parseErr := url.ParseQuery(string(body))
require.NoError(t, parseErr)
mu.Lock()
requests[r.URL.Path] = values
mu.Unlock()
w.Header().Set("Content-Type", "application/json")
switch r.URL.Path {
case "/v1/customers":
_, _ = w.Write([]byte(`{"id":"cus_test"}`))
case "/v1/subscriptions":
_, _ = w.Write([]byte(`{"status":"active","items":{"data":[{"quantity":2,"current_period_end":1780000000,"price":{"id":"price_brl","product":"prod_default","currency":"brl"}}]}}`))
default:
http.NotFound(w, r)
}
}))
t.Cleanup(stripe.Close)
t.Setenv("STRIPE_SECRET_KEY", "sk_test")
t.Setenv("STRIPE_API_BASE", stripe.URL)
svc := NewAccountService(repository.NewAccountRepo(db))
wp := worker.NewWorkerPool(db)
svc.SetWorkerPool(wp)
require.NoError(t, svc.SelectBillingCurrency(context.Background(), admin.ID, account.ID, "brl"))
var job model.BackgroundJob
require.NoError(t, db.Where("job_type = ?", TaskTypeEnterpriseCreateStripeCustomer).First(&job).Error)
require.Equal(t, model.BackgroundJobStatusQueued, job.Status)
require.Equal(t, "default", job.Queue)
require.Equal(t, 3, job.MaxAttempts)
require.Equal(t, fmt.Sprintf("stripe-customer:%d", account.ID), job.IdempotencyKey)
processed, err := wp.ProcessOne(context.Background())
require.NoError(t, err)
require.True(t, processed)
require.NoError(t, db.First(account, account.ID).Error)
attrs := account.CustomAttributesMap()
require.NotContains(t, attrs, "is_creating_customer")
require.Equal(t, "cus_test", attrs["stripe_customer_id"])
require.Equal(t, "price_brl", attrs["stripe_price_id"])
require.Equal(t, "prod_default", attrs["stripe_product_id"])
require.Equal(t, "Hacker", attrs["plan_name"])
require.Equal(t, float64(2), attrs["subscribed_quantity"])
require.Equal(t, "active", attrs["subscription_status"])
require.Equal(t, "2026-05-28T20:26:40Z", attrs["subscription_ends_on"])
require.Equal(t, "brl", attrs["billing_currency"])
mu.Lock()
customerForm := requests["/v1/customers"]
subscriptionForm := requests["/v1/subscriptions"]
mu.Unlock()
require.Equal(t, "Acme Brasil", customerForm.Get("name"))
require.Equal(t, "admin@example.com", customerForm.Get("email"))
require.Equal(t, "BR", customerForm.Get("address[country]"))
require.Equal(t, "pt-BR", customerForm.Get("preferred_locales[0]"))
require.Equal(t, "cus_test", subscriptionForm.Get("customer"))
require.Equal(t, "price_brl", subscriptionForm.Get("items[0][price]"))
require.Equal(t, "2", subscriptionForm.Get("items[0][quantity]"))
}
func TestEnterpriseBillingWorkerAlwaysClearsCreationFlagOnFailure(t *testing.T) {
db, err := gorm.Open(sqlite.Open(fmt.Sprintf("file:%s?mode=memory&cache=private", t.Name())), &gorm.Config{})
require.NoError(t, err)
require.NoError(t, db.AutoMigrate(&model.Account{}, &model.User{}, &model.AccountUser{}, &model.InstallationConfig{}, &model.BackgroundJob{}))
t.Cleanup(func() {
sqlDB, _ := db.DB()
_ = sqlDB.Close()
})
account := &model.Account{Name: "Acme", Locale: "en", Active: true}
admin := &model.User{Name: "Admin", Email: "admin@example.com", Password: "hashed", Active: true}
require.NoError(t, db.Create(account).Error)
require.NoError(t, db.Create(admin).Error)
require.NoError(t, db.Create(&model.AccountUser{AccountID: account.ID, UserID: admin.ID, Role: "administrator"}).Error)
svc := NewAccountService(repository.NewAccountRepo(db))
wp := worker.NewWorkerPool(db)
svc.SetWorkerPool(wp)
require.NoError(t, svc.EnsureEnterpriseAccountCustomerCreationFlag(context.Background(), account.ID, admin.ID))
processed, err := wp.ProcessOne(context.Background())
require.ErrorContains(t, err, "STRIPE_SECRET_KEY is not configured")
require.True(t, processed)
require.NoError(t, db.First(account, account.ID).Error)
require.NotContains(t, account.CustomAttributesMap(), "is_creating_customer")
}