mi-assessment/server/MiAssessment/tests/MiAssessment.Tests/Services/ApiOrderServicePropertyTests.cs
2026-02-09 14:45:06 +08:00

1561 lines
56 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using FsCheck;
using FsCheck.Xunit;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using MiAssessment.Core.Interfaces;
using MiAssessment.Core.Services;
using MiAssessment.Model.Data;
using MiAssessment.Model.Entities;
using MiAssessment.Model.Models.Order;
using Moq;
using Xunit;
namespace MiAssessment.Tests.Services;
/// <summary>
/// 小程序API OrderService 属性测试
/// 验证订单服务的分页查询一致性和用户数据隔离
/// </summary>
public class ApiOrderServicePropertyTests
{
private readonly Mock<ILogger<OrderService>> _mockLogger = new();
private readonly Mock<IWechatPayService> _mockWechatPayService = new();
#region Property 3: - Order List
/// <summary>
/// Property 3: 分页查询返回的记录数不超过pageSize
/// *For any* paginated query, the returned items count SHALL not exceed pageSize.
///
/// **Feature: miniapp-api, Property 3: 分页查询一致性*
/// **Validates: Requirements 7.1**
/// </summary>
[Property(MaxTest = 100)]
public bool OrderPaginationReturnsCorrectCount(PositiveInt seed)
{
// Arrange
using var dbContext = CreateTestDbContext();
var userId = (long)seed.Get;
var pageSize = Math.Max(1, seed.Get % 20 + 1); // 1-20
// 创建多条订单记录超过pageSize<7A>?
var orderCount = pageSize + 10;
for (int i = 0; i < orderCount; i++)
{
dbContext.Orders.Add(CreateOrder(seed.Get + i, userId, orderType: 1, status: 2));
}
dbContext.SaveChanges();
var service = new OrderService(dbContext, _mockLogger.Object, _mockWechatPayService.Object);
// Act
var result = service.GetListAsync(userId, 1, pageSize, null).GetAwaiter().GetResult();
// Assert: 返回的记录数不超过pageSize
return result.List.Count <= pageSize;
}
/// <summary>
/// Property 3: 分页查询的Total等于满足条件的总记录数
/// *For any* paginated query, Total SHALL equal the count of all matching records.
///
/// **Feature: miniapp-api, Property 3: 分页查询一致性*
/// **Validates: Requirements 7.1**
/// </summary>
[Property(MaxTest = 100)]
public bool OrderPaginationTotalEqualsMatchingRecordsCount(PositiveInt seed)
{
// Arrange
using var dbContext = CreateTestDbContext();
var userId = (long)seed.Get;
var otherUserId = (long)(seed.Get + 10000);
// 为当前用户创建订单记录
var userOrderCount = Math.Max(1, seed.Get % 15 + 1); // 1-15
for (int i = 0; i < userOrderCount; i++)
{
dbContext.Orders.Add(CreateOrder(seed.Get + i, userId, orderType: 1, status: 2));
}
// 为其他用户创建订单记录不应计入Total<61>?
for (int i = 0; i < 5; i++)
{
dbContext.Orders.Add(CreateOrder(seed.Get + 100 + i, otherUserId, orderType: 1, status: 2));
}
// 创建已删除的记录不应计入Total<61>?
for (int i = 0; i < 3; i++)
{
var deletedOrder = CreateOrder(seed.Get + 200 + i, userId, orderType: 1, status: 2);
deletedOrder.IsDeleted = true;
dbContext.Orders.Add(deletedOrder);
}
dbContext.SaveChanges();
var service = new OrderService(dbContext, _mockLogger.Object, _mockWechatPayService.Object);
// Act
var result = service.GetListAsync(userId, 1, 20, null).GetAwaiter().GetResult();
// Assert: Total等于当前用户未删除的订单<E8AEA2>?
return result.Total == userOrderCount;
}
/// <summary>
/// Property 3: 遍历所有页面能获取所有满足条件的记录
/// *For any* paginated query, traversing all pages SHALL return all matching records.
///
/// **Feature: miniapp-api, Property 3: 分页查询一致性*
/// **Validates: Requirements 7.1**
/// </summary>
[Property(MaxTest = 50)]
public bool OrderPaginationTraversalReturnsAllRecords(PositiveInt seed)
{
// Arrange
using var dbContext = CreateTestDbContext();
var userId = (long)seed.Get;
var pageSize = Math.Max(1, seed.Get % 5 + 1); // 1-5 (小pageSize以测试多页
// 创建订单记录
var totalRecords = Math.Max(1, seed.Get % 12 + 1); // 1-12
var expectedIds = new HashSet<long>();
for (int i = 0; i < totalRecords; i++)
{
var order = CreateOrder(seed.Get + i, userId, orderType: 1, status: 2);
dbContext.Orders.Add(order);
expectedIds.Add(order.Id);
}
dbContext.SaveChanges();
var service = new OrderService(dbContext, _mockLogger.Object, _mockWechatPayService.Object);
// Act: 遍历所有页<E69C89>?
var allRetrievedIds = new HashSet<long>();
var page = 1;
var maxPages = (totalRecords / pageSize) + 2; // 防止无限循环
while (page <= maxPages)
{
var result = service.GetListAsync(userId, page, pageSize, null).GetAwaiter().GetResult();
if (result.List.Count == 0) break;
foreach (var item in result.List)
{
allRetrievedIds.Add(item.Id);
}
if (page >= result.TotalPages) break;
page++;
}
// Assert: 遍历所有页面后获取的记录ID集合应等于预期的ID集合
return allRetrievedIds.SetEquals(expectedIds);
}
/// <summary>
/// Property 3: 分页查询的TotalPages计算正确
/// *For any* paginated query, TotalPages SHALL equal ceil(Total / PageSize).
///
/// **Feature: miniapp-api, Property 3: 分页查询一致性*
/// **Validates: Requirements 7.1**
/// </summary>
[Property(MaxTest = 100)]
public bool OrderPaginationTotalPagesCalculatedCorrectly(PositiveInt seed)
{
// Arrange
using var dbContext = CreateTestDbContext();
var userId = (long)seed.Get;
var pageSize = Math.Max(1, seed.Get % 20 + 1); // 1-20
// 创建订单记录
var totalRecords = Math.Max(1, seed.Get % 50 + 1); // 1-50
for (int i = 0; i < totalRecords; i++)
{
dbContext.Orders.Add(CreateOrder(seed.Get + i, userId, orderType: 1, status: 2));
}
dbContext.SaveChanges();
var service = new OrderService(dbContext, _mockLogger.Object, _mockWechatPayService.Object);
// Act
var result = service.GetListAsync(userId, 1, pageSize, null).GetAwaiter().GetResult();
// Assert: TotalPages = ceil(Total / PageSize)
var expectedTotalPages = (int)Math.Ceiling((double)result.Total / pageSize);
return result.TotalPages == expectedTotalPages;
}
/// <summary>
/// Property 3: 分页查询不同页面返回不重复的记录
/// *For any* paginated query, different pages SHALL return non-overlapping records.
///
/// **Feature: miniapp-api, Property 3: 分页查询一致性*
/// **Validates: Requirements 7.1**
/// </summary>
[Property(MaxTest = 50)]
public bool OrderPaginationPagesReturnNonOverlappingRecords(PositiveInt seed)
{
// Arrange
using var dbContext = CreateTestDbContext();
var userId = (long)seed.Get;
var pageSize = 3; // 固定pageSize以确保多页
// 创建足够多的订单记录以产生多页
var totalRecords = 10;
for (int i = 0; i < totalRecords; i++)
{
dbContext.Orders.Add(CreateOrder(seed.Get + i, userId, orderType: 1, status: 2));
}
dbContext.SaveChanges();
var service = new OrderService(dbContext, _mockLogger.Object, _mockWechatPayService.Object);
// Act: 获取前两<E5898D>?
var page1 = service.GetListAsync(userId, 1, pageSize, null).GetAwaiter().GetResult();
var page2 = service.GetListAsync(userId, 2, pageSize, null).GetAwaiter().GetResult();
// Assert: 两页的记录ID不重叠
var page1Ids = page1.List.Select(o => o.Id).ToHashSet();
var page2Ids = page2.List.Select(o => o.Id).ToHashSet();
return !page1Ids.Intersect(page2Ids).Any();
}
/// <summary>
/// Property 3: 按订单类型筛选时分页查询一致性
/// *For any* paginated query with orderType filter, Total SHALL equal the count
/// of all matching records with that orderType.
///
/// **Feature: miniapp-api, Property 3: 分页查询一致性*
/// **Validates: Requirements 7.1**
/// </summary>
[Property(MaxTest = 100)]
public bool OrderPaginationWithTypeFilterReturnsCorrectTotal(PositiveInt seed)
{
// Arrange
using var dbContext = CreateTestDbContext();
var userId = (long)seed.Get;
var filterType = (seed.Get % 2) + 1; // 1 or 2
// 创建测评订单类型<E7B1BB>?
var type1Count = Math.Max(1, seed.Get % 10 + 1); // 1-10
for (int i = 0; i < type1Count; i++)
{
dbContext.Orders.Add(CreateOrder(seed.Get + i, userId, orderType: 1, status: 2));
}
// 创建规划订单类型<E7B1BB>?
var type2Count = Math.Max(1, (seed.Get + 5) % 10 + 1); // 1-10
for (int i = 0; i < type2Count; i++)
{
dbContext.Orders.Add(CreateOrder(seed.Get + 100 + i, userId, orderType: 2, status: 2));
}
dbContext.SaveChanges();
var service = new OrderService(dbContext, _mockLogger.Object, _mockWechatPayService.Object);
// Act
var result = service.GetListAsync(userId, 1, 20, filterType).GetAwaiter().GetResult();
// Assert: Total等于指定类型的订单数
var expectedCount = filterType == 1 ? type1Count : type2Count;
return result.Total == expectedCount;
}
/// <summary>
/// Property 3: 按订单类型筛选时只返回匹配类型的记录
/// *For any* paginated query with orderType filter, all returned records SHALL
/// have the specified orderType.
///
/// **Feature: miniapp-api, Property 3: 分页查询一致性*
/// **Validates: Requirements 7.1**
/// </summary>
[Property(MaxTest = 100)]
public bool OrderPaginationWithTypeFilterReturnsOnlyMatchingType(PositiveInt seed)
{
// Arrange
using var dbContext = CreateTestDbContext();
var userId = (long)seed.Get;
var filterType = (seed.Get % 2) + 1; // 1 or 2
// 创建测评订单类型<E7B1BB>?
for (int i = 0; i < 5; i++)
{
dbContext.Orders.Add(CreateOrder(seed.Get + i, userId, orderType: 1, status: 2));
}
// 创建规划订单类型<E7B1BB>?
for (int i = 0; i < 5; i++)
{
dbContext.Orders.Add(CreateOrder(seed.Get + 100 + i, userId, orderType: 2, status: 2));
}
dbContext.SaveChanges();
var service = new OrderService(dbContext, _mockLogger.Object, _mockWechatPayService.Object);
// Act
var result = service.GetListAsync(userId, 1, 20, filterType).GetAwaiter().GetResult();
// Assert: 所有返回的记录都是指定类型
return result.List.All(o => o.OrderType == filterType);
}
#endregion
#region Property 4: - Order List
/// <summary>
/// Property 4: 订单列表只返回当前用户的订单
/// *For any* order list query, the returned data SHALL only belong to the current
/// logged-in user and SHALL NOT contain data from other users.
///
/// **Feature: miniapp-api, Property 4: 用户数据隔离**
/// **Validates: Requirements 7.1**
/// </summary>
[Property(MaxTest = 100)]
public bool OrderListOnlyReturnsCurrentUserOrders(PositiveInt seed)
{
// Arrange
using var dbContext = CreateTestDbContext();
var userId = (long)seed.Get;
var otherUserId = (long)(seed.Get + 10000);
// 为当前用户创建订单
var userOrderCount = Math.Max(1, seed.Get % 10 + 1); // 1-10
for (int i = 0; i < userOrderCount; i++)
{
dbContext.Orders.Add(CreateOrder(seed.Get + i, userId, orderType: 1, status: 2));
}
// 为其他用户创建订单
for (int i = 0; i < 5; i++)
{
dbContext.Orders.Add(CreateOrder(seed.Get + 100 + i, otherUserId, orderType: 1, status: 2));
}
dbContext.SaveChanges();
var service = new OrderService(dbContext, _mockLogger.Object, _mockWechatPayService.Object);
// Act: 当前用户查询订单列表
var result = service.GetListAsync(userId, 1, 100, null).GetAwaiter().GetResult();
// Assert: 返回的订单数等于当前用户的订单数,且不包含其他用户的订单
if (result.Total != userOrderCount) return false;
if (result.List.Count != userOrderCount) return false;
// 验证数据库中确实存在其他用户的订单
var otherUserOrders = dbContext.Orders.Where(o => o.UserId == otherUserId && !o.IsDeleted).ToList();
if (otherUserOrders.Count != 5) return false;
// 验证返回的订单中不包含其他用户的订单ID
var returnedIds = result.List.Select(o => o.Id).ToHashSet();
var otherUserIds = otherUserOrders.Select(o => o.Id).ToHashSet();
return !returnedIds.Intersect(otherUserIds).Any();
}
/// <summary>
/// Property 4: 多用户场景下订单列表数据隔离正确
/// *For any* multi-user scenario, each user SHALL only see their own orders,
/// and the total count of accessible orders SHALL match their own order count.
///
/// **Feature: miniapp-api, Property 4: 用户数据隔离**
/// **Validates: Requirements 7.1**
/// </summary>
[Property(MaxTest = 50)]
public bool MultiUserOrderListDataIsolationIsCorrect(PositiveInt seed)
{
// Arrange
using var dbContext = CreateTestDbContext();
var user1Id = (long)seed.Get;
var user2Id = (long)(seed.Get + 10000);
var user3Id = (long)(seed.Get + 20000);
// 为每个用户创建不同数量的订单
var user1OrderCount = Math.Max(1, seed.Get % 5 + 1); // 1-5
var user2OrderCount = Math.Max(1, (seed.Get + 1) % 5 + 1); // 1-5
var user3OrderCount = Math.Max(1, (seed.Get + 2) % 5 + 1); // 1-5
// 创建用户1的订单
for (int i = 0; i < user1OrderCount; i++)
{
dbContext.Orders.Add(CreateOrder(seed.Get + i, user1Id, orderType: 1, status: 2));
}
// 创建用户2的订单
for (int i = 0; i < user2OrderCount; i++)
{
dbContext.Orders.Add(CreateOrder(seed.Get + 1000 + i, user2Id, orderType: 1, status: 2));
}
// 创建用户3的订单
for (int i = 0; i < user3OrderCount; i++)
{
dbContext.Orders.Add(CreateOrder(seed.Get + 2000 + i, user3Id, orderType: 1, status: 2));
}
dbContext.SaveChanges();
var service = new OrderService(dbContext, _mockLogger.Object, _mockWechatPayService.Object);
// Act & Assert: 验证每个用户只能看到自己的订单
var user1Result = service.GetListAsync(user1Id, 1, 100, null).GetAwaiter().GetResult();
var user2Result = service.GetListAsync(user2Id, 1, 100, null).GetAwaiter().GetResult();
var user3Result = service.GetListAsync(user3Id, 1, 100, null).GetAwaiter().GetResult();
// 验证每个用户的订单数量正确
if (user1Result.Total != user1OrderCount) return false;
if (user2Result.Total != user2OrderCount) return false;
if (user3Result.Total != user3OrderCount) return false;
// 验证订单ID不重叠
var user1Ids = user1Result.List.Select(o => o.Id).ToHashSet();
var user2Ids = user2Result.List.Select(o => o.Id).ToHashSet();
var user3Ids = user3Result.List.Select(o => o.Id).ToHashSet();
if (user1Ids.Intersect(user2Ids).Any()) return false;
if (user1Ids.Intersect(user3Ids).Any()) return false;
if (user2Ids.Intersect(user3Ids).Any()) return false;
return true;
}
#endregion
#region Property 4: - Order Detail
/// <summary>
/// Property 4: 用户只能获取自己的订单详情
/// *For any* GetDetail request, the returned data SHALL only belong to the current
/// logged-in user. Requests for other users' orders SHALL return null.
///
/// **Feature: miniapp-api, Property 4: 用户数据隔离**
/// **Validates: Requirements 7.2, 7.3**
/// </summary>
[Property(MaxTest = 100)]
public bool OrderDetailOnlyReturnsOwnOrders(PositiveInt seed)
{
// Arrange
using var dbContext = CreateTestDbContext();
var userId = (long)seed.Get;
var otherUserId = (long)(seed.Get + 10000);
var orderId = (long)seed.Get;
// 创建当前用户的订单
var order = CreateOrder(orderId, userId, orderType: 1, status: 2);
dbContext.Orders.Add(order);
dbContext.SaveChanges();
var service = new OrderService(dbContext, _mockLogger.Object, _mockWechatPayService.Object);
// Act: 当前用户获取自己的订单
var ownResult = service.GetDetailAsync(userId, orderId).GetAwaiter().GetResult();
// Act: 其他用户尝试获取该订单
var otherResult = service.GetDetailAsync(otherUserId, orderId).GetAwaiter().GetResult();
// Assert: 当前用户可以获取,其他用户不能获取
return ownResult != null &&
ownResult.Id == orderId &&
otherResult == null;
}
/// <summary>
/// Property 4: 用户无法获取其他用户的订单详情
/// *For any* GetDetail request with an orderId belonging to another user,
/// the result SHALL be null (no data returned).
///
/// **Feature: miniapp-api, Property 4: 用户数据隔离**
/// **Validates: Requirements 7.3**
/// </summary>
[Property(MaxTest = 100)]
public bool OrderDetailReturnsNullForOtherUserOrders(PositiveInt seed)
{
// Arrange
using var dbContext = CreateTestDbContext();
var userId = (long)seed.Get;
var otherUserId = (long)(seed.Get + 10000);
var orderId = (long)seed.Get;
// 创建其他用户的订单
var order = CreateOrder(orderId, otherUserId, orderType: 1, status: 2);
dbContext.Orders.Add(order);
dbContext.SaveChanges();
var service = new OrderService(dbContext, _mockLogger.Object, _mockWechatPayService.Object);
// Act: 当前用户尝试获取其他用户的订单
var result = service.GetDetailAsync(userId, orderId).GetAwaiter().GetResult();
// Assert: 应该返回null
return result == null;
}
/// <summary>
/// Property 4: 多用户场景下订单详情数据隔离正确
/// *For any* multi-user scenario, each user SHALL only access their own order details,
/// and attempts to access other users' orders SHALL return null.
///
/// **Feature: miniapp-api, Property 4: 用户数据隔离**
/// **Validates: Requirements 7.2, 7.3**
/// </summary>
[Property(MaxTest = 50)]
public bool MultiUserOrderDetailDataIsolationIsCorrect(PositiveInt seed)
{
// Arrange
using var dbContext = CreateTestDbContext();
var user1Id = (long)seed.Get;
var user2Id = (long)(seed.Get + 10000);
var user3Id = (long)(seed.Get + 20000);
// 为每个用户创建订单
var user1OrderIds = new List<long>();
var user2OrderIds = new List<long>();
var user3OrderIds = new List<long>();
// 创建用户1的订单
for (int i = 0; i < 3; i++)
{
var orderId = seed.Get + i;
dbContext.Orders.Add(CreateOrder(orderId, user1Id, orderType: 1, status: 2));
user1OrderIds.Add(orderId);
}
// 创建用户2的订单
for (int i = 0; i < 3; i++)
{
var orderId = seed.Get + 1000 + i;
dbContext.Orders.Add(CreateOrder(orderId, user2Id, orderType: 1, status: 2));
user2OrderIds.Add(orderId);
}
// 创建用户3的订单
for (int i = 0; i < 3; i++)
{
var orderId = seed.Get + 2000 + i;
dbContext.Orders.Add(CreateOrder(orderId, user3Id, orderType: 1, status: 2));
user3OrderIds.Add(orderId);
}
dbContext.SaveChanges();
var service = new OrderService(dbContext, _mockLogger.Object, _mockWechatPayService.Object);
// Act & Assert: 验证每个用户只能访问自己的订单详情
// 用户1可以访问自己的订单
foreach (var orderId in user1OrderIds)
{
var result = service.GetDetailAsync(user1Id, orderId).GetAwaiter().GetResult();
if (result == null || result.Id != orderId) return false;
}
// 用户1不能访问用户2的订单
foreach (var orderId in user2OrderIds)
{
var result = service.GetDetailAsync(user1Id, orderId).GetAwaiter().GetResult();
if (result != null) return false;
}
// 用户1不能访问用户3的订单
foreach (var orderId in user3OrderIds)
{
var result = service.GetDetailAsync(user1Id, orderId).GetAwaiter().GetResult();
if (result != null) return false;
}
// 用户2可以访问自己的订单
foreach (var orderId in user2OrderIds)
{
var result = service.GetDetailAsync(user2Id, orderId).GetAwaiter().GetResult();
if (result == null || result.Id != orderId) return false;
}
// 用户2不能访问用户1的订单
foreach (var orderId in user1OrderIds)
{
var result = service.GetDetailAsync(user2Id, orderId).GetAwaiter().GetResult();
if (result != null) return false;
}
return true;
}
#endregion
#region Property 4: - Pay Result
/// <summary>
/// Property 4: 用户只能查询自己订单的支付结<E4BB98>?
/// *For any* GetPayResult request, the returned data SHALL only belong to the current
/// logged-in user. Requests for other users' orders SHALL return null.
///
/// **Feature: miniapp-api, Property 4: 用户数据隔离**
/// **Validates: Requirements 7.3**
/// </summary>
[Property(MaxTest = 100)]
public bool PayResultOnlyReturnsOwnOrders(PositiveInt seed)
{
// Arrange
using var dbContext = CreateTestDbContext();
var userId = (long)seed.Get;
var otherUserId = (long)(seed.Get + 10000);
var orderId = (long)seed.Get;
// 创建当前用户的订单
var order = CreateOrder(orderId, userId, orderType: 1, status: 2);
dbContext.Orders.Add(order);
dbContext.SaveChanges();
var service = new OrderService(dbContext, _mockLogger.Object, _mockWechatPayService.Object);
// Act: 当前用户查询自己订单的支付结<E4BB98>?
var ownResult = service.GetPayResultAsync(userId, orderId).GetAwaiter().GetResult();
// Act: 其他用户尝试查询该订单的支付结果
var otherResult = service.GetPayResultAsync(otherUserId, orderId).GetAwaiter().GetResult();
// Assert: 当前用户可以查询其他用户不能查<E883BD>?
return ownResult != null && otherResult == null;
}
/// <summary>
/// Property 4: 用户无法查询其他用户订单的支付结<E4BB98>?
/// *For any* GetPayResult request with an orderId belonging to another user,
/// the result SHALL be null (no data returned).
///
/// **Feature: miniapp-api, Property 4: 用户数据隔离**
/// **Validates: Requirements 7.3**
/// </summary>
[Property(MaxTest = 100)]
public bool PayResultReturnsNullForOtherUserOrders(PositiveInt seed)
{
// Arrange
using var dbContext = CreateTestDbContext();
var userId = (long)seed.Get;
var otherUserId = (long)(seed.Get + 10000);
var orderId = (long)seed.Get;
// 创建其他用户的订单
var order = CreateOrder(orderId, otherUserId, orderType: 1, status: 2);
dbContext.Orders.Add(order);
dbContext.SaveChanges();
var service = new OrderService(dbContext, _mockLogger.Object, _mockWechatPayService.Object);
// Act: 当前用户尝试查询其他用户订单的支付结<E4BB98>?
var result = service.GetPayResultAsync(userId, orderId).GetAwaiter().GetResult();
// Assert: 应该返回null
return result == null;
}
#endregion
#region
/// <summary>
/// Property 3: 空数据库返回空分页结<E9A1B5>?
///
/// **Feature: miniapp-api, Property 3: 分页查询一致性*
/// **Validates: Requirements 7.1**
/// </summary>
[Fact]
public void EmptyDatabaseReturnsEmptyOrderPagedResult()
{
// Arrange
using var dbContext = CreateTestDbContext();
var service = new OrderService(dbContext, _mockLogger.Object, _mockWechatPayService.Object);
// Act
var result = service.GetListAsync(1, 1, 20, null).GetAwaiter().GetResult();
// Assert
Assert.Empty(result.List);
Assert.Equal(0, result.Total);
Assert.Equal(0, result.TotalPages);
}
/// <summary>
/// Property 3: 分页参数边界值处<E580BC>?
///
/// **Feature: miniapp-api, Property 3: 分页查询一致性*
/// **Validates: Requirements 7.1**
/// </summary>
[Theory]
[InlineData(0, 20)] // page < 1
[InlineData(-1, 20)] // page < 1
[InlineData(1, 0)] // pageSize < 1
[InlineData(1, -1)] // pageSize < 1
[InlineData(1, 200)] // pageSize > 100
public void OrderPaginationHandlesBoundaryValues(int page, int pageSize)
{
// Arrange
using var dbContext = CreateTestDbContext();
var userId = 1L;
// 创建订单记录
for (int i = 0; i < 5; i++)
{
dbContext.Orders.Add(CreateOrder(i + 1, userId, orderType: 1, status: 2));
}
dbContext.SaveChanges();
var service = new OrderService(dbContext, _mockLogger.Object, _mockWechatPayService.Object);
// Act
var result = service.GetListAsync(userId, page, pageSize, null).GetAwaiter().GetResult();
// Assert: 服务应该处理边界值不抛出异<E587BA>?
Assert.NotNull(result);
Assert.True(result.Page >= 1);
Assert.True(result.PageSize >= 1 && result.PageSize <= 100);
}
/// <summary>
/// Property 4: 不存在的订单ID返回null
///
/// **Feature: miniapp-api, Property 4: 用户数据隔离**
/// **Validates: Requirements 7.2**
/// </summary>
[Property(MaxTest = 100)]
public bool OrderDetailReturnsNullForNonExistentOrders(PositiveInt seed)
{
// Arrange
using var dbContext = CreateTestDbContext();
var userId = (long)seed.Get;
var nonExistentOrderId = (long)(seed.Get + 999999);
var service = new OrderService(dbContext, _mockLogger.Object, _mockWechatPayService.Object);
// Act: 获取不存在的订单
var result = service.GetDetailAsync(userId, nonExistentOrderId).GetAwaiter().GetResult();
// Assert: 应该返回null
return result == null;
}
/// <summary>
/// Property 4: 已删除的订单返回null
///
/// **Feature: miniapp-api, Property 4: 用户数据隔离**
/// **Validates: Requirements 7.2**
/// </summary>
[Property(MaxTest = 100)]
public bool OrderDetailReturnsNullForDeletedOrders(PositiveInt seed)
{
// Arrange
using var dbContext = CreateTestDbContext();
var userId = (long)seed.Get;
var orderId = (long)seed.Get;
// 创建已删除的订单
var order = CreateOrder(orderId, userId, orderType: 1, status: 2);
order.IsDeleted = true;
dbContext.Orders.Add(order);
dbContext.SaveChanges();
var service = new OrderService(dbContext, _mockLogger.Object, _mockWechatPayService.Object);
// Act: 获取已删除的订单
var result = service.GetDetailAsync(userId, orderId).GetAwaiter().GetResult();
// Assert: 应该返回null
return result == null;
}
/// <summary>
/// Property 4: 已删除的订单不出现在列表<E58897>?
///
/// **Feature: miniapp-api, Property 4: 用户数据隔离**
/// **Validates: Requirements 7.1**
/// </summary>
[Property(MaxTest = 100)]
public bool OrderListExcludesDeletedOrders(PositiveInt seed)
{
// Arrange
using var dbContext = CreateTestDbContext();
var userId = (long)seed.Get;
// 创建正常订单
var normalOrderCount = Math.Max(1, seed.Get % 5 + 1); // 1-5
for (int i = 0; i < normalOrderCount; i++)
{
dbContext.Orders.Add(CreateOrder(seed.Get + i, userId, orderType: 1, status: 2));
}
// 创建已删除的订单
var deletedOrderCount = Math.Max(1, (seed.Get + 3) % 5 + 1); // 1-5
var deletedOrderIds = new HashSet<long>();
for (int i = 0; i < deletedOrderCount; i++)
{
var orderId = seed.Get + 100 + i;
var order = CreateOrder(orderId, userId, orderType: 1, status: 2);
order.IsDeleted = true;
dbContext.Orders.Add(order);
deletedOrderIds.Add(orderId);
}
dbContext.SaveChanges();
var service = new OrderService(dbContext, _mockLogger.Object, _mockWechatPayService.Object);
// Act
var result = service.GetListAsync(userId, 1, 100, null).GetAwaiter().GetResult();
// Assert: 返回的订单数等于正常订单数,且不包含已删除的订单
if (result.Total != normalOrderCount) return false;
var returnedIds = result.List.Select(o => o.Id).ToHashSet();
return !returnedIds.Intersect(deletedOrderIds).Any();
}
#endregion
#region Property 5: <EFBFBD>?
/// <summary>
/// Property 5: 测评订单创建后同时存在订单记录和测评记录
/// *For any* assessment order creation request, after successful creation there SHALL exist
/// both an order record and an assessment record, and the assessment record's OrderId
/// SHALL point to the newly created order.
///
/// **Feature: miniapp-api, Property 5: 订单创建完整<E5AE8C>?*
/// **Validates: Requirements 8.1**
/// </summary>
[Property(MaxTest = 100)]
public bool AssessmentOrderCreationCreatesOrderAndAssessmentRecord(PositiveInt seed)
{
// Arrange
using var dbContext = CreateTestDbContext();
var userId = (long)seed.Get;
var productId = (long)(seed.Get % 100 + 1);
// 创建测评类型
var assessmentType = CreateAssessmentType(productId);
dbContext.AssessmentTypes.Add(assessmentType);
dbContext.SaveChanges();
var service = new OrderService(dbContext, _mockLogger.Object, _mockWechatPayService.Object);
var request = new CreateOrderRequest
{
OrderType = 1, // 测评订单
ProductId = productId,
AssessmentInfo = CreateAssessmentInfo(seed.Get)
};
// Act
var result = service.CreateAsync(userId, request).GetAwaiter().GetResult();
// Assert: 验证订单记录存在
var order = dbContext.Orders.FirstOrDefault(o => o.Id == result.OrderId);
if (order == null) return false;
// Assert: 验证测评记录存在
var assessmentRecord = dbContext.AssessmentRecords.FirstOrDefault(r => r.OrderId == result.OrderId);
if (assessmentRecord == null) return false;
// Assert: 验证测评记录的OrderId指向新创建的订单
if (assessmentRecord.OrderId != order.Id) return false;
// Assert: 验证返回的AssessmentRecordId正确
if (result.AssessmentRecordId != assessmentRecord.Id) return false;
// Assert: 验证订单和测评记录的用户ID一<44>?
if (order.UserId != userId || assessmentRecord.UserId != userId) return false;
return true;
}
/// <summary>
/// Property 5: 规划订单创建后同时存在订单记录和规划预约记录
/// *For any* planner order creation request, after successful creation there SHALL exist
/// both an order record and a planner booking record, and the booking record's OrderId
/// SHALL point to the newly created order.
///
/// **Feature: miniapp-api, Property 5: 订单创建完整<E5AE8C>?*
/// **Validates: Requirements 8.2**
/// </summary>
[Property(MaxTest = 100)]
public bool PlannerOrderCreationCreatesOrderAndBookingRecord(PositiveInt seed)
{
// Arrange
using var dbContext = CreateTestDbContext();
var userId = (long)seed.Get;
var productId = (long)(seed.Get % 100 + 1);
// 创建规划<E8A784>?
var planner = CreatePlanner(productId);
dbContext.Planners.Add(planner);
dbContext.SaveChanges();
var service = new OrderService(dbContext, _mockLogger.Object, _mockWechatPayService.Object);
var request = new CreateOrderRequest
{
OrderType = 2, // 规划订单
ProductId = productId,
PlannerInfo = CreatePlannerInfo(seed.Get)
};
// Act
var result = service.CreateAsync(userId, request).GetAwaiter().GetResult();
// Assert: 验证订单记录存在
var order = dbContext.Orders.FirstOrDefault(o => o.Id == result.OrderId);
if (order == null) return false;
// Assert: 验证规划预约记录存在
var plannerBooking = dbContext.PlannerBookings.FirstOrDefault(b => b.OrderId == result.OrderId);
if (plannerBooking == null) return false;
// Assert: 验证规划预约记录的OrderId指向新创建的订单
if (plannerBooking.OrderId != order.Id) return false;
// Assert: 验证订单和规划预约记录的用户ID一<44>?
if (order.UserId != userId || plannerBooking.UserId != userId) return false;
return true;
}
/// <summary>
/// Property 5: 使用邀请码创建订单时金额设<E9A29D>?并标记邀请码已使<E5B7B2>?
/// *For any* order creation with invite code, the order amount SHALL be set to 0
/// and the invite code SHALL be marked as used.
///
/// **Feature: miniapp-api, Property 5: 订单创建完整<E5AE8C>?*
/// **Validates: Requirements 8.3**
/// </summary>
[Property(MaxTest = 100)]
public bool OrderCreationWithInviteCodeSetsAmountToZeroAndMarksCodeUsed(PositiveInt seed)
{
// Arrange
using var dbContext = CreateTestDbContext();
var userId = (long)seed.Get;
var productId = (long)(seed.Get % 100 + 1);
var inviteCodeId = (long)(seed.Get + 1000);
// 创建测评类型
var assessmentType = CreateAssessmentType(productId);
dbContext.AssessmentTypes.Add(assessmentType);
// 创建已分配的邀请码<EFBC88>?=已分配)
var inviteCode = CreateInviteCode(inviteCodeId, status: 2);
dbContext.InviteCodes.Add(inviteCode);
dbContext.SaveChanges();
var service = new OrderService(dbContext, _mockLogger.Object, _mockWechatPayService.Object);
var request = new CreateOrderRequest
{
OrderType = 1,
ProductId = productId,
AssessmentInfo = CreateAssessmentInfo(seed.Get),
InviteCodeId = inviteCodeId
};
// Act
var result = service.CreateAsync(userId, request).GetAwaiter().GetResult();
// Assert: 验证支付金额<E98791>?
if (result.PayAmount != 0) return false;
// Assert: 验证不需要支<E8A681>?
if (result.NeedPay) return false;
// Assert: 验证订单记录的PayAmount<6E>?
var order = dbContext.Orders.FirstOrDefault(o => o.Id == result.OrderId);
if (order == null || order.PayAmount != 0) return false;
// Assert: 验证邀请码状态变为已使用<E4BDBF>?<3F>?
var updatedInviteCode = dbContext.InviteCodes.FirstOrDefault(ic => ic.Id == inviteCodeId);
if (updatedInviteCode == null || updatedInviteCode.Status != 3) return false;
// Assert: 验证邀请码记录了使用者和订单信息
if (updatedInviteCode.UseUserId != userId) return false;
if (updatedInviteCode.UseOrderId != result.OrderId) return false;
return true;
}
/// <summary>
/// Property 5: 订单创建后关联记录的数据完整<E5AE8C>?
/// *For any* order creation, the associated record (assessment or booking) SHALL contain
/// all the information provided in the request.
///
/// **Feature: miniapp-api, Property 5: 订单创建完整<E5AE8C>?*
/// **Validates: Requirements 8.1, 8.2**
/// </summary>
[Property(MaxTest = 100)]
public bool OrderCreationPreservesAllRequestData(PositiveInt seed)
{
// Arrange
using var dbContext = CreateTestDbContext();
var userId = (long)seed.Get;
var productId = (long)(seed.Get % 100 + 1);
// 创建测评类型
var assessmentType = CreateAssessmentType(productId);
dbContext.AssessmentTypes.Add(assessmentType);
dbContext.SaveChanges();
var service = new OrderService(dbContext, _mockLogger.Object, _mockWechatPayService.Object);
var assessmentInfo = CreateAssessmentInfo(seed.Get);
var request = new CreateOrderRequest
{
OrderType = 1,
ProductId = productId,
AssessmentInfo = assessmentInfo
};
// Act
var result = service.CreateAsync(userId, request).GetAwaiter().GetResult();
// Assert: 验证测评记录包含所有请求数<E6B182>?
var assessmentRecord = dbContext.AssessmentRecords.FirstOrDefault(r => r.OrderId == result.OrderId);
if (assessmentRecord == null) return false;
// 验证所有字<E69C89>?
if (assessmentRecord.Name != assessmentInfo.Name) return false;
if (assessmentRecord.Phone != assessmentInfo.Phone) return false;
if (assessmentRecord.Gender != assessmentInfo.Gender) return false;
if (assessmentRecord.Age != assessmentInfo.Age) return false;
if (assessmentRecord.EducationStage != assessmentInfo.EducationStage) return false;
if (assessmentRecord.Province != assessmentInfo.Province) return false;
if (assessmentRecord.City != assessmentInfo.City) return false;
if (assessmentRecord.District != assessmentInfo.District) return false;
return true;
}
#endregion
#region Property 10:
/// <summary>
/// Property 10: 无效的测评类型ID导致订单创建失败时无残留数据
/// *For any* order creation with invalid product ID, there SHALL be no partially created
/// order or associated records in the database.
///
/// **Feature: miniapp-api, Property 10: 订单事务回滚**
/// **Validates: Requirements 8.4**
/// </summary>
[Property(MaxTest = 100)]
public bool InvalidProductIdCausesNoPartialData(PositiveInt seed)
{
// Arrange
using var dbContext = CreateTestDbContext();
var userId = (long)seed.Get;
var invalidProductId = (long)(seed.Get + 999999); // 不存在的产品ID
// 记录创建前的记录<E8AEB0>?
var orderCountBefore = dbContext.Orders.Count();
var assessmentRecordCountBefore = dbContext.AssessmentRecords.Count();
var service = new OrderService(dbContext, _mockLogger.Object, _mockWechatPayService.Object);
var request = new CreateOrderRequest
{
OrderType = 1,
ProductId = invalidProductId,
AssessmentInfo = CreateAssessmentInfo(seed.Get)
};
// Act & Assert
try
{
service.CreateAsync(userId, request).GetAwaiter().GetResult();
// 如果没有抛出异常测试失<E8AF95>?
return false;
}
catch (ArgumentException)
{
// 预期的异<E79A84>?
}
// Assert: 验证没有创建任何订单记录
var orderCountAfter = dbContext.Orders.Count();
if (orderCountAfter != orderCountBefore) return false;
// Assert: 验证没有创建任何测评记录
var assessmentRecordCountAfter = dbContext.AssessmentRecords.Count();
if (assessmentRecordCountAfter != assessmentRecordCountBefore) return false;
return true;
}
/// <summary>
/// Property 10: 无效的规划师ID导致订单创建失败时无残留数据
/// *For any* planner order creation with invalid planner ID, there SHALL be no partially
/// created order or booking records in the database.
///
/// **Feature: miniapp-api, Property 10: 订单事务回滚**
/// **Validates: Requirements 8.4**
/// </summary>
[Property(MaxTest = 100)]
public bool InvalidPlannerIdCausesNoPartialData(PositiveInt seed)
{
// Arrange
using var dbContext = CreateTestDbContext();
var userId = (long)seed.Get;
var invalidPlannerId = (long)(seed.Get + 999999); // 不存在的规划师ID
// 记录创建前的记录<E8AEB0>?
var orderCountBefore = dbContext.Orders.Count();
var bookingCountBefore = dbContext.PlannerBookings.Count();
var service = new OrderService(dbContext, _mockLogger.Object, _mockWechatPayService.Object);
var request = new CreateOrderRequest
{
OrderType = 2,
ProductId = invalidPlannerId,
PlannerInfo = CreatePlannerInfo(seed.Get)
};
// Act & Assert
try
{
service.CreateAsync(userId, request).GetAwaiter().GetResult();
// 如果没有抛出异常测试失<E8AF95>?
return false;
}
catch (ArgumentException)
{
// 预期的异<E79A84>?
}
// Assert: 验证没有创建任何订单记录
var orderCountAfter = dbContext.Orders.Count();
if (orderCountAfter != orderCountBefore) return false;
// Assert: 验证没有创建任何规划预约记录
var bookingCountAfter = dbContext.PlannerBookings.Count();
if (bookingCountAfter != bookingCountBefore) return false;
return true;
}
/// <summary>
/// Property 10: 已使用的邀请码导致订单创建失败时无残留数据
/// *For any* order creation with already used invite code, there SHALL be no partially
/// created order or associated records in the database.
///
/// **Feature: miniapp-api, Property 10: 订单事务回滚**
/// **Validates: Requirements 8.4**
/// </summary>
[Property(MaxTest = 100)]
public bool UsedInviteCodeCausesNoPartialData(PositiveInt seed)
{
// Arrange
using var dbContext = CreateTestDbContext();
var userId = (long)seed.Get;
var productId = (long)(seed.Get % 100 + 1);
var inviteCodeId = (long)(seed.Get + 1000);
// 创建测评类型
var assessmentType = CreateAssessmentType(productId);
dbContext.AssessmentTypes.Add(assessmentType);
// 创建已使用的邀请码<EFBC88>?=已使用)
var inviteCode = CreateInviteCode(inviteCodeId, status: 3);
dbContext.InviteCodes.Add(inviteCode);
dbContext.SaveChanges();
// 记录创建前的记录<E8AEB0>?
var orderCountBefore = dbContext.Orders.Count();
var assessmentRecordCountBefore = dbContext.AssessmentRecords.Count();
var service = new OrderService(dbContext, _mockLogger.Object, _mockWechatPayService.Object);
var request = new CreateOrderRequest
{
OrderType = 1,
ProductId = productId,
AssessmentInfo = CreateAssessmentInfo(seed.Get),
InviteCodeId = inviteCodeId
};
// Act & Assert
try
{
service.CreateAsync(userId, request).GetAwaiter().GetResult();
// 如果没有抛出异常测试失<E8AF95>?
return false;
}
catch (ArgumentException)
{
// 预期的异<E79A84>?
}
// Assert: 验证没有创建任何订单记录
var orderCountAfter = dbContext.Orders.Count();
if (orderCountAfter != orderCountBefore) return false;
// Assert: 验证没有创建任何测评记录
var assessmentRecordCountAfter = dbContext.AssessmentRecords.Count();
if (assessmentRecordCountAfter != assessmentRecordCountBefore) return false;
return true;
}
/// <summary>
/// Property 10: 不存在的邀请码ID导致订单创建失败时无残留数据
/// *For any* order creation with non-existent invite code ID, there SHALL be no partially
/// created order or associated records in the database.
///
/// **Feature: miniapp-api, Property 10: 订单事务回滚**
/// **Validates: Requirements 8.4**
/// </summary>
[Property(MaxTest = 100)]
public bool NonExistentInviteCodeCausesNoPartialData(PositiveInt seed)
{
// Arrange
using var dbContext = CreateTestDbContext();
var userId = (long)seed.Get;
var productId = (long)(seed.Get % 100 + 1);
var nonExistentInviteCodeId = (long)(seed.Get + 999999);
// 创建测评类型
var assessmentType = CreateAssessmentType(productId);
dbContext.AssessmentTypes.Add(assessmentType);
dbContext.SaveChanges();
// 记录创建前的记录<E8AEB0>?
var orderCountBefore = dbContext.Orders.Count();
var assessmentRecordCountBefore = dbContext.AssessmentRecords.Count();
var service = new OrderService(dbContext, _mockLogger.Object, _mockWechatPayService.Object);
var request = new CreateOrderRequest
{
OrderType = 1,
ProductId = productId,
AssessmentInfo = CreateAssessmentInfo(seed.Get),
InviteCodeId = nonExistentInviteCodeId
};
// Act & Assert
try
{
service.CreateAsync(userId, request).GetAwaiter().GetResult();
// 如果没有抛出异常测试失<E8AF95>?
return false;
}
catch (ArgumentException)
{
// 预期的异<E79A84>?
}
// Assert: 验证没有创建任何订单记录
var orderCountAfter = dbContext.Orders.Count();
if (orderCountAfter != orderCountBefore) return false;
// Assert: 验证没有创建任何测评记录
var assessmentRecordCountAfter = dbContext.AssessmentRecords.Count();
if (assessmentRecordCountAfter != assessmentRecordCountBefore) return false;
return true;
}
/// <summary>
/// Property 10: 缺少必要信息导致订单创建失败时无残留数据
/// *For any* order creation with missing required info (e.g., assessment info for assessment order),
/// there SHALL be no partially created order or associated records in the database.
///
/// **Feature: miniapp-api, Property 10: 订单事务回滚**
/// **Validates: Requirements 8.4**
/// </summary>
[Property(MaxTest = 100)]
public bool MissingRequiredInfoCausesNoPartialData(PositiveInt seed)
{
// Arrange
using var dbContext = CreateTestDbContext();
var userId = (long)seed.Get;
var productId = (long)(seed.Get % 100 + 1);
// 创建测评类型
var assessmentType = CreateAssessmentType(productId);
dbContext.AssessmentTypes.Add(assessmentType);
dbContext.SaveChanges();
// 记录创建前的记录<E8AEB0>?
var orderCountBefore = dbContext.Orders.Count();
var assessmentRecordCountBefore = dbContext.AssessmentRecords.Count();
var service = new OrderService(dbContext, _mockLogger.Object, _mockWechatPayService.Object);
// 测评订单但不提供AssessmentInfo
var request = new CreateOrderRequest
{
OrderType = 1,
ProductId = productId,
AssessmentInfo = null // 缺少必要信息
};
// Act & Assert
try
{
service.CreateAsync(userId, request).GetAwaiter().GetResult();
// 如果没有抛出异常测试失<E8AF95>?
return false;
}
catch (ArgumentException)
{
// 预期的异<E79A84>?
}
// Assert: 验证没有创建任何订单记录
var orderCountAfter = dbContext.Orders.Count();
if (orderCountAfter != orderCountBefore) return false;
// Assert: 验证没有创建任何测评记录
var assessmentRecordCountAfter = dbContext.AssessmentRecords.Count();
if (assessmentRecordCountAfter != assessmentRecordCountBefore) return false;
return true;
}
/// <summary>
/// Property 10: 无效的订单类型导致订单创建失败时无残留数<E79599>?
/// *For any* order creation with invalid order type, there SHALL be no partially
/// created order or associated records in the database.
///
/// **Feature: miniapp-api, Property 10: 订单事务回滚**
/// **Validates: Requirements 8.4**
/// </summary>
[Property(MaxTest = 100)]
public bool InvalidOrderTypeCausesNoPartialData(PositiveInt seed)
{
// Arrange
using var dbContext = CreateTestDbContext();
var userId = (long)seed.Get;
var productId = (long)(seed.Get % 100 + 1);
var invalidOrderType = (seed.Get % 10) + 3; // 3-12都是无效的订单类型
// 创建测评类型以防万一<E4B887>?
var assessmentType = CreateAssessmentType(productId);
dbContext.AssessmentTypes.Add(assessmentType);
dbContext.SaveChanges();
// 记录创建前的记录<E8AEB0>?
var orderCountBefore = dbContext.Orders.Count();
var assessmentRecordCountBefore = dbContext.AssessmentRecords.Count();
var bookingCountBefore = dbContext.PlannerBookings.Count();
var service = new OrderService(dbContext, _mockLogger.Object, _mockWechatPayService.Object);
var request = new CreateOrderRequest
{
OrderType = invalidOrderType,
ProductId = productId,
AssessmentInfo = CreateAssessmentInfo(seed.Get)
};
// Act & Assert
try
{
service.CreateAsync(userId, request).GetAwaiter().GetResult();
// 如果没有抛出异常测试失<E8AF95>?
return false;
}
catch (ArgumentException)
{
// 预期的异<E79A84>?
}
// Assert: 验证没有创建任何订单记录
var orderCountAfter = dbContext.Orders.Count();
if (orderCountAfter != orderCountBefore) return false;
// Assert: 验证没有创建任何测评记录
var assessmentRecordCountAfter = dbContext.AssessmentRecords.Count();
if (assessmentRecordCountAfter != assessmentRecordCountBefore) return false;
// Assert: 验证没有创建任何规划预约记录
var bookingCountAfter = dbContext.PlannerBookings.Count();
if (bookingCountAfter != bookingCountBefore) return false;
return true;
}
#endregion
#region
/// <summary>
/// 创建测试用内存数据库上下<E4B88A>?
/// </summary>
private TestOrderDbContext CreateTestDbContext()
{
var options = new DbContextOptionsBuilder<TestOrderDbContext>()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.Options;
return new TestOrderDbContext(options);
}
/// <summary>
/// 创建测试订单
/// </summary>
private Order CreateOrder(long id, long userId, int orderType, int status)
{
return new Order
{
Id = id,
OrderNo = $"ORD{id:D10}",
UserId = userId,
OrderType = orderType,
ProductId = 1,
ProductName = orderType == 1 ? "多元智能测评" : "学业规划服务",
Amount = 99.00m,
PayAmount = 99.00m,
Status = status,
PayTime = status >= 2 ? DateTime.Now.AddMinutes(-10) : null,
CreateTime = DateTime.Now.AddMinutes(-id), // 不同的创建时间以测试排序
UpdateTime = DateTime.Now,
IsDeleted = false
};
}
/// <summary>
/// 创建测试用测评类型
/// </summary>
private AssessmentType CreateAssessmentType(long id)
{
return new AssessmentType
{
Id = id,
Name = $"测评类型{id}",
Code = $"TYPE{id}",
Price = 99.00m,
Status = 1, // 已上<E5B7B2>?
QuestionCount = 80,
Sort = 1,
CreateTime = DateTime.Now,
UpdateTime = DateTime.Now,
IsDeleted = false
};
}
/// <summary>
/// 创建测试用规划师
/// </summary>
private Planner CreatePlanner(long id)
{
return new Planner
{
Id = id,
Name = $"规划师{id}",
Avatar = $"https://example.com/avatar{id}.jpg",
Introduction = $"规划师{id}的简介",
Price = 199.00m,
Status = 1, // 启用
Sort = 1,
CreateTime = DateTime.Now,
UpdateTime = DateTime.Now,
IsDeleted = false
};
}
/// <summary>
/// 创建测试用邀请码
/// </summary>
private InviteCode CreateInviteCode(long id, int status)
{
return new InviteCode
{
Id = id,
Code = $"CODE{id % 100000:D5}",
Status = status,
CreateTime = DateTime.Now,
UpdateTime = DateTime.Now,
IsDeleted = false
};
}
/// <summary>
/// 创建测试用测评信息
/// </summary>
private AssessmentInfoDto CreateAssessmentInfo(int seed)
{
return new AssessmentInfoDto
{
Name = $"测试用户{seed}",
Phone = $"138{seed % 100000000:D8}",
Gender = (seed % 2) + 1, // 1或2
Age = (seed % 50) + 6, // 6-55岁
EducationStage = (seed % 6) + 1, // 1-6
Province = "北京市",
City = "北京市",
District = "海淀区"
};
}
/// <summary>
/// 创建测试用规划预约信息
/// </summary>
private PlannerInfoDto CreatePlannerInfo(int seed)
{
return new PlannerInfoDto
{
Name = $"预约用户{seed}",
Phone = $"139{seed % 100000000:D8}",
Remark = $"备注{seed}"
};
}
#endregion
}
/// <summary>
/// 测试用DbContext继承自MiAssessmentDbContext但忽略外键关系验证
/// </summary>
public class TestOrderDbContext : MiAssessmentDbContext
{
public TestOrderDbContext(DbContextOptions<TestOrderDbContext> options)
: base(CreateBaseOptions(options))
{
}
private static DbContextOptions<MiAssessmentDbContext> CreateBaseOptions(DbContextOptions<TestOrderDbContext> options)
{
var builder = new DbContextOptionsBuilder<MiAssessmentDbContext>();
builder.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.ConfigureWarnings(w => w.Ignore(Microsoft.EntityFrameworkCore.Diagnostics.InMemoryEventId.TransactionIgnoredWarning));
return builder.Options;
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// 不调用基类的OnModelCreating避免外键关系验证
// 只配置必要的表映<E8A1A8>?
modelBuilder.Entity<Order>().ToTable("orders");
modelBuilder.Entity<AssessmentRecord>().ToTable("assessment_records");
modelBuilder.Entity<AssessmentType>().ToTable("assessment_types");
modelBuilder.Entity<Planner>().ToTable("planners");
modelBuilder.Entity<PlannerBooking>().ToTable("planner_bookings");
modelBuilder.Entity<InviteCode>().ToTable("invite_codes");
// 忽略导航属<E888AA>?
modelBuilder.Entity<AssessmentRecord>().Ignore(e => e.AssessmentType);
}
}