301 lines
13 KiB
C#
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);
|
|
}
|
|
}
|