//go:build test && dev // +build test,dev package payments import ( "context" "testing" "time" "crussell/db" "crussell/testutils" "crussell/testutils/fixtures" ) func setupRefundTestWithDiscount(t *testing.T) (string, string, float64) { t.Helper() userID, err := fixtures.CreateTestUser(db.DB) if err != nil { t.Fatalf("failed to create user: %v", err) } serviceID, err := fixtures.CreateTestService(db.DB) if err != nil { t.Fatalf("failed to create service: %v", err) } bookingID, err := fixtures.CreateTestBookingAtTime(db.DB, userID, serviceID, time.Date(2099, 12, 31, 10, 0, 0, 0, time.UTC)) if err != nil { t.Fatalf("failed to create booking: %v", err) } // Insert a real cash payment of 50 _, err = db.DB.Exec(context.Background(), ` INSERT INTO payments (booking_id, payment_type, payment_method, amount, status, created_at, updated_at) VALUES ($1, 'full', 'cash', 5000, 'completed', NOW(), NOW()) `, bookingID) if err != nil { t.Fatalf("failed to create cash payment: %v", err) } // Insert a discount payment record (should be excluded from refund) _, err = db.DB.Exec(context.Background(), ` INSERT INTO payments (booking_id, payment_type, payment_method, amount, status, created_at, updated_at) VALUES ($1, 'partial', 'discount', 500, 'completed', NOW(), NOW()) `, bookingID) if err != nil { t.Fatalf("failed to create discount payment: %v", err) } // Insert an on_the_house payment record (should also be excluded) _, err = db.DB.Exec(context.Background(), ` INSERT INTO payments (booking_id, payment_type, payment_method, amount, status, created_at, updated_at) VALUES ($1, 'partial', 'on_the_house', 1000, 'completed', NOW(), NOW()) `, bookingID) if err != nil { t.Fatalf("failed to create on_the_house payment: %v", err) } return userID, bookingID, 50.0 } func TestProcessCancellationRefund_ExcludesDiscountPayments(t *testing.T) { testutils.SetupTestDB(t) userID, bookingID, total := setupRefundTestWithDiscount(t) farFuture := time.Date(2099, 12, 31, 10, 0, 0, 0, time.UTC) now := farFuture.Add(-72 * time.Hour).Add(-1 * time.Hour) // >72h before result, err := ProcessCancellationRefund( context.Background(), bookingID, total, 50, farFuture, now, "client_cancelled", &userID, ) if err != nil { t.Fatalf("ProcessCancellationRefund failed: %v", err) } // Refundable should be 50 (only the cash payment), not 65 (which would include discount + OTH) if result.RefundableAmount != 50 { t.Errorf("expected refundable 50 (excluding discount/OTH), got %.2f", result.RefundableAmount) } } func TestProcessCancellationRefund_ExcludesOnTheHousePayments(t *testing.T) { testutils.SetupTestDB(t) userID, bookingID, total := setupRefundTestWithDiscount(t) // Make on_the_house the only non-discount payment by marking the 50 cash as a payment that gets refunded // but also add a pure on_the_house booking with no real money _, err := db.DB.Exec(context.Background(), ` INSERT INTO payments (booking_id, payment_type, payment_method, amount, status, created_at, updated_at) VALUES ($1, 'partial', 'discount', 2500, 'completed', NOW(), NOW()) `, bookingID) if err != nil { t.Fatalf("failed to create extra discount: %v", err) } farFuture := time.Date(2099, 12, 31, 10, 0, 0, 0, time.UTC) now := farFuture.Add(-72 * time.Hour).Add(-1 * time.Hour) result, err := ProcessCancellationRefund( context.Background(), bookingID, total, 50, farFuture, now, "client_cancelled", &userID, ) if err != nil { t.Fatalf("ProcessCancellationRefund failed: %v", err) } // Refundable should be based on the actual cash payment only if result.RefundableAmount != 50 { t.Errorf("expected refundable 50 (real money only), got %.2f", result.RefundableAmount) } }