package test import ( "bytes" "encoding/json" "errors" "net/http" "net/http/httptest" "testing" "github.com/gin-gonic/gin" "github.com/rovina/zsp-backend/internal/handler" "github.com/rovina/zsp-backend/internal/model" "github.com/rovina/zsp-backend/internal/service" "gorm.io/gorm" ) // mockDeliveryService implements service.DeliveryService for testing type mockDeliveryService struct { listApplyFn func(page, pageSize int, orderNumber, blNumber, status string) ([]model.DeliveryApply, int64, error) getApplyByIDFn func(id uint) (*model.DeliveryApply, error) createApplyFn func(e *model.DeliveryApply) error deleteApplyFn func(id uint) error listOutboundFn func(page, pageSize int, outboundNumber, blNumber, status string) ([]model.DeliveryOutbound, int64, error) getOutboundByIDFn func(id uint) (*model.DeliveryOutbound, error) createOutboundFn func(e *model.DeliveryOutbound) error deleteOutboundFn func(id uint) error listWarehousesFn func() ([]model.Warehouse, error) createWarehouseFn func(e *model.Warehouse) error deleteWarehouseFn func(id uint) error listInboundFn func(page, pageSize int, warehouse, commodity string) ([]model.InventoryInbound, int64, error) createInboundFn func(e *model.InventoryInbound) error deleteInboundFn func(id uint) error listOwnershipFn func(page, pageSize int, transferNumber, blNumber, status string) ([]model.OwnershipTransfer, int64, error) deleteOwnershipFn func(id uint) error dbFn func() *gorm.DB } func (m *mockDeliveryService) ListApply(page, pageSize int, orderNumber, blNumber, status string) ([]model.DeliveryApply, int64, error) { return m.listApplyFn(page, pageSize, orderNumber, blNumber, status) } func (m *mockDeliveryService) GetApplyByID(id uint) (*model.DeliveryApply, error) { return m.getApplyByIDFn(id) } func (m *mockDeliveryService) CreateApply(e *model.DeliveryApply) error { return m.createApplyFn(e) } func (m *mockDeliveryService) UpdateApply(e *model.DeliveryApply) error { return nil } func (m *mockDeliveryService) DeleteApply(id uint) error { return m.deleteApplyFn(id) } func (m *mockDeliveryService) ListOutbound(page, pageSize int, outboundNumber, blNumber, status string) ([]model.DeliveryOutbound, int64, error) { return m.listOutboundFn(page, pageSize, outboundNumber, blNumber, status) } func (m *mockDeliveryService) GetOutboundByID(id uint) (*model.DeliveryOutbound, error) { return m.getOutboundByIDFn(id) } func (m *mockDeliveryService) CreateOutbound(e *model.DeliveryOutbound) error { return m.createOutboundFn(e) } func (m *mockDeliveryService) UpdateOutbound(e *model.DeliveryOutbound) error { return nil } func (m *mockDeliveryService) DeleteOutbound(id uint) error { return m.deleteOutboundFn(id) } func (m *mockDeliveryService) ListWarehouses() ([]model.Warehouse, error) { return m.listWarehousesFn() } func (m *mockDeliveryService) GetWarehouseByID(id uint) (*model.Warehouse, error) { return nil, nil } func (m *mockDeliveryService) CreateWarehouse(e *model.Warehouse) error { return m.createWarehouseFn(e) } func (m *mockDeliveryService) UpdateWarehouse(e *model.Warehouse) error { return nil } func (m *mockDeliveryService) DeleteWarehouse(id uint) error { return m.deleteWarehouseFn(id) } func (m *mockDeliveryService) ListInbound(page, pageSize int, warehouse, commodity string) ([]model.InventoryInbound, int64, error) { return m.listInboundFn(page, pageSize, warehouse, commodity) } func (m *mockDeliveryService) GetInboundByID(id uint) (*model.InventoryInbound, error) { return nil, nil } func (m *mockDeliveryService) CreateInbound(e *model.InventoryInbound) error { return m.createInboundFn(e) } func (m *mockDeliveryService) UpdateInbound(e *model.InventoryInbound) error { return nil } func (m *mockDeliveryService) DeleteInbound(id uint) error { return m.deleteInboundFn(id) } func (m *mockDeliveryService) ListOwnership(page, pageSize int, transferNumber, blNumber, status string) ([]model.OwnershipTransfer, int64, error) { return m.listOwnershipFn(page, pageSize, transferNumber, blNumber, status) } func (m *mockDeliveryService) GetOwnershipByID(id uint) (*model.OwnershipTransfer, error) { return nil, nil } func (m *mockDeliveryService) CreateOwnership(e *model.OwnershipTransfer) error { return nil } func (m *mockDeliveryService) DeleteOwnership(id uint) error { return m.deleteOwnershipFn(id) } func (m *mockDeliveryService) CreateOwnershipFiles(files []model.OwnershipTransferFile) error { return nil } func (m *mockDeliveryService) DB() *gorm.DB { return nil } var _ service.DeliveryService = (*mockDeliveryService)(nil) func setupDeliveryRouter(h *handler.DeliveryHandler) *gin.Engine { gin.SetMode(gin.TestMode) r := gin.New() api := r.Group("/api") { applyDelivery := api.Group("/apply-delivery") { applyDelivery.GET("", h.ListApply) applyDelivery.POST("", h.CreateApply) applyDelivery.DELETE("/:id", h.DeleteApply) } deliveryDetails := api.Group("/delivery-details") { deliveryDetails.GET("", h.ListOutbound) deliveryDetails.POST("", h.CreateOutbound) deliveryDetails.DELETE("/:id", h.DeleteOutbound) } inventory := api.Group("/inventory") { inventory.GET("/warehouses", h.ListWarehouses) inventory.POST("/warehouses", h.CreateWarehouse) inventory.DELETE("/warehouses/:id", h.DeleteWarehouse) inventory.POST("/inbound", h.CreateInbound) inventory.DELETE("/inbound/:id", h.DeleteInbound) } ownershipTransfer := api.Group("/ownership-transfer") { ownershipTransfer.GET("", h.ListOwnership) ownershipTransfer.DELETE("/:id", h.DeleteOwnership) } } return r } // ========== 我要提货 /api/apply-delivery ========== func TestDeliveryListApply_Success(t *testing.T) { mock := &mockDeliveryService{ listApplyFn: func(page, pageSize int, orderNumber, blNumber, status string) ([]model.DeliveryApply, int64, error) { return []model.DeliveryApply{ {ID: 1, OrderNumber: "DA001", Status: "pending"}, {ID: 2, OrderNumber: "DA002", Status: "approved"}, }, 2, nil }, } h := handler.NewDeliveryHandler(mock) router := setupDeliveryRouter(h) w := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/api/apply-delivery?page=1&pageSize=10", nil) router.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("expected status 200, got %d", w.Code) } var resp map[string]interface{} json.Unmarshal(w.Body.Bytes(), &resp) if resp["success"] != true { t.Errorf("expected success true, got %v", resp["success"]) } } func TestDeliveryCreateApply_Success(t *testing.T) { mock := &mockDeliveryService{ createApplyFn: func(e *model.DeliveryApply) error { e.ID = 1 e.OrderNumber = "DA1234567890" return nil }, } h := handler.NewDeliveryHandler(mock) router := setupDeliveryRouter(h) body := `{"blNumber":"BL001","commodity":"燕麦","quantity":100}` w := httptest.NewRecorder() req, _ := http.NewRequest("POST", "/api/apply-delivery", bytes.NewReader([]byte(body))) req.Header.Set("Content-Type", "application/json") router.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("expected status 200, got %d", w.Code) } } func TestDeliveryDeleteApply_Success(t *testing.T) { mock := &mockDeliveryService{ deleteApplyFn: func(id uint) error { return nil }, } h := handler.NewDeliveryHandler(mock) router := setupDeliveryRouter(h) w := httptest.NewRecorder() req, _ := http.NewRequest("DELETE", "/api/apply-delivery/1", nil) router.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("expected status 200, got %d", w.Code) } } func TestDeliveryDeleteApply_NotFound(t *testing.T) { mock := &mockDeliveryService{ deleteApplyFn: func(id uint) error { return errors.New("not found") }, } h := handler.NewDeliveryHandler(mock) router := setupDeliveryRouter(h) w := httptest.NewRecorder() req, _ := http.NewRequest("DELETE", "/api/apply-delivery/999", nil) router.ServeHTTP(w, req) if w.Code != http.StatusInternalServerError { t.Errorf("expected status 500, got %d", w.Code) } } // ========== 提货明细 /api/delivery-details ========== func TestDeliveryListOutbound_Success(t *testing.T) { mock := &mockDeliveryService{ listOutboundFn: func(page, pageSize int, outboundNumber, blNumber, status string) ([]model.DeliveryOutbound, int64, error) { return []model.DeliveryOutbound{ {ID: 1, OutboundNumber: "OB001", OutboundStatus: "completed"}, }, 1, nil }, } h := handler.NewDeliveryHandler(mock) router := setupDeliveryRouter(h) w := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/api/delivery-details?page=1&pageSize=10", nil) router.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("expected status 200, got %d", w.Code) } } func TestDeliveryCreateOutbound_Success(t *testing.T) { mock := &mockDeliveryService{ createOutboundFn: func(e *model.DeliveryOutbound) error { e.ID = 1 e.OutboundNumber = "OB1234567890" return nil }, } h := handler.NewDeliveryHandler(mock) router := setupDeliveryRouter(h) body := `{"blNumber":"BL001","warehouse":"A库","quantity":50}` w := httptest.NewRecorder() req, _ := http.NewRequest("POST", "/api/delivery-details", bytes.NewReader([]byte(body))) req.Header.Set("Content-Type", "application/json") router.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("expected status 200, got %d", w.Code) } } func TestDeliveryDeleteOutbound_Success(t *testing.T) { mock := &mockDeliveryService{ deleteOutboundFn: func(id uint) error { return nil }, } h := handler.NewDeliveryHandler(mock) router := setupDeliveryRouter(h) w := httptest.NewRecorder() req, _ := http.NewRequest("DELETE", "/api/delivery-details/1", nil) router.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("expected status 200, got %d", w.Code) } } // ========== 库存 /api/inventory ========== func TestDeliveryListWarehouses_Success(t *testing.T) { mock := &mockDeliveryService{ listWarehousesFn: func() ([]model.Warehouse, error) { return []model.Warehouse{ {ID: 1, Name: "A库", Status: "active"}, {ID: 2, Name: "B库", Status: "active"}, }, nil }, } h := handler.NewDeliveryHandler(mock) router := setupDeliveryRouter(h) w := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/api/inventory/warehouses", nil) router.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("expected status 200, got %d", w.Code) } } func TestDeliveryCreateWarehouse_Success(t *testing.T) { mock := &mockDeliveryService{ createWarehouseFn: func(e *model.Warehouse) error { e.ID = 1 return nil }, } h := handler.NewDeliveryHandler(mock) router := setupDeliveryRouter(h) body := `{"name":"C库","location":"广州"}` w := httptest.NewRecorder() req, _ := http.NewRequest("POST", "/api/inventory/warehouses", bytes.NewReader([]byte(body))) req.Header.Set("Content-Type", "application/json") router.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("expected status 200, got %d", w.Code) } } func TestDeliveryDeleteWarehouse_Success(t *testing.T) { mock := &mockDeliveryService{ deleteWarehouseFn: func(id uint) error { return nil }, } h := handler.NewDeliveryHandler(mock) router := setupDeliveryRouter(h) w := httptest.NewRecorder() req, _ := http.NewRequest("DELETE", "/api/inventory/warehouses/1", nil) router.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("expected status 200, got %d", w.Code) } } func TestDeliveryCreateInbound_Success(t *testing.T) { mock := &mockDeliveryService{ createInboundFn: func(e *model.InventoryInbound) error { e.ID = 1 e.InboundNumber = "IN1234567890" return nil }, } h := handler.NewDeliveryHandler(mock) router := setupDeliveryRouter(h) body := `{"warehouse":"A库","commodity":"燕麦","inboundWeight":1000}` w := httptest.NewRecorder() req, _ := http.NewRequest("POST", "/api/inventory/inbound", bytes.NewReader([]byte(body))) req.Header.Set("Content-Type", "application/json") router.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("expected status 200, got %d", w.Code) } } // ========== 货权转移 /api/ownership-transfer ========== func TestDeliveryListOwnership_Success(t *testing.T) { mock := &mockDeliveryService{ listOwnershipFn: func(page, pageSize int, transferNumber, blNumber, status string) ([]model.OwnershipTransfer, int64, error) { return []model.OwnershipTransfer{ {ID: 1, TransferNumber: "OT001", Status: "draft"}, }, 1, nil }, } h := handler.NewDeliveryHandler(mock) router := setupDeliveryRouter(h) w := httptest.NewRecorder() req, _ := http.NewRequest("GET", "/api/ownership-transfer?page=1&pageSize=10", nil) router.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("expected status 200, got %d", w.Code) } } func TestDeliveryDeleteOwnership_Success(t *testing.T) { mock := &mockDeliveryService{ deleteOwnershipFn: func(id uint) error { return nil }, } h := handler.NewDeliveryHandler(mock) router := setupDeliveryRouter(h) w := httptest.NewRecorder() req, _ := http.NewRequest("DELETE", "/api/ownership-transfer/1", nil) router.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("expected status 200, got %d", w.Code) } }