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
}