rentease/internal/service/booking/stripe_sync_test.go

165 lines
5 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
}
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 { return 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")
}
}