HaniBlindBox/server/HoneyBox/tests/HoneyBox.Tests/Integration/CouponServiceIntegrationTests.cs
2026-01-04 01:47:02 +08:00

301 lines
13 KiB
C#

using HoneyBox.Core.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.Integration;
/// <summary>
/// 优惠券服务集成测试
/// Requirements: 3.1-7.5
/// </summary>
public class CouponServiceIntegrationTests
{
private HoneyBoxDbContext CreateInMemoryDbContext()
{
var options = new DbContextOptionsBuilder<HoneyBoxDbContext>()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.ConfigureWarnings(w => w.Ignore(InMemoryEventId.TransactionIgnoredWarning))
.Options;
return new HoneyBoxDbContext(options);
}
private CouponService CreateCouponService(HoneyBoxDbContext dbContext)
{
var mockLogger = new Mock<ILogger<CouponService>>();
return new CouponService(dbContext, mockLogger.Object);
}
private User CreateTestUser(int id, decimal integral = 1000)
{
return new User
{
Id = id,
OpenId = $"openid_{Guid.NewGuid():N}",
Uid = $"uid_{Guid.NewGuid():N}",
Nickname = "测试用户",
HeadImg = "",
Integral = integral,
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now
};
}
[Fact]
public async Task GetCouponList_UnusedStatus_ReturnsUnusedCoupons()
{
var dbContext = CreateInMemoryDbContext();
var service = CreateCouponService(dbContext);
var userId = 1;
await dbContext.UserCoupons.AddRangeAsync(new List<UserCoupon>
{
new() { UserId = userId, Level = 3, Num = 100, Status = 1, Type = 1, Title = "高级赏券", FromId = "0", CreatedAt = DateTime.Now },
new() { UserId = userId, Level = 4, Num = 50, Status = 1, Type = 1, Title = "普通赏券", FromId = "0", CreatedAt = DateTime.Now },
new() { UserId = userId, Level = 3, Num = 80, Status = 2, Type = 1, Title = "已分享券", FromId = "0", ShareTime = DateTime.Now, CreatedAt = DateTime.Now }
});
await dbContext.Users.AddAsync(CreateTestUser(userId));
await dbContext.SaveChangesAsync();
var result = await service.GetCouponListAsync(userId, 1, 1, 15);
Assert.Equal(2, result.List.Count);
}
[Fact]
public async Task GetCouponList_SharedStatus_ReturnsSharedCoupons()
{
var dbContext = CreateInMemoryDbContext();
var service = CreateCouponService(dbContext);
var userId = 1;
await dbContext.UserCoupons.AddRangeAsync(new List<UserCoupon>
{
new() { UserId = userId, Level = 3, Num = 100, Status = 1, Type = 1, Title = "未分享券", FromId = "0", CreatedAt = DateTime.Now },
new() { UserId = userId, Level = 3, Num = 80, Status = 2, Type = 1, Title = "已分享券1", FromId = "0", ShareTime = DateTime.Now.AddHours(-1), CreatedAt = DateTime.Now },
new() { UserId = userId, Level = 4, Num = 60, Status = 2, Type = 1, Title = "已分享券2", FromId = "0", ShareTime = DateTime.Now, CreatedAt = DateTime.Now }
});
await dbContext.Users.AddAsync(CreateTestUser(userId));
await dbContext.SaveChangesAsync();
var result = await service.GetCouponListAsync(userId, 2, 1, 15);
Assert.Equal(2, result.List.Count);
}
[Theory]
[InlineData(1, "特级赏券")]
[InlineData(2, "终极赏券")]
[InlineData(3, "高级赏券")]
[InlineData(4, "普通赏券")]
public async Task GetCouponList_LevelMapping_ReturnsCorrectLevelText(int level, string expectedText)
{
var dbContext = CreateInMemoryDbContext();
var service = CreateCouponService(dbContext);
var userId = 1;
await dbContext.UserCoupons.AddAsync(new UserCoupon { UserId = userId, Level = level, Num = 100, Status = 1, Type = 1, Title = "测试券", FromId = "0", CreatedAt = DateTime.Now });
await dbContext.Users.AddAsync(CreateTestUser(userId));
await dbContext.SaveChangesAsync();
var result = await service.GetCouponListAsync(userId, 1, 1, 15);
Assert.Single(result.List);
Assert.Equal(expectedText, result.List[0].LevelText);
}
[Fact]
public async Task GetCouponList_ReturnsStatistics()
{
var dbContext = CreateInMemoryDbContext();
var service = CreateCouponService(dbContext);
var userId = 1;
await dbContext.Users.AddAsync(CreateTestUser(userId, 500));
await dbContext.SaveChangesAsync();
var result = await service.GetCouponListAsync(userId, 1, 1, 15);
Assert.Equal(50, result.ZCount);
Assert.Equal(20, result.KeHcCount);
Assert.Equal("10%", result.SunHao);
Assert.Equal(500, result.UserIntegral);
}
[Fact]
public async Task ShareCoupon_Success_UpdatesStatusAndShareTime()
{
var dbContext = CreateInMemoryDbContext();
var service = CreateCouponService(dbContext);
var userId = 1;
var coupon = new UserCoupon { UserId = userId, Level = 3, Num = 100, Status = 1, Type = 1, Title = "测试券", FromId = "0", CreatedAt = DateTime.Now };
await dbContext.UserCoupons.AddAsync(coupon);
await dbContext.SaveChangesAsync();
var result = await service.ShareCouponAsync(userId, coupon.Id);
Assert.True(result);
var updatedCoupon = await dbContext.UserCoupons.FindAsync(coupon.Id);
Assert.Equal(2, updatedCoupon!.Status);
Assert.NotNull(updatedCoupon.ShareTime);
}
[Fact]
public async Task ShareCoupon_NotOwner_ThrowsException()
{
var dbContext = CreateInMemoryDbContext();
var service = CreateCouponService(dbContext);
var coupon = new UserCoupon { UserId = 1, Level = 3, Num = 100, Status = 1, Type = 1, Title = "测试券", FromId = "0", CreatedAt = DateTime.Now };
await dbContext.UserCoupons.AddAsync(coupon);
await dbContext.SaveChangesAsync();
await Assert.ThrowsAsync<InvalidOperationException>(() => service.ShareCouponAsync(2, coupon.Id));
}
[Fact]
public async Task ClaimCoupon_OwnCoupon_ThrowsException()
{
var dbContext = CreateInMemoryDbContext();
var service = CreateCouponService(dbContext);
var userId = 1;
var coupon = new UserCoupon { UserId = userId, Level = 3, Num = 100, Status = 2, Type = 1, Title = "测试券", FromId = "0", Other = 20, KlNum = 5, CreatedAt = DateTime.Now };
await dbContext.UserCoupons.AddAsync(coupon);
await dbContext.SaveChangesAsync();
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => service.ClaimCouponAsync(userId, coupon.Id));
Assert.Equal("请勿开启自己的劵", ex.Message);
}
[Fact]
public async Task ClaimCoupon_AlreadyClaimed_ThrowsException()
{
var dbContext = CreateInMemoryDbContext();
var service = CreateCouponService(dbContext);
var coupon = new UserCoupon { UserId = 1, Level = 3, Num = 100, Status = 2, Type = 1, Title = "测试券", FromId = "0", Other = 20, KlNum = 5, CreatedAt = DateTime.Now };
await dbContext.UserCoupons.AddAsync(coupon);
await dbContext.SaveChangesAsync();
var claimRecord = new UserCoupon { UserId = 2, Level = 3, Num = 0, LNum = 5, Status = 3, Type = 2, Title = "测试券", FromId = coupon.Id.ToString(), CreatedAt = DateTime.Now };
await dbContext.UserCoupons.AddAsync(claimRecord);
await dbContext.SaveChangesAsync();
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => service.ClaimCouponAsync(2, coupon.Id));
Assert.Equal("你已经领取过了", ex.Message);
}
[Fact]
public async Task ClaimCoupon_NoRemainingAmount_ThrowsException()
{
var dbContext = CreateInMemoryDbContext();
var service = CreateCouponService(dbContext);
var coupon = new UserCoupon { UserId = 1, Level = 3, Num = 100, Status = 2, Type = 1, Title = "测试券", FromId = "0", Other = 0, KlNum = 0, CreatedAt = DateTime.Now };
await dbContext.UserCoupons.AddAsync(coupon);
await dbContext.SaveChangesAsync();
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => service.ClaimCouponAsync(2, coupon.Id));
Assert.Equal("来晚了, 已经被人领完了", ex.Message);
}
[Fact]
public async Task CalculateSynthesis_AppliesLossRate()
{
var dbContext = CreateInMemoryDbContext();
var service = CreateCouponService(dbContext);
var userId = 1;
var coupons = new List<UserCoupon>
{
new() { UserId = userId, Level = 4, Num = 100, Status = 1, Type = 1, Title = "普通赏券1", FromId = "0", CreatedAt = DateTime.Now },
new() { UserId = userId, Level = 4, Num = 100, Status = 1, Type = 1, Title = "普通赏券2", FromId = "0", CreatedAt = DateTime.Now }
};
await dbContext.UserCoupons.AddRangeAsync(coupons);
await dbContext.SaveChangesAsync();
var result = await service.CalculateSynthesisAsync(userId, string.Join(",", coupons.Select(c => c.Id)));
Assert.Equal(200, result.SumNum);
Assert.Equal(180, result.ShNum);
}
[Theory]
[InlineData(1)]
[InlineData(2)]
public async Task CalculateSynthesis_HighLevelCoupon_ThrowsException(int level)
{
var dbContext = CreateInMemoryDbContext();
var service = CreateCouponService(dbContext);
var userId = 1;
var coupon = new UserCoupon { UserId = userId, Level = level, Num = 5000, Status = 1, Type = 1, Title = "高级券", FromId = "0", CreatedAt = DateTime.Now };
await dbContext.UserCoupons.AddAsync(coupon);
await dbContext.SaveChangesAsync();
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => service.CalculateSynthesisAsync(userId, coupon.Id.ToString()));
Assert.Equal("特级,终极赏券不能合成", ex.Message);
}
[Fact]
public async Task CalculateSynthesis_ExceedsMaxCount_ThrowsException()
{
var dbContext = CreateInMemoryDbContext();
var service = CreateCouponService(dbContext);
var userId = 1;
var coupons = Enumerable.Range(1, 21).Select(i => new UserCoupon { UserId = userId, Level = 4, Num = 10, Status = 1, Type = 1, Title = $"普通赏券{i}", FromId = "0", CreatedAt = DateTime.Now }).ToList();
await dbContext.UserCoupons.AddRangeAsync(coupons);
await dbContext.SaveChangesAsync();
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => service.CalculateSynthesisAsync(userId, string.Join(",", coupons.Select(c => c.Id))));
Assert.Equal("最多只能20个合成", ex.Message);
}
[Fact]
public async Task SynthesisCoupons_Success_CreatesNewCoupon()
{
var dbContext = CreateInMemoryDbContext();
var service = CreateCouponService(dbContext);
var userId = 1;
var coupons = new List<UserCoupon>
{
new() { UserId = userId, Level = 4, Num = 300, Status = 1, Type = 1, Title = "普通赏券1", FromId = "0", CreatedAt = DateTime.Now },
new() { UserId = userId, Level = 4, Num = 300, Status = 1, Type = 1, Title = "普通赏券2", FromId = "0", CreatedAt = DateTime.Now }
};
await dbContext.UserCoupons.AddRangeAsync(coupons);
await dbContext.SaveChangesAsync();
var result = await service.SynthesisCouponsAsync(userId, string.Join(",", coupons.Select(c => c.Id)));
Assert.True(result);
var originalCoupons = await dbContext.UserCoupons.Where(c => coupons.Select(x => x.Id).Contains(c.Id)).ToListAsync();
Assert.All(originalCoupons, c => Assert.Equal(4, c.Status));
var newCoupon = await dbContext.UserCoupons.FirstOrDefaultAsync(c => c.UserId == userId && c.Type == 3);
Assert.NotNull(newCoupon);
Assert.Equal(1, newCoupon.Status);
Assert.Equal(3, newCoupon.Level);
}
[Theory]
[InlineData(5000, 1)]
[InlineData(2000, 2)]
[InlineData(500, 3)]
[InlineData(100, 4)]
public async Task CalculateSynthesis_CalculatesCorrectLevel(int totalNum, int expectedLevel)
{
var dbContext = CreateInMemoryDbContext();
var service = CreateCouponService(dbContext);
var userId = 1;
var coupon = new UserCoupon { UserId = userId, Level = 4, Num = totalNum, Status = 1, Type = 1, Title = "普通赏券", FromId = "0", CreatedAt = DateTime.Now };
await dbContext.UserCoupons.AddAsync(coupon);
await dbContext.SaveChangesAsync();
var result = await service.CalculateSynthesisAsync(userId, coupon.Id.ToString());
Assert.Equal(expectedLevel, result.Coupon.Level);
}
}