feat(v0.11.3): F421-F424 admin platform handler and routes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
0a055db479
commit
f68405a52e
4 changed files with 515 additions and 0 deletions
|
|
@ -343,6 +343,9 @@ func (r *APIRouter) Setup(router *gin.Engine) error {
|
|||
|
||||
// v0.11.2: Advanced Moderation (F411-F420)
|
||||
r.setupModerationRoutes(v1)
|
||||
|
||||
// v0.11.3: Admin Platform Management (F421-F435)
|
||||
r.setupAdminPlatformRoutes(v1)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
|||
41
veza-backend-api/internal/api/routes_admin_platform.go
Normal file
41
veza-backend-api/internal/api/routes_admin_platform.go
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
admincore "veza-backend-api/internal/core/admin"
|
||||
"veza-backend-api/internal/services"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// setupAdminPlatformRoutes registers admin platform management routes (v0.11.3 F421-F435)
|
||||
func (r *APIRouter) setupAdminPlatformRoutes(router *gin.RouterGroup) {
|
||||
platformService := services.NewAdminPlatformService(r.db.GormDB, r.logger)
|
||||
platformHandler := admincore.NewPlatformAdminHandler(platformService, r.logger)
|
||||
|
||||
admin := router.Group("/admin/platform")
|
||||
{
|
||||
if r.config.AuthMiddleware != nil {
|
||||
admin.Use(r.config.AuthMiddleware.RequireAuth())
|
||||
admin.Use(r.config.AuthMiddleware.RequireAdmin())
|
||||
}
|
||||
|
||||
// F421: Platform metrics
|
||||
admin.GET("/metrics", platformHandler.GetPlatformMetrics)
|
||||
|
||||
// F422: User management
|
||||
admin.GET("/users", platformHandler.SearchUsers)
|
||||
admin.GET("/users/:userId", platformHandler.GetUserDetail)
|
||||
admin.PUT("/users/:userId/role", platformHandler.UpdateUserRole)
|
||||
admin.POST("/users/:userId/suspend", platformHandler.SuspendUser)
|
||||
admin.POST("/users/:userId/unsuspend", platformHandler.UnsuspendUser)
|
||||
|
||||
// F423: Content management
|
||||
admin.GET("/content", platformHandler.SearchContent)
|
||||
admin.POST("/content/:id/hide", platformHandler.HideContent)
|
||||
admin.POST("/content/:id/restore", platformHandler.RestoreContent)
|
||||
|
||||
// F424: Payment management
|
||||
admin.GET("/payments", platformHandler.GetPaymentOverview)
|
||||
admin.POST("/orders/:id/refund", platformHandler.RefundOrder)
|
||||
}
|
||||
}
|
||||
339
veza-backend-api/internal/core/admin/handler.go
Normal file
339
veza-backend-api/internal/core/admin/handler.go
Normal file
|
|
@ -0,0 +1,339 @@
|
|||
package admin
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
apperrors "veza-backend-api/internal/errors"
|
||||
"veza-backend-api/internal/handlers"
|
||||
"veza-backend-api/internal/services"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// PlatformAdminHandler handles admin platform HTTP requests (F421-F435)
|
||||
type PlatformAdminHandler struct {
|
||||
service *services.AdminPlatformService
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewPlatformAdminHandler creates a new platform admin handler
|
||||
func NewPlatformAdminHandler(service *services.AdminPlatformService, logger *zap.Logger) *PlatformAdminHandler {
|
||||
if logger == nil {
|
||||
logger = zap.NewNop()
|
||||
}
|
||||
return &PlatformAdminHandler{service: service, logger: logger}
|
||||
}
|
||||
|
||||
// getAdminID extracts admin user ID from context
|
||||
func (h *PlatformAdminHandler) getAdminID(c *gin.Context) (uuid.UUID, bool) {
|
||||
userID, ok := handlers.GetUserIDUUID(c)
|
||||
if !ok || userID == uuid.Nil {
|
||||
handlers.RespondWithAppError(c, apperrors.New(apperrors.ErrCodeUnauthorized, "authentication required"))
|
||||
return uuid.Nil, false
|
||||
}
|
||||
return userID, true
|
||||
}
|
||||
|
||||
// --- F421: Platform Dashboard ---
|
||||
|
||||
// GetPlatformMetrics handles GET /api/v1/admin/platform/metrics (F421)
|
||||
func (h *PlatformAdminHandler) GetPlatformMetrics(c *gin.Context) {
|
||||
metrics, err := h.service.GetPlatformMetrics(c.Request.Context())
|
||||
if err != nil {
|
||||
handlers.RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "failed to get metrics", err))
|
||||
return
|
||||
}
|
||||
|
||||
handlers.RespondSuccess(c, http.StatusOK, gin.H{
|
||||
"metrics": metrics,
|
||||
})
|
||||
}
|
||||
|
||||
// --- F422: User Management ---
|
||||
|
||||
// SearchUsers handles GET /api/v1/admin/platform/users (F422)
|
||||
func (h *PlatformAdminHandler) SearchUsers(c *gin.Context) {
|
||||
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "20"))
|
||||
offset, _ := strconv.Atoi(c.DefaultQuery("offset", "0"))
|
||||
|
||||
var isBanned *bool
|
||||
if b := c.Query("is_banned"); b != "" {
|
||||
v := b == "true"
|
||||
isBanned = &v
|
||||
}
|
||||
|
||||
params := services.AdminUserSearchParams{
|
||||
Query: c.Query("q"),
|
||||
Role: c.Query("role"),
|
||||
IsBanned: isBanned,
|
||||
Limit: limit,
|
||||
Offset: offset,
|
||||
SortBy: c.DefaultQuery("sort_by", "created_at"),
|
||||
}
|
||||
|
||||
users, total, err := h.service.SearchUsers(c.Request.Context(), params)
|
||||
if err != nil {
|
||||
handlers.RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "failed to search users", err))
|
||||
return
|
||||
}
|
||||
|
||||
handlers.RespondSuccess(c, http.StatusOK, gin.H{
|
||||
"users": users,
|
||||
"pagination": gin.H{
|
||||
"total": total,
|
||||
"limit": limit,
|
||||
"offset": offset,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// GetUserDetail handles GET /api/v1/admin/platform/users/:userId (F422)
|
||||
func (h *PlatformAdminHandler) GetUserDetail(c *gin.Context) {
|
||||
userIDStr := c.Param("userId")
|
||||
userID, err := uuid.Parse(userIDStr)
|
||||
if err != nil {
|
||||
handlers.RespondWithAppError(c, apperrors.NewValidationError("invalid user ID"))
|
||||
return
|
||||
}
|
||||
|
||||
user, err := h.service.GetUserDetail(c.Request.Context(), userID)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "not found") {
|
||||
handlers.RespondWithAppError(c, apperrors.NewNotFoundError("user"))
|
||||
return
|
||||
}
|
||||
handlers.RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "failed to get user", err))
|
||||
return
|
||||
}
|
||||
|
||||
handlers.RespondSuccess(c, http.StatusOK, gin.H{
|
||||
"user": user,
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateUserRole handles PUT /api/v1/admin/platform/users/:userId/role (F422)
|
||||
func (h *PlatformAdminHandler) UpdateUserRole(c *gin.Context) {
|
||||
userIDStr := c.Param("userId")
|
||||
userID, err := uuid.Parse(userIDStr)
|
||||
if err != nil {
|
||||
handlers.RespondWithAppError(c, apperrors.NewValidationError("invalid user ID"))
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
Role string `json:"role" binding:"required"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
handlers.RespondWithAppError(c, apperrors.NewValidationError("role is required"))
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.service.UpdateUserRole(c.Request.Context(), userID, req.Role); err != nil {
|
||||
if strings.Contains(err.Error(), "invalid role") {
|
||||
handlers.RespondWithAppError(c, apperrors.NewValidationError(err.Error()))
|
||||
return
|
||||
}
|
||||
if strings.Contains(err.Error(), "not found") {
|
||||
handlers.RespondWithAppError(c, apperrors.NewNotFoundError("user"))
|
||||
return
|
||||
}
|
||||
handlers.RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "failed to update role", err))
|
||||
return
|
||||
}
|
||||
|
||||
handlers.RespondSuccess(c, http.StatusOK, gin.H{"status": "role_updated"})
|
||||
}
|
||||
|
||||
// SuspendUser handles POST /api/v1/admin/platform/users/:userId/suspend (F422)
|
||||
func (h *PlatformAdminHandler) SuspendUser(c *gin.Context) {
|
||||
adminID, ok := h.getAdminID(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
userIDStr := c.Param("userId")
|
||||
userID, err := uuid.Parse(userIDStr)
|
||||
if err != nil {
|
||||
handlers.RespondWithAppError(c, apperrors.NewValidationError("invalid user ID"))
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
Reason string `json:"reason" binding:"required"`
|
||||
DurationDays *int `json:"duration_days"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
handlers.RespondWithAppError(c, apperrors.NewValidationError("reason is required"))
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.service.SuspendUser(c.Request.Context(), userID, adminID, req.Reason, req.DurationDays); err != nil {
|
||||
handlers.RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "failed to suspend user", err))
|
||||
return
|
||||
}
|
||||
|
||||
handlers.RespondSuccess(c, http.StatusOK, gin.H{"status": "suspended"})
|
||||
}
|
||||
|
||||
// UnsuspendUser handles POST /api/v1/admin/platform/users/:userId/unsuspend (F422)
|
||||
func (h *PlatformAdminHandler) UnsuspendUser(c *gin.Context) {
|
||||
adminID, ok := h.getAdminID(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
userIDStr := c.Param("userId")
|
||||
userID, err := uuid.Parse(userIDStr)
|
||||
if err != nil {
|
||||
handlers.RespondWithAppError(c, apperrors.NewValidationError("invalid user ID"))
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.service.UnsuspendUser(c.Request.Context(), userID, adminID); err != nil {
|
||||
handlers.RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "failed to unsuspend user", err))
|
||||
return
|
||||
}
|
||||
|
||||
handlers.RespondSuccess(c, http.StatusOK, gin.H{"status": "unsuspended"})
|
||||
}
|
||||
|
||||
// --- F423: Content Management ---
|
||||
|
||||
// SearchContent handles GET /api/v1/admin/platform/content (F423)
|
||||
func (h *PlatformAdminHandler) SearchContent(c *gin.Context) {
|
||||
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "20"))
|
||||
offset, _ := strconv.Atoi(c.DefaultQuery("offset", "0"))
|
||||
|
||||
items, total, err := h.service.SearchContent(c.Request.Context(), c.DefaultQuery("type", "track"), c.Query("q"), limit, offset)
|
||||
if err != nil {
|
||||
handlers.RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "failed to search content", err))
|
||||
return
|
||||
}
|
||||
|
||||
handlers.RespondSuccess(c, http.StatusOK, gin.H{
|
||||
"content": items,
|
||||
"pagination": gin.H{
|
||||
"total": total,
|
||||
"limit": limit,
|
||||
"offset": offset,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// HideContent handles POST /api/v1/admin/platform/content/:id/hide (F423)
|
||||
func (h *PlatformAdminHandler) HideContent(c *gin.Context) {
|
||||
adminID, ok := h.getAdminID(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
contentIDStr := c.Param("id")
|
||||
contentID, err := uuid.Parse(contentIDStr)
|
||||
if err != nil {
|
||||
handlers.RespondWithAppError(c, apperrors.NewValidationError("invalid content ID"))
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
ContentType string `json:"content_type" binding:"required"`
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
handlers.RespondWithAppError(c, apperrors.NewValidationError("content_type is required"))
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.service.HideContent(c.Request.Context(), adminID, req.ContentType, contentID, req.Reason); err != nil {
|
||||
if strings.Contains(err.Error(), "invalid content type") {
|
||||
handlers.RespondWithAppError(c, apperrors.NewValidationError(err.Error()))
|
||||
return
|
||||
}
|
||||
handlers.RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "failed to hide content", err))
|
||||
return
|
||||
}
|
||||
|
||||
handlers.RespondSuccess(c, http.StatusOK, gin.H{"status": "hidden"})
|
||||
}
|
||||
|
||||
// RestoreContent handles POST /api/v1/admin/platform/content/:id/restore (F423)
|
||||
func (h *PlatformAdminHandler) RestoreContent(c *gin.Context) {
|
||||
adminID, ok := h.getAdminID(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
contentIDStr := c.Param("id")
|
||||
contentID, err := uuid.Parse(contentIDStr)
|
||||
if err != nil {
|
||||
handlers.RespondWithAppError(c, apperrors.NewValidationError("invalid content ID"))
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
ContentType string `json:"content_type" binding:"required"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
handlers.RespondWithAppError(c, apperrors.NewValidationError("content_type is required"))
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.service.RestoreContent(c.Request.Context(), adminID, req.ContentType, contentID); err != nil {
|
||||
handlers.RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "failed to restore content", err))
|
||||
return
|
||||
}
|
||||
|
||||
handlers.RespondSuccess(c, http.StatusOK, gin.H{"status": "restored"})
|
||||
}
|
||||
|
||||
// --- F424: Payment Management ---
|
||||
|
||||
// GetPaymentOverview handles GET /api/v1/admin/platform/payments (F424)
|
||||
func (h *PlatformAdminHandler) GetPaymentOverview(c *gin.Context) {
|
||||
overview, err := h.service.GetPaymentOverview(c.Request.Context())
|
||||
if err != nil {
|
||||
handlers.RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "failed to get payment overview", err))
|
||||
return
|
||||
}
|
||||
|
||||
handlers.RespondSuccess(c, http.StatusOK, gin.H{
|
||||
"payments": overview,
|
||||
})
|
||||
}
|
||||
|
||||
// RefundOrder handles POST /api/v1/admin/platform/orders/:id/refund (F424)
|
||||
func (h *PlatformAdminHandler) RefundOrder(c *gin.Context) {
|
||||
adminID, ok := h.getAdminID(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
orderIDStr := c.Param("id")
|
||||
orderID, err := uuid.Parse(orderIDStr)
|
||||
if err != nil {
|
||||
handlers.RespondWithAppError(c, apperrors.NewValidationError("invalid order ID"))
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
Reason string `json:"reason" binding:"required"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
handlers.RespondWithAppError(c, apperrors.NewValidationError("reason is required"))
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.service.RefundOrder(c.Request.Context(), adminID, orderID, req.Reason); err != nil {
|
||||
if strings.Contains(err.Error(), "not found") || strings.Contains(err.Error(), "not eligible") {
|
||||
handlers.RespondWithAppError(c, apperrors.NewValidationError(err.Error()))
|
||||
return
|
||||
}
|
||||
handlers.RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "failed to refund order", err))
|
||||
return
|
||||
}
|
||||
|
||||
handlers.RespondSuccess(c, http.StatusOK, gin.H{"status": "refunded"})
|
||||
}
|
||||
132
veza-backend-api/internal/core/admin/handler_test.go
Normal file
132
veza-backend-api/internal/core/admin/handler_test.go
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
package admin
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func TestGetPlatformMetrics_NoAuth(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
handler := NewPlatformAdminHandler(nil, nil)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Request = httptest.NewRequest(http.MethodPost, "/admin/platform/users/invalid/suspend", strings.NewReader(`{"reason":"test"}`))
|
||||
c.Request.Header.Set("Content-Type", "application/json")
|
||||
c.Params = gin.Params{{Key: "userId", Value: "not-a-uuid"}}
|
||||
|
||||
// No user_id in context
|
||||
handler.SuspendUser(c)
|
||||
if w.Code != http.StatusUnauthorized {
|
||||
t.Errorf("expected 401, got %d", w.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetUserDetail_InvalidID(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
handler := NewPlatformAdminHandler(nil, nil)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Request = httptest.NewRequest(http.MethodGet, "/admin/platform/users/invalid", nil)
|
||||
c.Params = gin.Params{{Key: "userId", Value: "not-a-uuid"}}
|
||||
|
||||
handler.GetUserDetail(c)
|
||||
if w.Code != http.StatusBadRequest {
|
||||
t.Errorf("expected 400, got %d", w.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSuspendUser_InvalidUserID(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
handler := NewPlatformAdminHandler(nil, nil)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Request = httptest.NewRequest(http.MethodPost, "/admin/platform/users/invalid/suspend",
|
||||
strings.NewReader(`{"reason":"test"}`))
|
||||
c.Request.Header.Set("Content-Type", "application/json")
|
||||
c.Params = gin.Params{{Key: "userId", Value: "not-a-uuid"}}
|
||||
c.Set("user_id", uuid.MustParse("00000000-0000-0000-0000-000000000001"))
|
||||
|
||||
handler.SuspendUser(c)
|
||||
if w.Code != http.StatusBadRequest {
|
||||
t.Errorf("expected 400, got %d", w.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateUserRole_InvalidBody(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
handler := NewPlatformAdminHandler(nil, nil)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Request = httptest.NewRequest(http.MethodPut, "/admin/platform/users/00000000-0000-0000-0000-000000000001/role",
|
||||
strings.NewReader(`{}`))
|
||||
c.Request.Header.Set("Content-Type", "application/json")
|
||||
c.Params = gin.Params{{Key: "userId", Value: "00000000-0000-0000-0000-000000000001"}}
|
||||
|
||||
handler.UpdateUserRole(c)
|
||||
if w.Code != http.StatusBadRequest {
|
||||
t.Errorf("expected 400, got %d", w.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHideContent_InvalidContentID(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
handler := NewPlatformAdminHandler(nil, nil)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Request = httptest.NewRequest(http.MethodPost, "/admin/platform/content/bad-id/hide",
|
||||
strings.NewReader(`{"content_type":"track","reason":"test"}`))
|
||||
c.Request.Header.Set("Content-Type", "application/json")
|
||||
c.Params = gin.Params{{Key: "id", Value: "not-a-uuid"}}
|
||||
c.Set("user_id", uuid.MustParse("00000000-0000-0000-0000-000000000001"))
|
||||
|
||||
handler.HideContent(c)
|
||||
if w.Code != http.StatusBadRequest {
|
||||
t.Errorf("expected 400, got %d", w.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRefundOrder_InvalidOrderID(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
handler := NewPlatformAdminHandler(nil, nil)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Request = httptest.NewRequest(http.MethodPost, "/admin/platform/orders/bad/refund",
|
||||
strings.NewReader(`{"reason":"duplicate payment"}`))
|
||||
c.Request.Header.Set("Content-Type", "application/json")
|
||||
c.Params = gin.Params{{Key: "id", Value: "not-a-uuid"}}
|
||||
c.Set("user_id", uuid.MustParse("00000000-0000-0000-0000-000000000001"))
|
||||
|
||||
handler.RefundOrder(c)
|
||||
if w.Code != http.StatusBadRequest {
|
||||
t.Errorf("expected 400, got %d", w.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRefundOrder_MissingReason(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
handler := NewPlatformAdminHandler(nil, nil)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Request = httptest.NewRequest(http.MethodPost, "/admin/platform/orders/00000000-0000-0000-0000-000000000001/refund",
|
||||
strings.NewReader(`{}`))
|
||||
c.Request.Header.Set("Content-Type", "application/json")
|
||||
c.Params = gin.Params{{Key: "id", Value: "00000000-0000-0000-0000-000000000001"}}
|
||||
c.Set("user_id", uuid.MustParse("00000000-0000-0000-0000-000000000001"))
|
||||
|
||||
handler.RefundOrder(c)
|
||||
if w.Code != http.StatusBadRequest {
|
||||
t.Errorf("expected 400, got %d", w.Code)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue