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

751 lines
21 KiB
C#

using HoneyBox.Admin.Business.Models;
using HoneyBox.Admin.Business.Models.User;
using HoneyBox.Admin.Business.Services;
using HoneyBox.Model.Data;
using HoneyBox.Model.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Logging;
using Moq;
using Xunit;
namespace HoneyBox.Tests.Services;
/// <summary>
/// UserBusinessService 单元测试
/// </summary>
public class UserBusinessServiceTests : IDisposable
{
private readonly HoneyBoxDbContext _dbContext;
private readonly Mock<ILogger<UserBusinessService>> _mockLogger;
private readonly UserBusinessService _userService;
public UserBusinessServiceTests()
{
var options = new DbContextOptionsBuilder<HoneyBoxDbContext>()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.ConfigureWarnings(w => w.Ignore(InMemoryEventId.TransactionIgnoredWarning))
.Options;
_dbContext = new HoneyBoxDbContext(options);
_mockLogger = new Mock<ILogger<UserBusinessService>>();
_userService = new UserBusinessService(_dbContext, _mockLogger.Object);
}
public void Dispose()
{
_dbContext.Dispose();
}
#region Helper Methods
private async Task<User> CreateTestUserAsync(
string nickname = "TestUser",
decimal money = 100,
decimal integral = 50,
decimal score = 30,
byte status = 1,
int isTest = 0,
int pid = 0)
{
var user = new User
{
OpenId = Guid.NewGuid().ToString("N"),
Uid = $"UID{DateTime.Now.Ticks}",
Nickname = nickname,
HeadImg = "https://example.com/avatar.png",
Mobile = "13800138000",
Money = money,
Integral = integral,
Score = score,
Status = status,
IsTest = isTest,
Pid = pid,
Vip = 1,
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now
};
_dbContext.Users.Add(user);
await _dbContext.SaveChangesAsync();
return user;
}
private async Task<Coupon> CreateTestCouponAsync()
{
var coupon = new Coupon
{
Title = "测试优惠券",
Price = 10,
ManPrice = 100,
EffectiveDay = 30,
Status = 1,
CreatedAt = DateTime.Now
};
_dbContext.Coupons.Add(coupon);
await _dbContext.SaveChangesAsync();
return coupon;
}
private async Task<(Good goods, GoodsItem item)> CreateTestGoodsWithItemAsync()
{
var goods = new Good
{
Title = "测试盒子",
Price = 10,
ImgUrl = "https://example.com/goods.png",
ImgUrlDetail = "https://example.com/goods_detail.png",
Type = 1,
Status = 1,
Stock = 100,
SaleStock = 0,
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now
};
_dbContext.Goods.Add(goods);
await _dbContext.SaveChangesAsync();
var item = new GoodsItem
{
GoodsId = goods.Id,
Title = "测试奖品",
ImgUrl = "https://example.com/item.png",
Price = 50,
Money = 30,
Stock = 10,
SurplusStock = 10,
Type = 1,
ShangId = 1,
UpdatedAt = DateTime.Now
};
_dbContext.GoodsItems.Add(item);
await _dbContext.SaveChangesAsync();
return (goods, item);
}
private async Task<VipLevel> CreateTestVipLevelAsync()
{
var vipLevel = new VipLevel
{
Level = 1,
Title = "VIP1",
Number = 100,
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now
};
_dbContext.VipLevels.Add(vipLevel);
await _dbContext.SaveChangesAsync();
return vipLevel;
}
#endregion
#region GetUserListAsync Tests
[Fact]
public async Task GetUserListAsync_ReturnsPagedResult()
{
// Arrange
await CreateTestUserAsync("User1");
await CreateTestUserAsync("User2");
await CreateTestUserAsync("User3");
var request = new UserListRequest { Page = 1, PageSize = 2 };
// Act
var result = await _userService.GetUserListAsync(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);
}
[Fact]
public async Task GetUserListAsync_WithNicknameFilter_ReturnsFilteredResult()
{
// Arrange
await CreateTestUserAsync("Alice");
await CreateTestUserAsync("Bob");
await CreateTestUserAsync("AliceSmith");
var request = new UserListRequest { Nickname = "Alice" };
// Act
var result = await _userService.GetUserListAsync(request);
// Assert
Assert.Equal(2, result.Total);
Assert.All(result.List, u => Assert.Contains("Alice", u.Nickname!));
}
[Fact]
public async Task GetUserListAsync_WithParentIdFilter_ReturnsSubordinates()
{
// Arrange
var parent = await CreateTestUserAsync("Parent");
await CreateTestUserAsync("Child1", pid: parent.Id);
await CreateTestUserAsync("Child2", pid: parent.Id);
await CreateTestUserAsync("Other");
var request = new UserListRequest { ParentId = parent.Id };
// Act
var result = await _userService.GetUserListAsync(request);
// Assert
Assert.Equal(2, result.Total);
Assert.All(result.List, u => Assert.Equal(parent.Id, u.ParentId));
}
[Fact]
public async Task GetUserListAsync_WithDateRangeFilter_ReturnsFilteredResult()
{
// Arrange
var user1 = await CreateTestUserAsync("User1");
user1.CreatedAt = DateTime.Now.AddDays(-5);
var user2 = await CreateTestUserAsync("User2");
user2.CreatedAt = DateTime.Now.AddDays(-1);
await _dbContext.SaveChangesAsync();
var request = new UserListRequest
{
StartDate = DateTime.Now.AddDays(-3),
EndDate = DateTime.Now
};
// Act
var result = await _userService.GetUserListAsync(request);
// Assert
Assert.Equal(1, result.Total);
}
#endregion
#region GetUserDetailAsync Tests
[Fact]
public async Task GetUserDetailAsync_WithExistingUser_ReturnsDetail()
{
// Arrange
var user = await CreateTestUserAsync("DetailUser", money: 200, integral: 100, score: 50);
// Act
var result = await _userService.GetUserDetailAsync(user.Id);
// Assert
Assert.NotNull(result);
Assert.Equal(user.Id, result.Id);
Assert.Equal("DetailUser", result.Nickname);
Assert.Equal(200, result.Balance);
Assert.Equal(100, result.Integral);
Assert.Equal(50, result.Diamond);
}
[Fact]
public async Task GetUserDetailAsync_WithNonExistingUser_ReturnsNull()
{
// Act
var result = await _userService.GetUserDetailAsync(99999);
// Assert
Assert.Null(result);
}
#endregion
#region ChangeUserMoneyAsync Tests
[Fact]
public async Task ChangeUserMoneyAsync_AddBalance_IncreasesBalance()
{
// Arrange
var user = await CreateTestUserAsync(money: 100);
var request = new UserMoneyChangeRequest
{
Type = MoneyChangeType.Balance,
Amount = 50,
Operation = OperationType.Add,
Remark = "测试增加"
};
// Act
var result = await _userService.ChangeUserMoneyAsync(user.Id, request, 1);
// Assert
Assert.True(result);
var updatedUser = await _dbContext.Users.FindAsync(user.Id);
Assert.Equal(150, updatedUser!.Money);
// 验证记录了变动日志
var profitLog = await _dbContext.ProfitMoneys.FirstOrDefaultAsync(p => p.UserId == user.Id);
Assert.NotNull(profitLog);
Assert.Equal(50, profitLog.ChangeMoney);
}
[Fact]
public async Task ChangeUserMoneyAsync_SubtractBalance_DecreasesBalance()
{
// Arrange
var user = await CreateTestUserAsync(money: 100);
var request = new UserMoneyChangeRequest
{
Type = MoneyChangeType.Balance,
Amount = 30,
Operation = OperationType.Subtract
};
// Act
var result = await _userService.ChangeUserMoneyAsync(user.Id, request, 1);
// Assert
Assert.True(result);
var updatedUser = await _dbContext.Users.FindAsync(user.Id);
Assert.Equal(70, updatedUser!.Money);
}
[Fact]
public async Task ChangeUserMoneyAsync_SubtractMoreThanBalance_ThrowsException()
{
// Arrange
var user = await CreateTestUserAsync(money: 50);
var request = new UserMoneyChangeRequest
{
Type = MoneyChangeType.Balance,
Amount = 100,
Operation = OperationType.Subtract
};
// Act & Assert
var ex = await Assert.ThrowsAsync<BusinessException>(
() => _userService.ChangeUserMoneyAsync(user.Id, request, 1));
Assert.Equal(BusinessErrorCodes.ValidationFailed, ex.Code);
Assert.Contains("不足", ex.Message);
}
[Fact]
public async Task ChangeUserMoneyAsync_AddIntegral_IncreasesIntegral()
{
// Arrange
var user = await CreateTestUserAsync(integral: 50);
var request = new UserMoneyChangeRequest
{
Type = MoneyChangeType.Integral,
Amount = 25,
Operation = OperationType.Add
};
// Act
var result = await _userService.ChangeUserMoneyAsync(user.Id, request, 1);
// Assert
Assert.True(result);
var updatedUser = await _dbContext.Users.FindAsync(user.Id);
Assert.Equal(75, updatedUser!.Integral);
}
[Fact]
public async Task ChangeUserMoneyAsync_AddDiamond_IncreasesDiamond()
{
// Arrange
var user = await CreateTestUserAsync(score: 30);
var request = new UserMoneyChangeRequest
{
Type = MoneyChangeType.Diamond,
Amount = 20,
Operation = OperationType.Add
};
// Act
var result = await _userService.ChangeUserMoneyAsync(user.Id, request, 1);
// Assert
Assert.True(result);
var updatedUser = await _dbContext.Users.FindAsync(user.Id);
Assert.Equal(50, updatedUser!.Score);
}
[Fact]
public async Task ChangeUserMoneyAsync_NonExistingUser_ThrowsException()
{
// Arrange
var request = new UserMoneyChangeRequest
{
Type = MoneyChangeType.Balance,
Amount = 50,
Operation = OperationType.Add
};
// Act & Assert
var ex = await Assert.ThrowsAsync<BusinessException>(
() => _userService.ChangeUserMoneyAsync(99999, request, 1));
Assert.Equal(BusinessErrorCodes.NotFound, ex.Code);
}
#endregion
#region SetUserStatusAsync Tests
[Fact]
public async Task SetUserStatusAsync_BanUser_SetsStatusToDisabled()
{
// Arrange
var user = await CreateTestUserAsync(status: 1);
// Act
var result = await _userService.SetUserStatusAsync(user.Id, 0);
// Assert
Assert.True(result);
var updatedUser = await _dbContext.Users.FindAsync(user.Id);
Assert.Equal(0, updatedUser!.Status);
}
[Fact]
public async Task SetUserStatusAsync_UnbanUser_SetsStatusToEnabled()
{
// Arrange
var user = await CreateTestUserAsync(status: 0);
// Act
var result = await _userService.SetUserStatusAsync(user.Id, 1);
// Assert
Assert.True(result);
var updatedUser = await _dbContext.Users.FindAsync(user.Id);
Assert.Equal(1, updatedUser!.Status);
}
[Fact]
public async Task SetUserStatusAsync_NonExistingUser_ThrowsException()
{
// Act & Assert
var ex = await Assert.ThrowsAsync<BusinessException>(
() => _userService.SetUserStatusAsync(99999, 0));
Assert.Equal(BusinessErrorCodes.NotFound, ex.Code);
}
#endregion
#region SetTestAccountAsync Tests
[Fact]
public async Task SetTestAccountAsync_SetAsTest_UpdatesIsTest()
{
// Arrange
var user = await CreateTestUserAsync(isTest: 0);
// Act
var result = await _userService.SetTestAccountAsync(user.Id, 1);
// Assert
Assert.True(result);
var updatedUser = await _dbContext.Users.FindAsync(user.Id);
Assert.Equal(1, updatedUser!.IsTest);
}
[Fact]
public async Task SetTestAccountAsync_RemoveTest_UpdatesIsTest()
{
// Arrange
var user = await CreateTestUserAsync(isTest: 1);
// Act
var result = await _userService.SetTestAccountAsync(user.Id, 0);
// Assert
Assert.True(result);
var updatedUser = await _dbContext.Users.FindAsync(user.Id);
Assert.Equal(0, updatedUser!.IsTest);
}
#endregion
#region ClearMobileAsync Tests
[Fact]
public async Task ClearMobileAsync_ClearsMobileAndSavesOld()
{
// Arrange
var user = await CreateTestUserAsync();
var originalMobile = user.Mobile;
// Act
var result = await _userService.ClearMobileAsync(user.Id);
// Assert
Assert.True(result);
var updatedUser = await _dbContext.Users.FindAsync(user.Id);
Assert.Null(updatedUser!.Mobile);
Assert.Equal(originalMobile, updatedUser.OldMobile);
}
#endregion
#region ClearWeChatAsync Tests
[Fact]
public async Task ClearWeChatAsync_GeneratesNewOpenId()
{
// Arrange
var user = await CreateTestUserAsync();
var originalOpenId = user.OpenId;
// Act
var result = await _userService.ClearWeChatAsync(user.Id);
// Assert
Assert.True(result);
var updatedUser = await _dbContext.Users.FindAsync(user.Id);
Assert.NotEqual(originalOpenId, updatedUser!.OpenId);
Assert.StartsWith("cleared_", updatedUser.OpenId);
Assert.Null(updatedUser.UnionId);
Assert.Null(updatedUser.GzhOpenId);
}
#endregion
#region GiftCouponAsync Tests
[Fact]
public async Task GiftCouponAsync_CreatesCouponReceiveRecords()
{
// Arrange
var user = await CreateTestUserAsync();
var coupon = await CreateTestCouponAsync();
var request = new GiftCouponRequest
{
CouponId = coupon.Id,
Quantity = 3
};
// Act
var result = await _userService.GiftCouponAsync(user.Id, request);
// Assert
Assert.True(result);
var receives = await _dbContext.CouponReceives
.Where(r => r.UserId == user.Id && r.CouponId == coupon.Id)
.ToListAsync();
Assert.Equal(3, receives.Count);
Assert.All(receives, r =>
{
Assert.Equal(coupon.Title, r.Title);
Assert.Equal(coupon.Price, r.Price);
Assert.Equal((byte)1, r.Status);
Assert.Equal((byte?)0, r.State);
});
}
[Fact]
public async Task GiftCouponAsync_NonExistingUser_ThrowsException()
{
// Arrange
var coupon = await CreateTestCouponAsync();
var request = new GiftCouponRequest { CouponId = coupon.Id, Quantity = 1 };
// Act & Assert
var ex = await Assert.ThrowsAsync<BusinessException>(
() => _userService.GiftCouponAsync(99999, request));
Assert.Equal(BusinessErrorCodes.NotFound, ex.Code);
}
[Fact]
public async Task GiftCouponAsync_NonExistingCoupon_ThrowsException()
{
// Arrange
var user = await CreateTestUserAsync();
var request = new GiftCouponRequest { CouponId = 99999, Quantity = 1 };
// Act & Assert
var ex = await Assert.ThrowsAsync<BusinessException>(
() => _userService.GiftCouponAsync(user.Id, request));
Assert.Equal(BusinessErrorCodes.NotFound, ex.Code);
}
#endregion
#region GiftCardAsync Tests
[Fact]
public async Task GiftCardAsync_CreatesOrderAndOrderItems()
{
// Arrange
var user = await CreateTestUserAsync();
var (goods, item) = await CreateTestGoodsWithItemAsync();
var request = new GiftCardRequest
{
GoodsId = goods.Id,
GoodsListId = item.Id,
Quantity = 2
};
// Act
var result = await _userService.GiftCardAsync(user.Id, request);
// Assert
Assert.True(result);
// 验证订单
var order = await _dbContext.Orders
.FirstOrDefaultAsync(o => o.UserId == user.Id && o.OrderType == 9);
Assert.NotNull(order);
Assert.Equal(0, order.Price);
Assert.Equal(2, order.Num);
// 验证订单详情
var orderItems = await _dbContext.OrderItems
.Where(oi => oi.OrderId == order.Id)
.ToListAsync();
Assert.Equal(2, orderItems.Count);
Assert.All(orderItems, oi =>
{
Assert.Equal(item.Title, oi.GoodslistTitle);
Assert.Equal(9, oi.OrderType);
Assert.NotNull(oi.PrizeCode);
});
}
[Fact]
public async Task GiftCardAsync_NonExistingGoods_ThrowsException()
{
// Arrange
var user = await CreateTestUserAsync();
var request = new GiftCardRequest { GoodsId = 99999, GoodsListId = 1, Quantity = 1 };
// Act & Assert
var ex = await Assert.ThrowsAsync<BusinessException>(
() => _userService.GiftCardAsync(user.Id, request));
Assert.Equal(BusinessErrorCodes.NotFound, ex.Code);
}
#endregion
#region VIP Management Tests
[Fact]
public async Task GetVipLevelsAsync_ReturnsAllLevels()
{
// Arrange
await CreateTestVipLevelAsync();
var vip2 = new VipLevel
{
Level = 2,
Title = "VIP2",
Number = 500,
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now
};
_dbContext.VipLevels.Add(vip2);
await _dbContext.SaveChangesAsync();
// Act
var result = await _userService.GetVipLevelsAsync();
// Assert
Assert.Equal(2, result.Count);
Assert.Equal("VIP1", result[0].Title);
Assert.Equal("VIP2", result[1].Title);
}
[Fact]
public async Task UpdateVipLevelAsync_UpdatesLevel()
{
// Arrange
var vipLevel = await CreateTestVipLevelAsync();
var request = new VipLevelUpdateRequest
{
Title = "Updated VIP",
Number = 200
};
// Act
var result = await _userService.UpdateVipLevelAsync(vipLevel.Id, request);
// Assert
Assert.True(result);
var updated = await _dbContext.VipLevels.FindAsync(vipLevel.Id);
Assert.Equal("Updated VIP", updated!.Title);
Assert.Equal(200, updated.Number);
}
[Fact]
public async Task UpdateVipLevelAsync_NonExistingLevel_ThrowsException()
{
// Arrange
var request = new VipLevelUpdateRequest { Title = "Test", Number = 100 };
// Act & Assert
var ex = await Assert.ThrowsAsync<BusinessException>(
() => _userService.UpdateVipLevelAsync(99999, request));
Assert.Equal(BusinessErrorCodes.NotFound, ex.Code);
}
#endregion
#region GetSubordinateUsersAsync Tests
[Fact]
public async Task GetSubordinateUsersAsync_ReturnsSubordinates()
{
// Arrange
var parent = await CreateTestUserAsync("Parent");
await CreateTestUserAsync("Child1", pid: parent.Id);
await CreateTestUserAsync("Child2", pid: parent.Id);
// Act
var result = await _userService.GetSubordinateUsersAsync(parent.Id, 1, 10);
// Assert
Assert.Equal(2, result.Total);
Assert.Equal(2, result.List.Count);
Assert.All(result.List, u => Assert.Equal(parent.Id, u.ParentId));
}
[Fact]
public async Task GetSubordinateUsersAsync_NonExistingUser_ThrowsException()
{
// Act & Assert
var ex = await Assert.ThrowsAsync<BusinessException>(
() => _userService.GetSubordinateUsersAsync(99999, 1, 10));
Assert.Equal(BusinessErrorCodes.NotFound, ex.Code);
}
#endregion
#region GetUserProfitLossAsync Tests
[Fact]
public async Task GetUserProfitLossAsync_ReturnsStatistics()
{
// Arrange
var user = await CreateTestUserAsync(money: 100, integral: 50, score: 30);
// Act
var result = await _userService.GetUserProfitLossAsync(user.Id, null);
// Assert
Assert.Equal(user.Id, result.UserId);
Assert.Equal(100, result.CurrentBalance);
Assert.Equal(50, result.CurrentIntegral);
Assert.Equal(30, result.CurrentDiamond);
}
[Fact]
public async Task GetUserProfitLossAsync_NonExistingUser_ThrowsException()
{
// Act & Assert
var ex = await Assert.ThrowsAsync<BusinessException>(
() => _userService.GetUserProfitLossAsync(99999, null));
Assert.Equal(BusinessErrorCodes.NotFound, ex.Code);
}
#endregion
}