using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using MiAssessment.Admin.Business.Data; using MiAssessment.Admin.Business.Entities; using MiAssessment.Admin.Business.Models; using MiAssessment.Admin.Business.Models.AssessmentRecord; using MiAssessment.Admin.Business.Models.Common; using MiAssessment.Admin.Business.Services; using Moq; using Xunit; namespace MiAssessment.Tests.Admin; /// /// 测评记录服务单元测试 /// 验证测评记录服务的边界条件和错误处理 /// public class AssessmentRecordServiceTests { private readonly Mock> _mockLogger = new(); #region 测试记录不存在返回错误 (Requirements 2.4) /// /// 测试 GetRecordDetailAsync 当记录 ID 不存在时返回 null /// **Validates: Requirements 2.4** /// [Fact] public async Task GetRecordDetailAsync_WithNonExistentId_ReturnsNull() { // Arrange using var dbContext = CreateDbContext(); var service = new AssessmentRecordService(dbContext, _mockLogger.Object); var nonExistentId = 99999L; // Act var result = await service.GetRecordDetailAsync(nonExistentId); // Assert Assert.Null(result); } /// /// 测试 GetRecordDetailAsync 当记录已软删除时返回 null /// **Validates: Requirements 2.4** /// [Fact] public async Task GetRecordDetailAsync_WithSoftDeletedRecord_ReturnsNull() { // Arrange using var dbContext = CreateDbContext(); var user = CreateTestUser(dbContext, 1); var order = CreateTestOrder(dbContext, user.Id, 1); var assessmentType = CreateTestAssessmentType(dbContext, 1); // 创建一个已软删除的记录 var record = new AssessmentRecord { UserId = user.Id, OrderId = order.Id, AssessmentTypeId = assessmentType.Id, Name = "测试人", Phone = "13900000001", Gender = 1, Age = 15, EducationStage = 2, Province = "北京市", City = "北京市", District = "朝阳区", Status = 4, CreateTime = DateTime.Now, UpdateTime = DateTime.Now, IsDeleted = true // 已软删除 }; dbContext.AssessmentRecords.Add(record); await dbContext.SaveChangesAsync(); var service = new AssessmentRecordService(dbContext, _mockLogger.Object); // Act var result = await service.GetRecordDetailAsync(record.Id); // Assert Assert.Null(result); } /// /// 测试 GetRecordReportAsync 当记录 ID 不存在时返回 null /// **Validates: Requirements 2.4** /// [Fact] public async Task GetRecordReportAsync_WithNonExistentId_ReturnsNull() { // Arrange using var dbContext = CreateDbContext(); var service = new AssessmentRecordService(dbContext, _mockLogger.Object); var nonExistentId = 99999L; // Act var result = await service.GetRecordReportAsync(nonExistentId); // Assert Assert.Null(result); } #endregion #region 测试未完成记录请求报告返回错误 (Requirements 3.3) /// /// 测试 GetRecordReportAsync 当记录状态为 1(待测评)时返回 null /// **Validates: Requirements 3.3** /// [Fact] public async Task GetRecordReportAsync_WithStatus1_ReturnsNull() { // Arrange using var dbContext = CreateDbContext(); var record = await CreateTestRecordWithStatus(dbContext, 1, 1); // Status = 1 (待测评) var service = new AssessmentRecordService(dbContext, _mockLogger.Object); // Act var result = await service.GetRecordReportAsync(record.Id); // Assert Assert.Null(result); } /// /// 测试 GetRecordReportAsync 当记录状态为 2(测评中)时返回 null /// **Validates: Requirements 3.3** /// [Fact] public async Task GetRecordReportAsync_WithStatus2_ReturnsNull() { // Arrange using var dbContext = CreateDbContext(); var record = await CreateTestRecordWithStatus(dbContext, 2, 2); // Status = 2 (测评中) var service = new AssessmentRecordService(dbContext, _mockLogger.Object); // Act var result = await service.GetRecordReportAsync(record.Id); // Assert Assert.Null(result); } /// /// 测试 GetRecordReportAsync 当记录状态为 3(生成中)时返回 null /// **Validates: Requirements 3.3** /// [Fact] public async Task GetRecordReportAsync_WithStatus3_ReturnsNull() { // Arrange using var dbContext = CreateDbContext(); var record = await CreateTestRecordWithStatus(dbContext, 3, 3); // Status = 3 (生成中) var service = new AssessmentRecordService(dbContext, _mockLogger.Object); // Act var result = await service.GetRecordReportAsync(record.Id); // Assert Assert.Null(result); } /// /// 测试 GetRecordReportAsync 当记录状态为 4(已完成)时返回报告 /// **Validates: Requirements 3.3** /// [Fact] public async Task GetRecordReportAsync_WithStatus4_ReturnsReport() { // Arrange using var dbContext = CreateDbContext(); var record = await CreateTestRecordWithStatus(dbContext, 4, 4); // Status = 4 (已完成) var service = new AssessmentRecordService(dbContext, _mockLogger.Object); // Act var result = await service.GetRecordReportAsync(record.Id); // Assert Assert.NotNull(result); Assert.Equal(record.Id, result.Id); Assert.Equal(4, result.Status); Assert.Equal("已完成", result.StatusName); } #endregion #region 测试导出超限返回错误 (Requirements 4.3) /// /// 测试 ExportRecordsAsync 当结果集超过 10000 条记录时抛出 BusinessException /// **Validates: Requirements 4.3** /// [Fact] public async Task ExportRecordsAsync_WithExceedingLimit_ThrowsBusinessException() { // Arrange using var dbContext = CreateDbContext(); // 创建超过 10000 条记录 await CreateBulkTestRecords(dbContext, 10001); var service = new AssessmentRecordService(dbContext, _mockLogger.Object); var request = new AssessmentRecordQueryRequest { Page = 1, PageSize = 100 }; // Act & Assert var exception = await Assert.ThrowsAsync( () => service.ExportRecordsAsync(request)); Assert.Equal(ErrorCodes.ExportDataTooLarge, exception.Code); Assert.Equal("导出数据量过大,请缩小查询范围", exception.Message); } /// /// 测试 ExportRecordsAsync 当结果集正好为 10000 条记录时不抛出异常 /// **Validates: Requirements 4.3** /// [Fact] public async Task ExportRecordsAsync_WithExactLimit_DoesNotThrow() { // Arrange using var dbContext = CreateDbContext(); // 创建正好 10000 条记录 await CreateBulkTestRecords(dbContext, 10000); var service = new AssessmentRecordService(dbContext, _mockLogger.Object); var request = new AssessmentRecordQueryRequest { Page = 1, PageSize = 100 }; // Act var result = await service.ExportRecordsAsync(request); // Assert Assert.NotNull(result); Assert.True(result.Length > 0); } /// /// 测试 ExportRecordsAsync 当结果集少于 10000 条记录时正常导出 /// **Validates: Requirements 4.3** /// [Fact] public async Task ExportRecordsAsync_WithinLimit_ReturnsExcelBytes() { // Arrange using var dbContext = CreateDbContext(); // 创建少量记录 await CreateBulkTestRecords(dbContext, 100); var service = new AssessmentRecordService(dbContext, _mockLogger.Object); var request = new AssessmentRecordQueryRequest { Page = 1, PageSize = 100 }; // Act var result = await service.ExportRecordsAsync(request); // Assert Assert.NotNull(result); Assert.True(result.Length > 0); // 验证是有效的 Excel 文件(XLSX 文件以 PK 开头,因为它是 ZIP 格式) Assert.Equal(0x50, result[0]); // 'P' Assert.Equal(0x4B, result[1]); // 'K' } #endregion #region 辅助方法 /// /// 创建内存数据库上下文 /// private AdminBusinessDbContext CreateDbContext() { var options = new DbContextOptionsBuilder() .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) .Options; return new AdminBusinessDbContext(options); } /// /// 创建测试用户 /// private User CreateTestUser(AdminBusinessDbContext dbContext, int seed) { var user = new User { Uid = $"{seed % 1000000:D6}", OpenId = $"openid_{seed}_{Guid.NewGuid():N}", Phone = $"138{seed % 100000000:D8}", Nickname = $"User_{seed}", UserLevel = 1, Status = 1, CreateTime = DateTime.Now, UpdateTime = DateTime.Now, IsDeleted = false }; dbContext.Users.Add(user); dbContext.SaveChanges(); return user; } /// /// 创建测试订单 /// private Order CreateTestOrder(AdminBusinessDbContext dbContext, long userId, int seed) { var order = new Order { OrderNo = $"ORD{DateTime.Now:yyyyMMddHHmmss}{seed % 10000:D4}_{Guid.NewGuid():N}", UserId = userId, OrderType = 1, ProductId = 1, ProductName = $"测试商品_{seed}", Amount = 100m, PayAmount = 100m, PayType = 1, Status = 2, PayTime = DateTime.Now, CreateTime = DateTime.Now, UpdateTime = DateTime.Now, IsDeleted = false }; dbContext.Orders.Add(order); dbContext.SaveChanges(); return order; } /// /// 创建测试测评类型 /// private AssessmentType CreateTestAssessmentType(AdminBusinessDbContext dbContext, int seed) { var assessmentType = new AssessmentType { Name = $"测评类型_{seed}", Code = $"TYPE_{seed}_{Guid.NewGuid():N}", Price = 99.00m, QuestionCount = 80, Sort = seed, Status = 1, CreateTime = DateTime.Now, UpdateTime = DateTime.Now, IsDeleted = false }; dbContext.AssessmentTypes.Add(assessmentType); dbContext.SaveChanges(); return assessmentType; } /// /// 创建指定状态的测评记录 /// private async Task CreateTestRecordWithStatus(AdminBusinessDbContext dbContext, int seed, int status) { var user = CreateTestUser(dbContext, seed); var order = CreateTestOrder(dbContext, user.Id, seed); var assessmentType = CreateTestAssessmentType(dbContext, seed); var record = new AssessmentRecord { UserId = user.Id, OrderId = order.Id, AssessmentTypeId = assessmentType.Id, Name = $"测试人_{seed}", Phone = $"139{seed % 100000000:D8}", Gender = 1, Age = 15, EducationStage = 2, Province = "北京市", City = "北京市", District = "朝阳区", Status = status, StartTime = status >= 2 ? DateTime.Now.AddMinutes(-30) : null, SubmitTime = status >= 3 ? DateTime.Now.AddMinutes(-10) : null, CompleteTime = status == 4 ? DateTime.Now : null, CreateTime = DateTime.Now, UpdateTime = DateTime.Now, IsDeleted = false }; dbContext.AssessmentRecords.Add(record); await dbContext.SaveChangesAsync(); return record; } /// /// 批量创建测试记录(用于导出测试) /// private async Task CreateBulkTestRecords(AdminBusinessDbContext dbContext, int count) { var user = CreateTestUser(dbContext, 1); var order = CreateTestOrder(dbContext, user.Id, 1); var assessmentType = CreateTestAssessmentType(dbContext, 1); var records = new List(); for (int i = 0; i < count; i++) { records.Add(new AssessmentRecord { UserId = user.Id, OrderId = order.Id, AssessmentTypeId = assessmentType.Id, Name = $"测试人_{i}", Phone = $"139{i % 100000000:D8}", Gender = (i % 2) + 1, Age = 10 + (i % 20), EducationStage = (i % 6) + 1, Province = "北京市", City = "北京市", District = "朝阳区", Status = (i % 4) + 1, CreateTime = DateTime.Now.AddMinutes(-i), UpdateTime = DateTime.Now, IsDeleted = false }); } dbContext.AssessmentRecords.AddRange(records); await dbContext.SaveChangesAsync(); } #endregion }