veza/veza-backend-api/tests/integration/refund_flow_test.go
senke a1000ce7fb style(backend): gofmt -w on 85 files (whitespace only)
backend-ci.yml's `test -z "$(gofmt -l .)"` strict gate (added in
13c21ac11) failed on a backlog of unformatted files. None of the
85 files in this commit had been edited since the gate was added
because no push touched veza-backend-api/** in between, so the
gate never fired until today's CI fixes triggered it.

The diff is exclusively whitespace alignment in struct literals
and trailing-space comments. `go build ./...` and the full test
suite (with VEZA_SKIP_INTEGRATION=1 -short) pass identically.
2026-04-14 12:22:14 +02:00

174 lines
5.4 KiB
Go

//go:build integration
// +build integration
package integration
import (
"bytes"
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"os"
"testing"
"time"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"veza-backend-api/internal/api"
"veza-backend-api/internal/config"
"veza-backend-api/internal/core/marketplace"
"veza-backend-api/internal/database"
"veza-backend-api/internal/metrics"
"veza-backend-api/internal/models"
"veza-backend-api/internal/services"
)
// mockRefundPaymentProvider implements PaymentProvider and Refund for refund flow test
type mockRefundPaymentProvider struct {
refundErr error
}
func (m *mockRefundPaymentProvider) CreatePayment(_ context.Context, _ int64, _ string, _ string, _ string, _ map[string]string) (string, string, error) {
return "pay_refund_mock", "secret", nil
}
func (m *mockRefundPaymentProvider) GetPayment(_ context.Context, _ string) (string, error) {
return "succeeded", nil
}
func (m *mockRefundPaymentProvider) Refund(_ context.Context, _ string, _ *int64, _ string) error {
return m.refundErr
}
func setupRefundFlowRouter(t *testing.T, db *gorm.DB, marketService *marketplace.Service) *gin.Engine {
t.Helper()
os.Setenv("ENABLE_CLAMAV", "false")
os.Setenv("CLAMAV_REQUIRED", "false")
gin.SetMode(gin.TestMode)
router := gin.New()
sqlDB, err := db.DB()
require.NoError(t, err)
vezaDB := &database.Database{
DB: sqlDB,
GormDB: db,
Logger: zap.NewNop(),
}
cfg := &config.Config{
HyperswitchWebhookSecret: "test-secret",
JWTSecret: "test-jwt-secret-key-minimum-32-characters-long",
JWTIssuer: "veza-api",
JWTAudience: "veza-app",
Logger: zap.NewNop(),
RedisClient: nil,
ErrorMetrics: metrics.NewErrorMetrics(),
UploadDir: "uploads/test",
Env: "development",
Database: vezaDB,
CORSOrigins: []string{"*"},
HandlerTimeout: 30 * time.Second,
RateLimitLimit: 100,
RateLimitWindow: 60,
AuthRateLimitLoginAttempts: 10,
AuthRateLimitLoginWindow: 15,
MarketplaceServiceOverride: marketService,
AuthMiddlewareOverride: &testAuthMiddleware{},
}
require.NoError(t, cfg.InitServicesForTest())
require.NoError(t, cfg.InitMiddlewaresForTest())
apiRouter := api.NewAPIRouter(vezaDB, cfg)
require.NoError(t, apiRouter.Setup(router))
return router
}
// TestRefundFlow_CompletedOrder_Refund_RevokesLicense verifies: order completed -> refund request ->
// order status refunded, licence revoked.
func TestRefundFlow_CompletedOrder_Refund_RevokesLicense(t *testing.T) {
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
require.NoError(t, err)
require.NoError(t, db.AutoMigrate(
&models.User{},
&models.Track{},
&marketplace.Product{},
&marketplace.Order{},
&marketplace.OrderItem{},
&marketplace.License{},
&marketplace.SellerTransfer{},
))
buyerID := uuid.New()
sellerID := uuid.New()
trackID := uuid.New()
productID := uuid.New()
orderID := uuid.New()
require.NoError(t, db.Create(&models.User{ID: buyerID}).Error)
require.NoError(t, db.Create(&models.User{ID: sellerID}).Error)
require.NoError(t, db.Create(&models.Track{ID: trackID, UserID: sellerID, FilePath: "/t.mp3"}).Error)
require.NoError(t, db.Create(&marketplace.Product{
ID: productID,
SellerID: sellerID,
Title: "Test Product",
Price: 9.99,
ProductType: "track",
TrackID: &trackID,
Status: marketplace.ProductStatusActive,
}).Error)
require.NoError(t, db.Create(&marketplace.Order{
ID: orderID,
BuyerID: buyerID,
TotalAmount: 9.99,
Currency: "EUR",
Status: "completed",
HyperswitchPaymentID: "pay_refund_mock",
}).Error)
require.NoError(t, db.Create(&marketplace.OrderItem{
ID: uuid.New(),
OrderID: orderID,
ProductID: productID,
Price: 9.99,
}).Error)
require.NoError(t, db.Create(&marketplace.License{
ID: uuid.New(),
BuyerID: buyerID,
TrackID: trackID,
ProductID: productID,
OrderID: orderID,
Type: marketplace.LicenseBasic,
}).Error)
mockPay := &mockRefundPaymentProvider{}
storageService := services.NewTrackStorageService("uploads/test", false, zap.NewNop())
marketService := marketplace.NewService(db, zap.NewNop(), storageService, marketplace.WithPaymentProvider(mockPay))
router := setupRefundFlowRouter(t, db, marketService)
body, _ := json.Marshal(map[string]string{"reason": "Customer request"})
req := httptest.NewRequest(http.MethodPost, "/api/v1/marketplace/orders/"+orderID.String()+"/refund", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-User-ID", buyerID.String())
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code, "refund must succeed: %s", w.Body.String())
var order marketplace.Order
require.NoError(t, db.First(&order, orderID).Error)
assert.Equal(t, "refunded", order.Status)
var licenses []marketplace.License
require.NoError(t, db.Where("order_id = ?", orderID).Find(&licenses).Error)
require.Len(t, licenses, 1)
assert.NotNil(t, licenses[0].RevokedAt, "license must be revoked")
}