package handlers import ( "context" "encoding/json" "net/http" "net/http/httptest" "testing" "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/core/marketplace" "veza-backend-api/internal/models" ) func setupAdminTransferTestDB(t *testing.T) *gorm.DB { db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{}) require.NoError(t, err) require.NoError(t, db.AutoMigrate( &models.User{}, &marketplace.SellerTransfer{}, )) return db } type mockTransferServiceAdmin struct { err error stripeTransferID string } func (m *mockTransferServiceAdmin) CreateTransfer(_ context.Context, _ uuid.UUID, _ int64, _, _ string) (string, error) { if m.err != nil { return "", m.err } id := m.stripeTransferID if id == "" { id = "tr_mock" } return id, nil } func (m *mockTransferServiceAdmin) ReverseTransfer(_ context.Context, _ string, _ *int64, _ string) (string, error) { return "rev_mock", nil } func TestGetTransfers_ReturnsAll(t *testing.T) { db := setupAdminTransferTestDB(t) logger := zap.NewNop() handler := NewAdminTransferHandler(db, nil, 0.10, logger) sellerID := uuid.New() orderID := uuid.New() require.NoError(t, db.Create(&marketplace.SellerTransfer{ ID: uuid.New(), SellerID: sellerID, OrderID: orderID, AmountCents: 1000, PlatformFeeCents: 100, Currency: "EUR", Status: "completed", }).Error) gin.SetMode(gin.TestMode) w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) c.Request = httptest.NewRequest(http.MethodGet, "/admin/transfers", nil) handler.GetTransfers(c) assert.Equal(t, http.StatusOK, w.Code) var body map[string]interface{} require.NoError(t, json.Unmarshal(w.Body.Bytes(), &body)) assert.True(t, body["success"].(bool)) data := body["data"].(map[string]interface{}) transfers := data["transfers"].([]interface{}) assert.Len(t, transfers, 1) assert.Equal(t, float64(1), float64(data["total"].(float64))) } func TestGetTransfers_FilterByStatus(t *testing.T) { db := setupAdminTransferTestDB(t) logger := zap.NewNop() handler := NewAdminTransferHandler(db, nil, 0.10, logger) sellerID := uuid.New() require.NoError(t, db.Create(&marketplace.SellerTransfer{ ID: uuid.New(), SellerID: sellerID, OrderID: uuid.New(), AmountCents: 1000, PlatformFeeCents: 100, Currency: "EUR", Status: "completed", }).Error) require.NoError(t, db.Create(&marketplace.SellerTransfer{ ID: uuid.New(), SellerID: sellerID, OrderID: uuid.New(), AmountCents: 500, PlatformFeeCents: 50, Currency: "EUR", Status: "failed", }).Error) gin.SetMode(gin.TestMode) w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) c.Request = httptest.NewRequest(http.MethodGet, "/admin/transfers?status=failed", nil) handler.GetTransfers(c) assert.Equal(t, http.StatusOK, w.Code) var body map[string]interface{} require.NoError(t, json.Unmarshal(w.Body.Bytes(), &body)) data := body["data"].(map[string]interface{}) transfers := data["transfers"].([]interface{}) assert.Len(t, transfers, 1) assert.Equal(t, float64(1), float64(data["total"].(float64))) } func TestGetTransfers_FilterBySeller(t *testing.T) { db := setupAdminTransferTestDB(t) logger := zap.NewNop() handler := NewAdminTransferHandler(db, nil, 0.10, logger) seller1 := uuid.New() seller2 := uuid.New() require.NoError(t, db.Create(&marketplace.SellerTransfer{ ID: uuid.New(), SellerID: seller1, OrderID: uuid.New(), AmountCents: 1000, PlatformFeeCents: 100, Currency: "EUR", Status: "completed", }).Error) require.NoError(t, db.Create(&marketplace.SellerTransfer{ ID: uuid.New(), SellerID: seller2, OrderID: uuid.New(), AmountCents: 500, PlatformFeeCents: 50, Currency: "EUR", Status: "completed", }).Error) gin.SetMode(gin.TestMode) w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) c.Request = httptest.NewRequest(http.MethodGet, "/admin/transfers?seller_id="+seller1.String(), nil) handler.GetTransfers(c) assert.Equal(t, http.StatusOK, w.Code) var body map[string]interface{} require.NoError(t, json.Unmarshal(w.Body.Bytes(), &body)) data := body["data"].(map[string]interface{}) transfers := data["transfers"].([]interface{}) assert.Len(t, transfers, 1) } func TestRetryTransfer_Success(t *testing.T) { db := setupAdminTransferTestDB(t) logger := zap.NewNop() mock := &mockTransferServiceAdmin{stripeTransferID: "tr_admin_retry_ok"} handler := NewAdminTransferHandler(db, mock, 0.10, logger) transferID := uuid.New() sellerID := uuid.New() orderID := uuid.New() require.NoError(t, db.Create(&marketplace.SellerTransfer{ ID: transferID, SellerID: sellerID, OrderID: orderID, AmountCents: 900, PlatformFeeCents: 100, Currency: "EUR", Status: "failed", }).Error) gin.SetMode(gin.TestMode) w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) c.Request = httptest.NewRequest(http.MethodPost, "/admin/transfers/"+transferID.String()+"/retry", nil) c.Params = gin.Params{{Key: "id", Value: transferID.String()}} handler.RetryTransfer(c) assert.Equal(t, http.StatusOK, w.Code) var updated marketplace.SellerTransfer require.NoError(t, db.First(&updated, transferID).Error) assert.Equal(t, "completed", updated.Status) // v1.0.7 item A: admin-triggered retry also persists the Stripe // transfer id. The worker and processSellerTransfers paths are // already covered by their own tests; this is the third path into // the same behavior. assert.Equal(t, "tr_admin_retry_ok", updated.StripeTransferID) } func TestRetryTransfer_NotFailed(t *testing.T) { db := setupAdminTransferTestDB(t) logger := zap.NewNop() mock := &mockTransferServiceAdmin{} handler := NewAdminTransferHandler(db, mock, 0.10, logger) transferID := uuid.New() require.NoError(t, db.Create(&marketplace.SellerTransfer{ ID: transferID, SellerID: uuid.New(), OrderID: uuid.New(), AmountCents: 900, PlatformFeeCents: 100, Currency: "EUR", Status: "completed", }).Error) gin.SetMode(gin.TestMode) w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) c.Request = httptest.NewRequest(http.MethodPost, "/admin/transfers/"+transferID.String()+"/retry", nil) c.Params = gin.Params{{Key: "id", Value: transferID.String()}} handler.RetryTransfer(c) assert.Equal(t, http.StatusBadRequest, w.Code) } func TestRetryTransfer_NotFound(t *testing.T) { db := setupAdminTransferTestDB(t) logger := zap.NewNop() mock := &mockTransferServiceAdmin{} handler := NewAdminTransferHandler(db, mock, 0.10, logger) nonexistentID := uuid.New() gin.SetMode(gin.TestMode) w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) c.Request = httptest.NewRequest(http.MethodPost, "/admin/transfers/"+nonexistentID.String()+"/retry", nil) c.Params = gin.Params{{Key: "id", Value: nonexistentID.String()}} handler.RetryTransfer(c) assert.Equal(t, http.StatusNotFound, w.Code) }