veza/veza-backend-api/tests/marketplace/marketplace_flow_test.go

794 lines
25 KiB
Go
Raw Normal View History

//go:build integration || marketplace
// +build integration marketplace
package marketplace
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"testing"
"time"
"veza-backend-api/internal/core/marketplace"
"veza-backend-api/internal/database"
"veza-backend-api/internal/handlers"
"veza-backend-api/internal/models"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
// MockStorageService is a mock implementation of StorageService
type MockStorageService struct {
GetDownloadURLFunc func(filePath string) (string, error)
}
func (m *MockStorageService) GetDownloadURL(ctx context.Context, filePath string) (string, error) {
if m.GetDownloadURLFunc != nil {
return m.GetDownloadURLFunc(filePath)
}
return fmt.Sprintf("/download/%s", filePath), nil
}
// setupMarketplaceFlowTestRouter crée un router de test avec les services nécessaires pour les tests de flux marketplace
func setupMarketplaceFlowTestRouter(t *testing.T) (*gin.Engine, *gorm.DB, *database.Database, *marketplace.Service, func()) {
gin.SetMode(gin.TestMode)
logger := zaptest.NewLogger(t)
// Setup in-memory SQLite database
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
require.NoError(t, err)
db.Exec("PRAGMA foreign_keys = ON")
// Auto-migrate models (User and Track use standard GORM)
err = db.AutoMigrate(
&models.User{},
&models.Track{},
)
require.NoError(t, err)
// Create marketplace tables manually for SQLite compatibility (gen_random_uuid() is PostgreSQL-specific)
// Products table
err = db.Exec(`
CREATE TABLE IF NOT EXISTS products (
id TEXT PRIMARY KEY,
seller_id TEXT NOT NULL,
title TEXT NOT NULL,
description TEXT,
price REAL NOT NULL,
currency TEXT DEFAULT 'EUR',
status TEXT DEFAULT 'draft',
product_type TEXT NOT NULL,
track_id TEXT,
license_type TEXT,
bpm INTEGER,
musical_key TEXT,
category TEXT,
created_at DATETIME,
updated_at DATETIME,
deleted_at DATETIME
)
`).Error
require.NoError(t, err)
// Orders table
err = db.Exec(`
CREATE TABLE IF NOT EXISTS orders (
id TEXT PRIMARY KEY,
buyer_id TEXT NOT NULL,
total_amount REAL NOT NULL,
currency TEXT DEFAULT 'EUR',
status TEXT DEFAULT 'pending',
payment_intent TEXT,
hyperswitch_payment_id TEXT,
payment_status TEXT DEFAULT 'pending',
promo_code_id TEXT,
discount_amount_cents INTEGER DEFAULT 0,
created_at DATETIME,
updated_at DATETIME
)
`).Error
require.NoError(t, err)
// Product images table (for Preload in CreateProduct response)
err = db.Exec(`
CREATE TABLE IF NOT EXISTS product_images (
id TEXT PRIMARY KEY,
product_id TEXT NOT NULL,
url TEXT NOT NULL,
sort_order INTEGER DEFAULT 0,
created_at DATETIME,
FOREIGN KEY(product_id) REFERENCES products(id) ON DELETE CASCADE
)
`).Error
require.NoError(t, err)
// Order items table
err = db.Exec(`
CREATE TABLE IF NOT EXISTS order_items (
id TEXT PRIMARY KEY,
order_id TEXT NOT NULL,
product_id TEXT NOT NULL,
price REAL NOT NULL,
FOREIGN KEY(order_id) REFERENCES orders(id) ON DELETE CASCADE
)
`).Error
require.NoError(t, err)
// Product licenses table (license types per product)
err = db.Exec(`
CREATE TABLE IF NOT EXISTS product_licenses (
id TEXT PRIMARY KEY,
product_id TEXT NOT NULL,
license_type TEXT NOT NULL,
price_cents INTEGER NOT NULL,
terms_text TEXT,
created_at DATETIME,
FOREIGN KEY(product_id) REFERENCES products(id) ON DELETE CASCADE
)
`).Error
require.NoError(t, err)
// Licenses table (purchased licenses)
err = db.Exec(`
CREATE TABLE IF NOT EXISTS licenses (
id TEXT PRIMARY KEY,
buyer_id TEXT NOT NULL,
track_id TEXT NOT NULL,
product_id TEXT NOT NULL,
order_id TEXT NOT NULL,
type TEXT NOT NULL,
rights TEXT,
downloads_left INTEGER DEFAULT 3,
created_at DATETIME,
expires_at DATETIME,
revoked_at DATETIME
)
`).Error
require.NoError(t, err)
// Add BeforeCreate hooks for SQLite compatibility to generate UUIDs
db.Callback().Create().Before("gorm:create").Register("generate_uuid_product", func(db *gorm.DB) {
if product, ok := db.Statement.Dest.(*marketplace.Product); ok && product.ID == uuid.Nil {
product.ID = uuid.New()
}
if products, ok := db.Statement.Dest.(*[]marketplace.Product); ok {
for i := range *products {
if (*products)[i].ID == uuid.Nil {
(*products)[i].ID = uuid.New()
}
}
}
})
db.Callback().Create().Before("gorm:create").Register("generate_uuid_order", func(db *gorm.DB) {
if order, ok := db.Statement.Dest.(*marketplace.Order); ok && order.ID == uuid.Nil {
order.ID = uuid.New()
}
if orders, ok := db.Statement.Dest.(*[]marketplace.Order); ok {
for i := range *orders {
if (*orders)[i].ID == uuid.Nil {
(*orders)[i].ID = uuid.New()
}
}
}
})
db.Callback().Create().Before("gorm:create").Register("generate_uuid_order_item", func(db *gorm.DB) {
if item, ok := db.Statement.Dest.(*marketplace.OrderItem); ok && item.ID == uuid.Nil {
item.ID = uuid.New()
}
if items, ok := db.Statement.Dest.(*[]marketplace.OrderItem); ok {
for i := range *items {
if (*items)[i].ID == uuid.Nil {
(*items)[i].ID = uuid.New()
}
}
}
})
db.Callback().Create().Before("gorm:create").Register("generate_uuid_license", func(db *gorm.DB) {
if license, ok := db.Statement.Dest.(*marketplace.License); ok && license.ID == uuid.Nil {
license.ID = uuid.New()
}
if licenses, ok := db.Statement.Dest.(*[]marketplace.License); ok {
for i := range *licenses {
if (*licenses)[i].ID == uuid.Nil {
(*licenses)[i].ID = uuid.New()
}
}
}
})
// Get underlying sql.DB from GORM for raw SQL queries
sqlDB, err := db.DB()
require.NoError(t, err)
dbWrapper := &database.Database{
DB: sqlDB,
GormDB: db,
Logger: logger,
}
// Setup storage service
storageService := &MockStorageService{
GetDownloadURLFunc: func(filePath string) (string, error) {
return fmt.Sprintf("https://storage.example.com/download/%s", filePath), nil
},
}
// Setup marketplace service
marketService := marketplace.NewService(db, logger, storageService)
// Setup handlers
marketHandler := handlers.NewMarketplaceHandler(marketService, logger, "uploads")
// Create router
router := gin.New()
// Mock auth middleware - set user_id from header if present
router.Use(func(c *gin.Context) {
if userIDStr := c.GetHeader("X-User-ID"); userIDStr != "" {
if userID, err := uuid.Parse(userIDStr); err == nil {
c.Set("user_id", userID)
}
}
c.Next()
})
// product_reviews table for review tests (v0.602 E2E)
err = db.Exec(`
CREATE TABLE IF NOT EXISTS product_reviews (
id TEXT PRIMARY KEY,
product_id TEXT NOT NULL,
buyer_id TEXT NOT NULL,
order_id TEXT NOT NULL,
rating INTEGER NOT NULL CHECK (rating >= 1 AND rating <= 5),
comment TEXT,
created_at DATETIME,
UNIQUE(product_id, buyer_id),
FOREIGN KEY(product_id) REFERENCES products(id),
FOREIGN KEY(buyer_id) REFERENCES users(id),
FOREIGN KEY(order_id) REFERENCES orders(id)
)
`).Error
require.NoError(t, err)
// Setup marketplace routes
marketplaceGroup := router.Group("/api/v1/marketplace")
{
marketplaceGroup.GET("/products", marketHandler.ListProducts)
marketplaceGroup.POST("/products", marketHandler.CreateProduct)
marketplaceGroup.PUT("/products/:id", marketHandler.UpdateProduct)
marketplaceGroup.GET("/orders", marketHandler.ListOrders)
marketplaceGroup.GET("/orders/:id", marketHandler.GetOrder)
marketplaceGroup.POST("/orders", marketHandler.CreateOrder)
marketplaceGroup.GET("/download/:product_id", marketHandler.GetDownloadURL)
marketplaceGroup.POST("/products/:id/reviews", marketHandler.CreateReview)
marketplaceGroup.GET("/products/:id/reviews", marketHandler.ListReviews)
marketplaceGroup.GET("/orders/:id/invoice", marketHandler.GetOrderInvoice)
}
cleanup := func() {
// Database will be closed automatically
}
return router, db, dbWrapper, marketService, cleanup
}
// createTestUser crée un utilisateur de test
func createTestUser(t *testing.T, db *gorm.DB, email, username string) *models.User {
user := &models.User{
ID: uuid.New(),
Username: username,
Email: email,
PasswordHash: "hashed_password",
Slug: username,
IsActive: true,
IsVerified: true,
}
err := db.Create(user).Error
require.NoError(t, err)
return user
}
// createTestTrack crée un track de test
func createTestTrack(t *testing.T, db *gorm.DB, userID uuid.UUID, title string) *models.Track {
track := &models.Track{
ID: uuid.New(),
UserID: userID,
Title: title,
Artist: "Test Artist",
FilePath: fmt.Sprintf("/tracks/%s.mp3", uuid.New().String()),
Format: "mp3",
FileSize: 5 * 1024 * 1024,
Duration: 180,
IsPublic: true,
Status: models.TrackStatusCompleted,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err := db.Create(track).Error
require.NoError(t, err)
return track
}
// TestMarketplaceFlow_CompleteFlow teste le flux complet : création produit → commande → téléchargement
func TestMarketplaceFlow_CompleteFlow(t *testing.T) {
router, db, _, _, cleanup := setupMarketplaceFlowTestRouter(t)
defer cleanup()
// Create seller and buyer
seller := createTestUser(t, db, "seller@example.com", "seller")
buyer := createTestUser(t, db, "buyer@example.com", "buyer")
// Create track owned by seller
track := createTestTrack(t, db, seller.ID, "My Awesome Track")
// Step 1: Seller creates a product
createProductReq := handlers.CreateProductRequest{
Title: "My Product",
Description: "A great track for sale",
Price: 9.99,
ProductType: "track",
TrackID: track.ID.String(),
LicenseType: "standard",
}
body, _ := json.Marshal(createProductReq)
req := httptest.NewRequest(http.MethodPost, "/api/v1/marketplace/products", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-User-ID", seller.ID.String())
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusCreated, w.Code)
var productResponse map[string]interface{}
err := json.Unmarshal(w.Body.Bytes(), &productResponse)
require.NoError(t, err)
assert.True(t, productResponse["success"].(bool))
// Extract product ID from response
productData, ok := productResponse["data"].(map[string]interface{})
require.True(t, ok)
productIDStr, ok := productData["id"].(string)
require.True(t, ok)
productID, err := uuid.Parse(productIDStr)
require.NoError(t, err)
// Step 2: Buyer creates an order for the product
createOrderReq := handlers.CreateOrderRequest{
Items: []struct {
ProductID string `json:"product_id" binding:"required" validate:"required,uuid"`
}{
{ProductID: productID.String()},
},
}
body, _ = json.Marshal(createOrderReq)
req = httptest.NewRequest(http.MethodPost, "/api/v1/marketplace/orders", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-User-ID", buyer.ID.String())
w = httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusCreated, w.Code)
var orderResponse map[string]interface{}
err = json.Unmarshal(w.Body.Bytes(), &orderResponse)
require.NoError(t, err)
assert.True(t, orderResponse["success"].(bool))
// Verify order was created with correct status (data is CreateOrderResponse with nested order)
orderData, ok := orderResponse["data"].(map[string]interface{})
require.True(t, ok)
order, ok := orderData["order"].(map[string]interface{})
require.True(t, ok)
assert.Equal(t, "completed", order["status"])
// Step 3: Buyer gets download URL for the purchased product
req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("/api/v1/marketplace/download/%s", productID.String()), nil)
req.Header.Set("X-User-ID", buyer.ID.String())
w = httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
var downloadResponse map[string]interface{}
err = json.Unmarshal(w.Body.Bytes(), &downloadResponse)
require.NoError(t, err)
assert.True(t, downloadResponse["success"].(bool))
// Verify download URL is present
downloadData, ok := downloadResponse["data"].(map[string]interface{})
require.True(t, ok)
assert.Contains(t, downloadData, "url")
assert.NotEmpty(t, downloadData["url"])
}
// TestMarketplaceFlow_CompleteFlowWithReviewAndInvoice tests product -> order -> review -> invoice (v0.602 E2E)
func TestMarketplaceFlow_CompleteFlowWithReviewAndInvoice(t *testing.T) {
router, db, _, _, cleanup := setupMarketplaceFlowTestRouter(t)
defer cleanup()
seller := createTestUser(t, db, "seller@example.com", "seller")
buyer := createTestUser(t, db, "buyer@example.com", "buyer")
track := createTestTrack(t, db, seller.ID, "Track For Review")
// 1. Create product
createProductReq := handlers.CreateProductRequest{
Title: "Product For Review",
Description: "Great track",
Price: 12.99,
ProductType: "track",
TrackID: track.ID.String(),
LicenseType: "standard",
}
body, _ := json.Marshal(createProductReq)
req := httptest.NewRequest(http.MethodPost, "/api/v1/marketplace/products", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-User-ID", seller.ID.String())
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
require.Equal(t, http.StatusCreated, w.Code)
var productResp map[string]interface{}
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &productResp))
productData := productResp["data"].(map[string]interface{})
productID := productData["id"].(string)
// 2. Create order (simulated payment completes immediately)
orderBody, _ := json.Marshal(handlers.CreateOrderRequest{
Items: []struct {
ProductID string `json:"product_id" binding:"required" validate:"required,uuid"`
}{{ProductID: productID}},
})
req = httptest.NewRequest(http.MethodPost, "/api/v1/marketplace/orders", bytes.NewBuffer(orderBody))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-User-ID", buyer.ID.String())
w = httptest.NewRecorder()
router.ServeHTTP(w, req)
require.Equal(t, http.StatusCreated, w.Code)
var orderResp map[string]interface{}
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &orderResp))
orderData := orderResp["data"].(map[string]interface{})
order := orderData["order"].(map[string]interface{})
orderID := order["id"].(string)
assert.Equal(t, "completed", order["status"])
// 3. Create review
reviewBody, _ := json.Marshal(map[string]interface{}{"rating": 5, "comment": "Excellent!"})
req = httptest.NewRequest(http.MethodPost, fmt.Sprintf("/api/v1/marketplace/products/%s/reviews", productID), bytes.NewBuffer(reviewBody))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-User-ID", buyer.ID.String())
w = httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusCreated, w.Code)
// 4. Download invoice (PDF)
req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("/api/v1/marketplace/orders/%s/invoice", orderID), nil)
req.Header.Set("X-User-ID", buyer.ID.String())
w = httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, "application/pdf", w.Header().Get("Content-Type"))
assert.NotEmpty(t, w.Body.Bytes())
}
// TestMarketplaceFlow_CreateProduct_Validation teste la validation lors de la création de produit
func TestMarketplaceFlow_CreateProduct_Validation(t *testing.T) {
router, db, _, _, cleanup := setupMarketplaceFlowTestRouter(t)
defer cleanup()
seller := createTestUser(t, db, "seller@example.com", "seller")
t.Run("Missing required fields", func(t *testing.T) {
createProductReq := handlers.CreateProductRequest{
Description: "A great track",
Price: 9.99,
}
body, _ := json.Marshal(createProductReq)
req := httptest.NewRequest(http.MethodPost, "/api/v1/marketplace/products", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-User-ID", seller.ID.String())
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusBadRequest, w.Code)
})
t.Run("Invalid price", func(t *testing.T) {
createProductReq := handlers.CreateProductRequest{
Title: "My Product",
Description: "A great track",
Price: -5.0, // Invalid negative price
ProductType: "track",
}
body, _ := json.Marshal(createProductReq)
req := httptest.NewRequest(http.MethodPost, "/api/v1/marketplace/products", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-User-ID", seller.ID.String())
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusBadRequest, w.Code)
})
t.Run("Invalid product type", func(t *testing.T) {
createProductReq := handlers.CreateProductRequest{
Title: "My Product",
Description: "A great track",
Price: 9.99,
ProductType: "invalid_type",
}
body, _ := json.Marshal(createProductReq)
req := httptest.NewRequest(http.MethodPost, "/api/v1/marketplace/products", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-User-ID", seller.ID.String())
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusBadRequest, w.Code)
})
}
// TestMarketplaceFlow_CreateOrder_Validation teste la validation lors de la création de commande
func TestMarketplaceFlow_CreateOrder_Validation(t *testing.T) {
router, db, _, _, cleanup := setupMarketplaceFlowTestRouter(t)
defer cleanup()
buyer := createTestUser(t, db, "buyer@example.com", "buyer")
t.Run("Empty items", func(t *testing.T) {
createOrderReq := handlers.CreateOrderRequest{
Items: []struct {
ProductID string `json:"product_id" binding:"required" validate:"required,uuid"`
}{},
}
body, _ := json.Marshal(createOrderReq)
req := httptest.NewRequest(http.MethodPost, "/api/v1/marketplace/orders", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-User-ID", buyer.ID.String())
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusBadRequest, w.Code)
})
t.Run("Non-existent product", func(t *testing.T) {
createOrderReq := handlers.CreateOrderRequest{
Items: []struct {
ProductID string `json:"product_id" binding:"required" validate:"required,uuid"`
}{
{ProductID: uuid.New().String()},
},
}
body, _ := json.Marshal(createOrderReq)
req := httptest.NewRequest(http.MethodPost, "/api/v1/marketplace/orders", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-User-ID", buyer.ID.String())
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusBadRequest, w.Code)
})
}
// TestMarketplaceFlow_GetDownloadURL_NoLicense teste le téléchargement sans licence
func TestMarketplaceFlow_GetDownloadURL_NoLicense(t *testing.T) {
router, db, _, _, cleanup := setupMarketplaceFlowTestRouter(t)
defer cleanup()
seller := createTestUser(t, db, "seller@example.com", "seller")
buyer := createTestUser(t, db, "buyer@example.com", "buyer")
// Create track and product
track := createTestTrack(t, db, seller.ID, "My Track")
product := &marketplace.Product{
ID: uuid.New(),
SellerID: seller.ID,
Title: "My Product",
Description: "A great track",
Price: 9.99,
ProductType: "track",
TrackID: &track.ID,
Status: marketplace.ProductStatusActive,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err := db.Create(product).Error
require.NoError(t, err)
// Try to download without purchasing
req := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/api/v1/marketplace/download/%s", product.ID.String()), nil)
req.Header.Set("X-User-ID", buyer.ID.String())
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusForbidden, w.Code)
}
// TestMarketplaceFlow_ListOrders teste la liste des commandes
func TestMarketplaceFlow_ListOrders(t *testing.T) {
router, db, _, _, cleanup := setupMarketplaceFlowTestRouter(t)
defer cleanup()
seller := createTestUser(t, db, "seller@example.com", "seller")
buyer := createTestUser(t, db, "buyer@example.com", "buyer")
// Create track and product
track := createTestTrack(t, db, seller.ID, "My Track")
product := &marketplace.Product{
ID: uuid.New(),
SellerID: seller.ID,
Title: "My Product",
Description: "A great track",
Price: 9.99,
ProductType: "track",
TrackID: &track.ID,
Status: marketplace.ProductStatusActive,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err := db.Create(product).Error
require.NoError(t, err)
// Create order
createOrderReq := handlers.CreateOrderRequest{
Items: []struct {
ProductID string `json:"product_id" binding:"required" validate:"required,uuid"`
}{
{ProductID: product.ID.String()},
},
}
body, _ := json.Marshal(createOrderReq)
req := httptest.NewRequest(http.MethodPost, "/api/v1/marketplace/orders", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-User-ID", buyer.ID.String())
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusCreated, w.Code)
// List orders
req = httptest.NewRequest(http.MethodGet, "/api/v1/marketplace/orders", nil)
req.Header.Set("X-User-ID", buyer.ID.String())
w = httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
var response map[string]interface{}
err = json.Unmarshal(w.Body.Bytes(), &response)
require.NoError(t, err)
assert.True(t, response["success"].(bool))
// Verify orders list
data, ok := response["data"].([]interface{})
require.True(t, ok)
assert.GreaterOrEqual(t, len(data), 1)
}
// TestMarketplaceFlow_GetOrder teste la récupération d'une commande spécifique
func TestMarketplaceFlow_GetOrder(t *testing.T) {
router, db, _, _, cleanup := setupMarketplaceFlowTestRouter(t)
defer cleanup()
seller := createTestUser(t, db, "seller@example.com", "seller")
buyer := createTestUser(t, db, "buyer@example.com", "buyer")
// Create track and product
track := createTestTrack(t, db, seller.ID, "My Track")
product := &marketplace.Product{
ID: uuid.New(),
SellerID: seller.ID,
Title: "My Product",
Description: "A great track",
Price: 9.99,
ProductType: "track",
TrackID: &track.ID,
Status: marketplace.ProductStatusActive,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err := db.Create(product).Error
require.NoError(t, err)
// Create order
createOrderReq := handlers.CreateOrderRequest{
Items: []struct {
ProductID string `json:"product_id" binding:"required" validate:"required,uuid"`
}{
{ProductID: product.ID.String()},
},
}
body, _ := json.Marshal(createOrderReq)
req := httptest.NewRequest(http.MethodPost, "/api/v1/marketplace/orders", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-User-ID", buyer.ID.String())
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusCreated, w.Code)
// Extract order ID (data is CreateOrderResponse with nested order)
var orderResponse map[string]interface{}
err = json.Unmarshal(w.Body.Bytes(), &orderResponse)
require.NoError(t, err)
orderData, ok := orderResponse["data"].(map[string]interface{})
require.True(t, ok)
orderObj, ok := orderData["order"].(map[string]interface{})
require.True(t, ok)
orderIDStr, ok := orderObj["id"].(string)
require.True(t, ok)
// Get order details
req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("/api/v1/marketplace/orders/%s", orderIDStr), nil)
req.Header.Set("X-User-ID", buyer.ID.String())
w = httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
var response map[string]interface{}
err = json.Unmarshal(w.Body.Bytes(), &response)
require.NoError(t, err)
assert.True(t, response["success"].(bool))
// Verify order details
orderDetails, ok := response["data"].(map[string]interface{})
require.True(t, ok)
assert.Equal(t, orderIDStr, orderDetails["id"].(string))
assert.Equal(t, "completed", orderDetails["status"])
}
// TestMarketplaceFlow_CreateOrder_InactiveProduct teste la commande d'un produit inactif
func TestMarketplaceFlow_CreateOrder_InactiveProduct(t *testing.T) {
router, db, _, _, cleanup := setupMarketplaceFlowTestRouter(t)
defer cleanup()
seller := createTestUser(t, db, "seller@example.com", "seller")
buyer := createTestUser(t, db, "buyer@example.com", "buyer")
// Create track and inactive product
track := createTestTrack(t, db, seller.ID, "My Track")
product := &marketplace.Product{
ID: uuid.New(),
SellerID: seller.ID,
Title: "My Product",
Description: "A great track",
Price: 9.99,
ProductType: "track",
TrackID: &track.ID,
Status: marketplace.ProductStatusDraft, // Inactive
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
err := db.Create(product).Error
require.NoError(t, err)
// Try to create order for inactive product
createOrderReq := handlers.CreateOrderRequest{
Items: []struct {
ProductID string `json:"product_id" binding:"required" validate:"required,uuid"`
}{
{ProductID: product.ID.String()},
},
}
body, _ := json.Marshal(createOrderReq)
req := httptest.NewRequest(http.MethodPost, "/api/v1/marketplace/orders", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-User-ID", buyer.ID.String())
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusBadRequest, w.Code)
}