rentease/internal/service/booking/stripe_sync_test.go
Ruidy afc61e02f1
Some checks failed
CI / checks (push) Has been cancelled
refactor: improve booking error handling and responses
Refactor booking retrieval to return errors instead of nil values,
enabling more robust error handling throughout the booking, payment,
and PDF endpoints. Add custom HTTP error page rendering for not found
and internal server errors. Update interfaces and tests to match new
method signatures. This improves user feedback and code maintainability.
2025-11-17 19:26:45 +01:00

169 lines
5.1 KiB
Go

package booking
import (
"context"
"errors"
"log/slog"
"testing"
"time"
"gorm.io/gorm"
"github.com/rjNemo/rentease/internal/config"
stripeclient "github.com/rjNemo/rentease/internal/driver/stripe"
)
type fakeStripeClient struct {
payments []stripeclient.Payment
err error
}
func (f *fakeStripeClient) ListPayments(ctx context.Context, params stripeclient.ListPaymentsParams) ([]stripeclient.Payment, error) {
return f.payments, f.err
}
func (f *fakeStripeClient) CreatePaymentLink(ctx context.Context, params stripeclient.CreatePaymentLinkParams) (string, error) {
return "", nil
}
type mockStore struct {
upserts []*Payment
err error
byStripeID map[string]*Payment
}
func (m *mockStore) record(p *Payment) (*Payment, error) {
cp := *p
m.upserts = append(m.upserts, &cp)
if cp.StripePaymentID != nil {
if m.byStripeID == nil {
m.byStripeID = make(map[string]*Payment)
}
clone := cp
m.byStripeID[*cp.StripePaymentID] = &clone
}
if m.err != nil {
return nil, m.err
}
return &cp, nil
}
func (m *mockStore) All() []*Line { return nil }
func (m *mockStore) Search(string) []*Line { return nil }
func (m *mockStore) List(time.Time, time.Time) ([]*Line, error) { return nil, nil }
func (m *mockStore) CardTotal(time.Time, time.Time) (float64, error) { return 0, nil }
func (m *mockStore) Get(int) (*Booking, error) { return nil, nil }
func (m *mockStore) Create(*Booking) error { return nil }
func (m *mockStore) Update(*Booking) error { return nil }
func (m *mockStore) Cancel(int) error { return nil }
func (m *mockStore) CreateItem(*Item) error { return nil }
func (m *mockStore) PayItem(int) (*Item, error) { return nil, nil }
func (m *mockStore) GetItem(int) (*Item, error) { return nil, nil }
func (m *mockStore) UpdateItem(int, string, string, string, int, float64) (*Item, error) {
return nil, nil
}
func (m *mockStore) CreatePayment(*Payment) (*Payment, error) { return nil, nil }
func (m *mockStore) GetPayment(int) (*Payment, error) { return nil, nil }
func (m *mockStore) UpdatePayment(int, float64, string) (*Payment, error) { return nil, nil }
func (m *mockStore) UpsertStripePayment(p *Payment) (*Payment, error) { return m.record(p) }
func (m *mockStore) FindStripePayment(id string) (*Payment, error) {
if m.byStripeID == nil {
return nil, gorm.ErrRecordNotFound
}
if p, ok := m.byStripeID[id]; ok {
clone := *p
return &clone, nil
}
return nil, gorm.ErrRecordNotFound
}
func TestSyncStripePayments(t *testing.T) {
bookingID := uint(42)
stripePayments := []stripeclient.Payment{
{
ID: "pi_123",
Amount: 120.50,
PaymentMethod: "card",
Status: "succeeded",
BookingID: &bookingID,
},
}
store := &mockStore{}
stripe := &fakeStripeClient{payments: stripePayments}
logger := slog.New(slog.DiscardHandler)
svc, err := NewService(logger, store, nil, nil, stripe)
if err != nil {
t.Fatalf("NewService returned error: %v", err)
}
if err := svc.SyncStripePayments(context.Background(), time.Now().Add(-time.Hour), time.Now()); err != nil {
t.Fatalf("SyncStripePayments returned error: %v", err)
}
if len(store.upserts) != 1 {
t.Fatalf("expected 1 upsert, got %d", len(store.upserts))
}
upsert := store.upserts[0]
if upsert.Amount != 120.50 {
t.Errorf("unexpected amount: %v", upsert.Amount)
}
if upsert.PaymentMethod != config.PaymentMethod("Card") {
t.Errorf("unexpected payment method: %v", upsert.PaymentMethod)
}
if upsert.StripePaymentID == nil || *upsert.StripePaymentID != "pi_123" {
t.Errorf("stripe payment id not set correctly: %v", upsert.StripePaymentID)
}
}
func TestSyncStripePaymentsSkipsMissingBooking(t *testing.T) {
stripePayments := []stripeclient.Payment{
{ID: "pi_123", Amount: 10},
}
store := &mockStore{}
stripe := &fakeStripeClient{payments: stripePayments}
logger := slog.New(slog.DiscardHandler)
svc, err := NewService(logger, store, nil, nil, stripe)
if err != nil {
t.Fatalf("NewService returned error: %v", err)
}
if err := svc.SyncStripePayments(context.Background(), time.Now().Add(-time.Hour), time.Now()); err != nil {
t.Fatalf("SyncStripePayments returned error: %v", err)
}
if len(store.upserts) != 0 {
t.Fatalf("expected 0 upserts, got %d", len(store.upserts))
}
}
func TestSyncStripePaymentsReturnsAggregatedError(t *testing.T) {
bookingID := uint(7)
stripePayments := []stripeclient.Payment{
{
ID: "pi_err",
Amount: 50,
PaymentMethod: "card",
Status: "succeeded",
BookingID: &bookingID,
},
}
store := &mockStore{err: errors.New("db failure")}
stripe := &fakeStripeClient{payments: stripePayments}
logger := slog.New(slog.DiscardHandler)
svc, err := NewService(logger, store, nil, nil, stripe)
if err != nil {
t.Fatalf("NewService returned error: %v", err)
}
err = svc.SyncStripePayments(context.Background(), time.Now().Add(-time.Hour), time.Now())
if err == nil {
t.Fatalf("expected error, got nil")
}
}