HaniBlindBox/server/HoneyBox/tests/HoneyBox.Tests/Services/OrderServiceTests.cs
2026-01-17 03:24:20 +08:00

677 lines
20 KiB
C#

using HoneyBox.Admin.Business.Models;
using HoneyBox.Admin.Business.Models.Order;
using HoneyBox.Admin.Business.Services;
using HoneyBox.Model.Data;
using HoneyBox.Model.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Moq;
using Xunit;
namespace HoneyBox.Tests.Services;
/// <summary>
/// OrderService 单元测试
/// </summary>
public class OrderServiceTests : IDisposable
{
private readonly HoneyBoxDbContext _dbContext;
private readonly Mock<ILogger<OrderService>> _mockLogger;
private readonly OrderService _orderService;
public OrderServiceTests()
{
// 使用 InMemory 数据库
var options = new DbContextOptionsBuilder<HoneyBoxDbContext>()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.Options;
_dbContext = new HoneyBoxDbContext(options);
_mockLogger = new Mock<ILogger<OrderService>>();
_orderService = new OrderService(_dbContext, _mockLogger.Object);
}
public void Dispose()
{
_dbContext.Dispose();
}
#region GetOrderListAsync Tests
[Fact]
public async Task GetOrderListAsync_WithNoFilters_ReturnsAllOrders()
{
// Arrange
await SeedOrderDataAsync();
var request = new OrderListRequest { Page = 1, PageSize = 10 };
// Act
var result = await _orderService.GetOrderListAsync(request);
// Assert
Assert.NotNull(result);
Assert.Equal(3, result.Total);
Assert.Equal(3, result.List.Count);
}
[Fact]
public async Task GetOrderListAsync_WithUserIdFilter_ReturnsFilteredOrders()
{
// Arrange
await SeedOrderDataAsync();
var request = new OrderListRequest { UserId = 1, Page = 1, PageSize = 10 };
// Act
var result = await _orderService.GetOrderListAsync(request);
// Assert
Assert.NotNull(result);
Assert.All(result.List, o => Assert.Equal(1, o.UserId));
}
[Fact]
public async Task GetOrderListAsync_WithOrderNumFilter_ReturnsFilteredOrders()
{
// Arrange
await SeedOrderDataAsync();
var request = new OrderListRequest { OrderNum = "ORD001", Page = 1, PageSize = 10 };
// Act
var result = await _orderService.GetOrderListAsync(request);
// Assert
Assert.NotNull(result);
Assert.Single(result.List);
Assert.Contains("ORD001", result.List[0].OrderNum);
}
[Fact]
public async Task GetOrderListAsync_WithStatusFilter_ReturnsFilteredOrders()
{
// Arrange
await SeedOrderDataAsync();
var request = new OrderListRequest { Status = 1, Page = 1, PageSize = 10 };
// Act
var result = await _orderService.GetOrderListAsync(request);
// Assert
Assert.NotNull(result);
Assert.All(result.List, o => Assert.Equal(1, o.Status));
}
[Fact]
public async Task GetOrderListAsync_WithDateRangeFilter_ReturnsFilteredOrders()
{
// Arrange
await SeedOrderDataAsync();
var request = new OrderListRequest
{
StartDate = DateTime.Today.AddDays(-1),
EndDate = DateTime.Today.AddDays(1),
Page = 1,
PageSize = 10
};
// Act
var result = await _orderService.GetOrderListAsync(request);
// Assert
Assert.NotNull(result);
Assert.True(result.Total > 0);
}
[Fact]
public async Task GetOrderListAsync_WithPagination_ReturnsCorrectPage()
{
// Arrange
await SeedOrderDataAsync();
var request = new OrderListRequest { Page = 1, PageSize = 2 };
// Act
var result = await _orderService.GetOrderListAsync(request);
// Assert
Assert.NotNull(result);
Assert.Equal(3, result.Total);
Assert.Equal(2, result.List.Count);
Assert.Equal(1, result.Page);
Assert.Equal(2, result.PageSize);
}
#endregion
#region GetOrderDetailAsync Tests
[Fact]
public async Task GetOrderDetailAsync_WithExistingOrder_ReturnsDetail()
{
// Arrange
await SeedOrderWithItemsAsync();
var order = await _dbContext.Orders.FirstAsync();
// Act
var result = await _orderService.GetOrderDetailAsync(order.Id);
// Assert
Assert.NotNull(result);
Assert.Equal(order.Id, result.Id);
Assert.Equal(order.OrderNum, result.OrderNum);
}
[Fact]
public async Task GetOrderDetailAsync_WithNonExistingOrder_ReturnsNull()
{
// Act
var result = await _orderService.GetOrderDetailAsync(99999);
// Assert
Assert.Null(result);
}
[Fact]
public async Task GetOrderDetailAsync_GroupsPrizesByPrizeCode()
{
// Arrange
await SeedOrderWithItemsAsync();
var order = await _dbContext.Orders.FirstAsync();
// Act
var result = await _orderService.GetOrderDetailAsync(order.Id);
// Assert
Assert.NotNull(result);
Assert.NotEmpty(result.PrizeGroups);
// Verify grouping
foreach (var group in result.PrizeGroups)
{
Assert.True(group.Count > 0);
Assert.NotEmpty(group.Items);
}
}
#endregion
#region GetStuckOrdersAsync Tests
[Fact]
public async Task GetStuckOrdersAsync_ReturnsOrdersWithPendingItems()
{
// Arrange
await SeedOrderWithItemsAsync();
var request = new OrderListRequest { Page = 1, PageSize = 10 };
// Act
var result = await _orderService.GetStuckOrdersAsync(request);
// Assert
Assert.NotNull(result);
// All returned orders should have pending items
foreach (var order in result.List)
{
var hasPendingItems = await _dbContext.OrderItems
.AnyAsync(oi => oi.OrderId == order.Id && oi.Status == 0);
Assert.True(hasPendingItems);
}
}
#endregion
#region GetShippingOrdersAsync Tests
[Fact]
public async Task GetShippingOrdersAsync_WithNoFilters_ReturnsAllShippingOrders()
{
// Arrange
await SeedShippingOrderDataAsync();
var request = new ShippingOrderListRequest { Page = 1, PageSize = 10 };
// Act
var result = await _orderService.GetShippingOrdersAsync(request);
// Assert
Assert.NotNull(result);
Assert.Equal(2, result.Total);
}
[Fact]
public async Task GetShippingOrdersAsync_WithStatusFilter_ReturnsFilteredOrders()
{
// Arrange
await SeedShippingOrderDataAsync();
var request = new ShippingOrderListRequest { Status = 1, Page = 1, PageSize = 10 };
// Act
var result = await _orderService.GetShippingOrdersAsync(request);
// Assert
Assert.NotNull(result);
Assert.All(result.List, s => Assert.Equal(1, s.Status));
}
#endregion
#region ShipOrderAsync Tests
[Fact]
public async Task ShipOrderAsync_WithValidOrder_UpdatesStatus()
{
// Arrange
await SeedShippingOrderDataAsync();
var shippingOrder = await _dbContext.OrderItemsSends.FirstAsync(s => s.Status == 1);
var request = new ShipOrderRequest
{
CourierName = "顺丰速运",
CourierNumber = "SF123456789"
};
// Act
var result = await _orderService.ShipOrderAsync(shippingOrder.Id, request, 1);
// Assert
Assert.True(result);
var updatedOrder = await _dbContext.OrderItemsSends.FindAsync(shippingOrder.Id);
Assert.Equal(2, updatedOrder!.Status); // 已发货
Assert.Equal("顺丰速运", updatedOrder.CourierName);
Assert.Equal("SF123456789", updatedOrder.CourierNumber);
}
[Fact]
public async Task ShipOrderAsync_WithNonExistingOrder_ThrowsException()
{
// Arrange
var request = new ShipOrderRequest
{
CourierName = "顺丰速运",
CourierNumber = "SF123456789"
};
// Act & Assert
var exception = await Assert.ThrowsAsync<BusinessException>(
() => _orderService.ShipOrderAsync(99999, request, 1));
Assert.Equal(BusinessErrorCodes.NotFound, exception.Code);
}
[Fact]
public async Task ShipOrderAsync_WithInvalidStatus_ThrowsException()
{
// Arrange
await SeedShippingOrderDataAsync();
var shippingOrder = await _dbContext.OrderItemsSends.FirstAsync(s => s.Status == 2); // 已发货
var request = new ShipOrderRequest
{
CourierName = "顺丰速运",
CourierNumber = "SF123456789"
};
// Act & Assert
var exception = await Assert.ThrowsAsync<BusinessException>(
() => _orderService.ShipOrderAsync(shippingOrder.Id, request, 1));
Assert.Equal(BusinessErrorCodes.ValidationFailed, exception.Code);
}
#endregion
#region CancelShippingOrderAsync Tests
[Fact]
public async Task CancelShippingOrderAsync_WithValidOrder_RestoresPrizes()
{
// Arrange
await SeedShippingOrderWithItemsAsync();
var shippingOrder = await _dbContext.OrderItemsSends.FirstAsync(s => s.Status == 1);
var itemsCount = await _dbContext.OrderItems.CountAsync(oi => oi.SendNum == shippingOrder.SendNum);
// Act
var result = await _orderService.CancelShippingOrderAsync(shippingOrder.Id, 1);
// Assert
Assert.True(result);
var updatedOrder = await _dbContext.OrderItemsSends.FindAsync(shippingOrder.Id);
Assert.Equal(4, updatedOrder!.Status); // 已取消
// Verify items are restored
var restoredItems = await _dbContext.OrderItems
.Where(oi => oi.SendNum == null && oi.Status == 0)
.CountAsync();
Assert.True(restoredItems >= itemsCount);
}
[Fact]
public async Task CancelShippingOrderAsync_WithNonExistingOrder_ThrowsException()
{
// Act & Assert
var exception = await Assert.ThrowsAsync<BusinessException>(
() => _orderService.CancelShippingOrderAsync(99999, 1));
Assert.Equal(BusinessErrorCodes.NotFound, exception.Code);
}
#endregion
#region ExportOrdersAsync Tests
[Fact]
public async Task ExportOrdersAsync_ReturnsValidCsvBytes()
{
// Arrange
await SeedOrderDataAsync();
var request = new OrderExportRequest();
// Act
var result = await _orderService.ExportOrdersAsync(request);
// Assert
Assert.NotNull(result);
Assert.True(result.Length > 0);
// Verify it's valid UTF-8 with BOM
Assert.Equal(0xEF, result[0]);
Assert.Equal(0xBB, result[1]);
Assert.Equal(0xBF, result[2]);
}
[Fact]
public async Task ExportOrdersAsync_WithFilters_ExportsFilteredData()
{
// Arrange
await SeedOrderDataAsync();
var request = new OrderExportRequest { Status = 1 };
// Act
var result = await _orderService.ExportOrdersAsync(request);
// Assert
Assert.NotNull(result);
Assert.True(result.Length > 0);
}
#endregion
#region Helper Methods
private async Task SeedUserDataAsync()
{
var users = new List<User>
{
new()
{
Id = 1,
Uid = "U001",
Nickname = "测试用户1",
Mobile = "13800138001",
OpenId = "openid1",
HeadImg = "http://test.com/head1.jpg",
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now
},
new()
{
Id = 2,
Uid = "U002",
Nickname = "测试用户2",
Mobile = "13800138002",
OpenId = "openid2",
HeadImg = "http://test.com/head2.jpg",
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now
}
};
_dbContext.Users.AddRange(users);
await _dbContext.SaveChangesAsync();
}
private async Task SeedOrderDataAsync()
{
await SeedUserDataAsync();
var orders = new List<Order>
{
new()
{
UserId = 1,
OrderNum = "ORD001",
OrderTotal = 100,
OrderZheTotal = 90,
Price = 90,
UseMoney = 0,
UseIntegral = 0,
UseScore = 0,
Zhe = 0.9m,
GoodsId = 1,
Num = 1,
GoodsPrice = 100,
GoodsTitle = "测试商品1",
PrizeNum = 1,
Status = 1,
Addtime = (int)DateTimeOffset.Now.ToUnixTimeSeconds(),
PayTime = (int)DateTimeOffset.Now.ToUnixTimeSeconds(),
PayType = 1,
OrderType = 0,
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now
},
new()
{
UserId = 1,
OrderNum = "ORD002",
OrderTotal = 200,
OrderZheTotal = 180,
Price = 180,
UseMoney = 0,
UseIntegral = 0,
UseScore = 0,
Zhe = 0.9m,
GoodsId = 2,
Num = 2,
GoodsPrice = 100,
GoodsTitle = "测试商品2",
PrizeNum = 2,
Status = 1,
Addtime = (int)DateTimeOffset.Now.ToUnixTimeSeconds(),
PayTime = (int)DateTimeOffset.Now.ToUnixTimeSeconds(),
PayType = 1,
OrderType = 0,
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now
},
new()
{
UserId = 2,
OrderNum = "ORD003",
OrderTotal = 50,
OrderZheTotal = 50,
Price = 50,
UseMoney = 0,
UseIntegral = 0,
UseScore = 0,
Zhe = 1,
GoodsId = 1,
Num = 1,
GoodsPrice = 50,
GoodsTitle = "测试商品3",
PrizeNum = 1,
Status = 0,
Addtime = (int)DateTimeOffset.Now.ToUnixTimeSeconds(),
PayTime = 0,
PayType = 0,
OrderType = 0,
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now
}
};
_dbContext.Orders.AddRange(orders);
await _dbContext.SaveChangesAsync();
}
private async Task SeedOrderWithItemsAsync()
{
await SeedOrderDataAsync();
var order = await _dbContext.Orders.FirstAsync();
var orderItems = new List<OrderItem>
{
new()
{
OrderId = order.Id,
UserId = order.UserId,
Status = 0, // 待处理
GoodsId = order.GoodsId,
Num = 1,
ShangId = 1,
GoodslistId = 1,
GoodslistTitle = "A赏",
GoodslistImgurl = "http://test.com/prize1.jpg",
GoodslistPrice = 500,
GoodslistMoney = 300,
GoodslistType = 1,
Addtime = (int)DateTimeOffset.Now.ToUnixTimeSeconds(),
PrizeCode = "PC001",
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now
},
new()
{
OrderId = order.Id,
UserId = order.UserId,
Status = 0, // 待处理
GoodsId = order.GoodsId,
Num = 2,
ShangId = 2,
GoodslistId = 2,
GoodslistTitle = "B赏",
GoodslistImgurl = "http://test.com/prize2.jpg",
GoodslistPrice = 200,
GoodslistMoney = 100,
GoodslistType = 1,
Addtime = (int)DateTimeOffset.Now.ToUnixTimeSeconds(),
PrizeCode = "PC002",
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now
},
new()
{
OrderId = order.Id,
UserId = order.UserId,
Status = 0, // 待处理
GoodsId = order.GoodsId,
Num = 3,
ShangId = 2,
GoodslistId = 2,
GoodslistTitle = "B赏",
GoodslistImgurl = "http://test.com/prize2.jpg",
GoodslistPrice = 200,
GoodslistMoney = 100,
GoodslistType = 1,
Addtime = (int)DateTimeOffset.Now.ToUnixTimeSeconds(),
PrizeCode = "PC002", // Same prize code for grouping test
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now
}
};
_dbContext.OrderItems.AddRange(orderItems);
await _dbContext.SaveChangesAsync();
}
private async Task SeedShippingOrderDataAsync()
{
await SeedUserDataAsync();
var shippingOrders = new List<OrderItemsSend>
{
new()
{
UserId = 1,
SendNum = "SEND001",
Freight = 10,
Status = 1, // 待发货
Count = 2,
Name = "张三",
Mobile = "13800138001",
Address = "北京市朝阳区xxx",
Addtime = (int)DateTimeOffset.Now.ToUnixTimeSeconds(),
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now
},
new()
{
UserId = 2,
SendNum = "SEND002",
Freight = 15,
Status = 2, // 已发货
Count = 1,
Name = "李四",
Mobile = "13800138002",
Address = "上海市浦东新区xxx",
CourierName = "顺丰速运",
CourierNumber = "SF987654321",
Addtime = (int)DateTimeOffset.Now.ToUnixTimeSeconds(),
SendTime = (int)DateTimeOffset.Now.ToUnixTimeSeconds(),
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now
}
};
_dbContext.OrderItemsSends.AddRange(shippingOrders);
await _dbContext.SaveChangesAsync();
}
private async Task SeedShippingOrderWithItemsAsync()
{
await SeedShippingOrderDataAsync();
var shippingOrder = await _dbContext.OrderItemsSends.FirstAsync(s => s.Status == 1);
var orderItems = new List<OrderItem>
{
new()
{
OrderId = 1,
UserId = shippingOrder.UserId,
SendNum = shippingOrder.SendNum,
Status = 2, // 已发货状态
GoodsId = 1,
Num = 1,
ShangId = 1,
GoodslistId = 1,
GoodslistTitle = "A赏",
GoodslistImgurl = "http://test.com/prize1.jpg",
GoodslistPrice = 500,
GoodslistMoney = 300,
GoodslistType = 1,
Addtime = (int)DateTimeOffset.Now.ToUnixTimeSeconds(),
PrizeCode = "PC001",
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now
},
new()
{
OrderId = 1,
UserId = shippingOrder.UserId,
SendNum = shippingOrder.SendNum,
Status = 2, // 已发货状态
GoodsId = 1,
Num = 2,
ShangId = 2,
GoodslistId = 2,
GoodslistTitle = "B赏",
GoodslistImgurl = "http://test.com/prize2.jpg",
GoodslistPrice = 200,
GoodslistMoney = 100,
GoodslistType = 1,
Addtime = (int)DateTimeOffset.Now.ToUnixTimeSeconds(),
PrizeCode = "PC002",
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now
}
};
_dbContext.OrderItems.AddRange(orderItems);
await _dbContext.SaveChangesAsync();
}
#endregion
}