- 系统配置管理模块 (Config) - 内容管理模块 (Banner, Promotion) - 测评管理模块 (Type, Question, Category, Mapping, Conclusion) - 用户管理模块 (User) - 订单管理模块 (Order) - 规划师管理模块 (Planner) - 分销管理模块 (InviteCode, Commission, Withdrawal) - 数据统计仪表盘模块 (Dashboard) - 权限控制集成 - 服务注册配置 全部381个测试通过
707 lines
24 KiB
C#
707 lines
24 KiB
C#
using FsCheck;
|
||
using FsCheck.Xunit;
|
||
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.Assessment;
|
||
using MiAssessment.Admin.Business.Models.Common;
|
||
using MiAssessment.Admin.Business.Services;
|
||
using Moq;
|
||
using Xunit;
|
||
|
||
namespace MiAssessment.Tests.Admin;
|
||
|
||
/// <summary>
|
||
/// Mapping 属性测试
|
||
/// 验证题目分类映射服务的正确性属性
|
||
/// </summary>
|
||
public class MappingPropertyTests
|
||
{
|
||
private readonly Mock<ILogger<AssessmentService>> _mockLogger = new();
|
||
|
||
#region Property 10: Mapping Relationship Bidirectionality
|
||
|
||
/// <summary>
|
||
/// Property 10: 通过题目ID查询映射应包含关联的分类ID
|
||
/// *For any* QuestionCategoryMapping, querying mappings by QuestionId SHALL include the CategoryId.
|
||
///
|
||
/// **Validates: Requirements 7.1**
|
||
/// </summary>
|
||
[Property(MaxTest = 100)]
|
||
public bool Bidirectionality_QueryByQuestion_ShouldIncludeCategoryId(PositiveInt seed)
|
||
{
|
||
// Arrange: 创建题目、分类和映射关系
|
||
using var dbContext = CreateDbContext();
|
||
var (assessmentTypeId, questionId, categoryId) = CreateTestData(dbContext, seed.Get);
|
||
|
||
// 创建映射关系
|
||
var mapping = new QuestionCategoryMapping
|
||
{
|
||
QuestionId = questionId,
|
||
CategoryId = categoryId,
|
||
CreateTime = DateTime.Now
|
||
};
|
||
dbContext.QuestionCategoryMappings.Add(mapping);
|
||
dbContext.SaveChanges();
|
||
|
||
var service = new AssessmentService(dbContext, _mockLogger.Object);
|
||
|
||
// Act: 通过题目ID查询映射
|
||
var categories = service.GetMappingsByQuestionAsync(questionId).GetAwaiter().GetResult();
|
||
|
||
// Assert: 应该包含关联的分类ID
|
||
return categories.Any(c => c.Id == categoryId);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Property 10: 通过分类ID查询映射应包含关联的题目ID
|
||
/// *For any* QuestionCategoryMapping, querying mappings by CategoryId SHALL include the QuestionId.
|
||
///
|
||
/// **Validates: Requirements 7.2**
|
||
/// </summary>
|
||
[Property(MaxTest = 100)]
|
||
public bool Bidirectionality_QueryByCategory_ShouldIncludeQuestionId(PositiveInt seed)
|
||
{
|
||
// Arrange: 创建题目、分类和映射关系
|
||
using var dbContext = CreateDbContext();
|
||
var (assessmentTypeId, questionId, categoryId) = CreateTestData(dbContext, seed.Get);
|
||
|
||
// 创建映射关系
|
||
var mapping = new QuestionCategoryMapping
|
||
{
|
||
QuestionId = questionId,
|
||
CategoryId = categoryId,
|
||
CreateTime = DateTime.Now
|
||
};
|
||
dbContext.QuestionCategoryMappings.Add(mapping);
|
||
dbContext.SaveChanges();
|
||
|
||
var service = new AssessmentService(dbContext, _mockLogger.Object);
|
||
|
||
// Act: 通过分类ID查询映射
|
||
var questions = service.GetMappingsByCategoryAsync(categoryId).GetAwaiter().GetResult();
|
||
|
||
// Assert: 应该包含关联的题目ID
|
||
return questions.Any(q => q.Id == questionId);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Property 10: 双向查询应返回一致的映射关系
|
||
/// 如果通过题目查询到分类A,则通过分类A查询应能找到该题目
|
||
///
|
||
/// **Validates: Requirements 7.1, 7.2**
|
||
/// </summary>
|
||
[Property(MaxTest = 100)]
|
||
public bool Bidirectionality_ConsistentBothDirections(PositiveInt seed, PositiveInt categoryCount)
|
||
{
|
||
// 限制分类数量为1-5
|
||
var actualCategoryCount = (categoryCount.Get % 5) + 1;
|
||
|
||
// Arrange: 创建题目和多个分类
|
||
using var dbContext = CreateDbContext();
|
||
var assessmentTypeId = CreateAssessmentType(dbContext, seed.Get);
|
||
var questionId = CreateQuestion(dbContext, assessmentTypeId, seed.Get);
|
||
var categoryIds = new List<long>();
|
||
|
||
for (int i = 0; i < actualCategoryCount; i++)
|
||
{
|
||
var categoryId = CreateCategory(dbContext, assessmentTypeId, seed.Get + i);
|
||
categoryIds.Add(categoryId);
|
||
|
||
// 创建映射关系
|
||
var mapping = new QuestionCategoryMapping
|
||
{
|
||
QuestionId = questionId,
|
||
CategoryId = categoryId,
|
||
CreateTime = DateTime.Now
|
||
};
|
||
dbContext.QuestionCategoryMappings.Add(mapping);
|
||
}
|
||
dbContext.SaveChanges();
|
||
|
||
var service = new AssessmentService(dbContext, _mockLogger.Object);
|
||
|
||
// Act: 通过题目查询分类
|
||
var categoriesFromQuestion = service.GetMappingsByQuestionAsync(questionId).GetAwaiter().GetResult();
|
||
|
||
// Assert: 对于每个查询到的分类,反向查询应能找到原题目
|
||
foreach (var category in categoriesFromQuestion)
|
||
{
|
||
var questionsFromCategory = service.GetMappingsByCategoryAsync(category.Id).GetAwaiter().GetResult();
|
||
if (!questionsFromCategory.Any(q => q.Id == questionId))
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Property 10: 多对多映射关系的双向一致性
|
||
/// 多个题目关联同一分类时,通过分类查询应返回所有关联的题目
|
||
///
|
||
/// **Validates: Requirements 7.1, 7.2**
|
||
/// </summary>
|
||
[Property(MaxTest = 50)]
|
||
public bool Bidirectionality_ManyToMany_Consistency(PositiveInt seed, PositiveInt questionCount)
|
||
{
|
||
// 限制题目数量为2-5
|
||
var actualQuestionCount = (questionCount.Get % 4) + 2;
|
||
|
||
// Arrange: 创建多个题目和一个分类
|
||
using var dbContext = CreateDbContext();
|
||
var assessmentTypeId = CreateAssessmentType(dbContext, seed.Get);
|
||
var categoryId = CreateCategory(dbContext, assessmentTypeId, seed.Get);
|
||
var questionIds = new List<long>();
|
||
|
||
for (int i = 0; i < actualQuestionCount; i++)
|
||
{
|
||
var questionId = CreateQuestion(dbContext, assessmentTypeId, seed.Get + i);
|
||
questionIds.Add(questionId);
|
||
|
||
// 创建映射关系
|
||
var mapping = new QuestionCategoryMapping
|
||
{
|
||
QuestionId = questionId,
|
||
CategoryId = categoryId,
|
||
CreateTime = DateTime.Now
|
||
};
|
||
dbContext.QuestionCategoryMappings.Add(mapping);
|
||
}
|
||
dbContext.SaveChanges();
|
||
|
||
var service = new AssessmentService(dbContext, _mockLogger.Object);
|
||
|
||
// Act: 通过分类查询题目
|
||
var questionsFromCategory = service.GetMappingsByCategoryAsync(categoryId).GetAwaiter().GetResult();
|
||
|
||
// Assert: 应该返回所有关联的题目
|
||
if (questionsFromCategory.Count != actualQuestionCount) return false;
|
||
|
||
foreach (var questionId in questionIds)
|
||
{
|
||
if (!questionsFromCategory.Any(q => q.Id == questionId))
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Property 10: 软删除的题目不应出现在分类的映射查询结果中
|
||
///
|
||
/// **Validates: Requirements 7.2**
|
||
/// </summary>
|
||
[Property(MaxTest = 100)]
|
||
public bool Bidirectionality_DeletedQuestionNotInCategoryQuery(PositiveInt seed)
|
||
{
|
||
// Arrange: 创建题目、分类和映射关系
|
||
using var dbContext = CreateDbContext();
|
||
var assessmentTypeId = CreateAssessmentType(dbContext, seed.Get);
|
||
var categoryId = CreateCategory(dbContext, assessmentTypeId, seed.Get);
|
||
|
||
// 创建正常题目
|
||
var normalQuestionId = CreateQuestion(dbContext, assessmentTypeId, seed.Get);
|
||
dbContext.QuestionCategoryMappings.Add(new QuestionCategoryMapping
|
||
{
|
||
QuestionId = normalQuestionId,
|
||
CategoryId = categoryId,
|
||
CreateTime = DateTime.Now
|
||
});
|
||
|
||
// 创建已删除的题目
|
||
var deletedQuestion = new Question
|
||
{
|
||
AssessmentTypeId = assessmentTypeId,
|
||
QuestionNo = seed.Get + 1000,
|
||
Content = $"Deleted Question {seed.Get}",
|
||
Sort = 1,
|
||
Status = 1,
|
||
CreateTime = DateTime.Now,
|
||
UpdateTime = DateTime.Now,
|
||
IsDeleted = true // 已软删除
|
||
};
|
||
dbContext.Questions.Add(deletedQuestion);
|
||
dbContext.SaveChanges();
|
||
|
||
dbContext.QuestionCategoryMappings.Add(new QuestionCategoryMapping
|
||
{
|
||
QuestionId = deletedQuestion.Id,
|
||
CategoryId = categoryId,
|
||
CreateTime = DateTime.Now
|
||
});
|
||
dbContext.SaveChanges();
|
||
|
||
var service = new AssessmentService(dbContext, _mockLogger.Object);
|
||
|
||
// Act: 通过分类查询题目
|
||
var questions = service.GetMappingsByCategoryAsync(categoryId).GetAwaiter().GetResult();
|
||
|
||
// Assert: 只应该返回正常的题目,不应包含已删除的题目
|
||
return questions.Count == 1 && questions[0].Id == normalQuestionId;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Property 10: 软删除的分类不应出现在题目的映射查询结果中
|
||
///
|
||
/// **Validates: Requirements 7.1**
|
||
/// </summary>
|
||
[Property(MaxTest = 100)]
|
||
public bool Bidirectionality_DeletedCategoryNotInQuestionQuery(PositiveInt seed)
|
||
{
|
||
// Arrange: 创建题目和分类
|
||
using var dbContext = CreateDbContext();
|
||
var assessmentTypeId = CreateAssessmentType(dbContext, seed.Get);
|
||
var questionId = CreateQuestion(dbContext, assessmentTypeId, seed.Get);
|
||
|
||
// 创建正常分类
|
||
var normalCategoryId = CreateCategory(dbContext, assessmentTypeId, seed.Get);
|
||
dbContext.QuestionCategoryMappings.Add(new QuestionCategoryMapping
|
||
{
|
||
QuestionId = questionId,
|
||
CategoryId = normalCategoryId,
|
||
CreateTime = DateTime.Now
|
||
});
|
||
|
||
// 创建已删除的分类
|
||
var deletedCategory = new ReportCategory
|
||
{
|
||
AssessmentTypeId = assessmentTypeId,
|
||
ParentId = 0,
|
||
Name = $"Deleted Category {seed.Get}",
|
||
Code = $"DELETED_{seed.Get}_{Guid.NewGuid():N}".Substring(0, 20),
|
||
CategoryType = 1,
|
||
ScoreRule = 1,
|
||
Sort = 1,
|
||
CreateTime = DateTime.Now,
|
||
UpdateTime = DateTime.Now,
|
||
IsDeleted = true // 已软删除
|
||
};
|
||
dbContext.ReportCategories.Add(deletedCategory);
|
||
dbContext.SaveChanges();
|
||
|
||
dbContext.QuestionCategoryMappings.Add(new QuestionCategoryMapping
|
||
{
|
||
QuestionId = questionId,
|
||
CategoryId = deletedCategory.Id,
|
||
CreateTime = DateTime.Now
|
||
});
|
||
dbContext.SaveChanges();
|
||
|
||
var service = new AssessmentService(dbContext, _mockLogger.Object);
|
||
|
||
// Act: 通过题目查询分类
|
||
var categories = service.GetMappingsByQuestionAsync(questionId).GetAwaiter().GetResult();
|
||
|
||
// Assert: 只应该返回正常的分类,不应包含已删除的分类
|
||
return categories.Count == 1 && categories[0].Id == normalCategoryId;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Property 11: Batch Operation Atomicity
|
||
|
||
/// <summary>
|
||
/// Property 11: 批量更新成功时所有映射都应被更新
|
||
/// *For any* batch update of question-category mappings, all mappings SHALL be updated successfully.
|
||
///
|
||
/// **Validates: Requirements 7.6**
|
||
/// </summary>
|
||
[Property(MaxTest = 100)]
|
||
public bool Atomicity_SuccessfulUpdate_AllMappingsUpdated(PositiveInt seed, PositiveInt categoryCount)
|
||
{
|
||
// 限制分类数量为1-5
|
||
var actualCategoryCount = (categoryCount.Get % 5) + 1;
|
||
|
||
// Arrange: 创建题目和多个分类
|
||
using var dbContext = CreateDbContext();
|
||
var assessmentTypeId = CreateAssessmentType(dbContext, seed.Get);
|
||
var questionId = CreateQuestion(dbContext, assessmentTypeId, seed.Get);
|
||
var categoryIds = new List<long>();
|
||
|
||
for (int i = 0; i < actualCategoryCount; i++)
|
||
{
|
||
var categoryId = CreateCategory(dbContext, assessmentTypeId, seed.Get + i);
|
||
categoryIds.Add(categoryId);
|
||
}
|
||
|
||
var service = new AssessmentService(dbContext, _mockLogger.Object);
|
||
|
||
// Act: 批量更新映射
|
||
var request = new BatchUpdateMappingsRequest
|
||
{
|
||
QuestionId = questionId,
|
||
CategoryIds = categoryIds
|
||
};
|
||
|
||
try
|
||
{
|
||
var result = service.BatchUpdateMappingsAsync(request).GetAwaiter().GetResult();
|
||
if (!result) return false;
|
||
|
||
// Assert: 验证所有映射都已创建
|
||
var mappings = dbContext.QuestionCategoryMappings
|
||
.Where(m => m.QuestionId == questionId)
|
||
.ToList();
|
||
|
||
if (mappings.Count != actualCategoryCount) return false;
|
||
|
||
foreach (var categoryId in categoryIds)
|
||
{
|
||
if (!mappings.Any(m => m.CategoryId == categoryId))
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
catch (BusinessException)
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Property 11: 批量更新应替换所有现有映射
|
||
///
|
||
/// **Validates: Requirements 7.6**
|
||
/// </summary>
|
||
[Property(MaxTest = 100)]
|
||
public bool Atomicity_UpdateReplacesExistingMappings(PositiveInt seed)
|
||
{
|
||
// Arrange: 创建题目和分类,并创建初始映射
|
||
using var dbContext = CreateDbContext();
|
||
var assessmentTypeId = CreateAssessmentType(dbContext, seed.Get);
|
||
var questionId = CreateQuestion(dbContext, assessmentTypeId, seed.Get);
|
||
|
||
// 创建初始分类和映射
|
||
var oldCategoryId = CreateCategory(dbContext, assessmentTypeId, seed.Get);
|
||
dbContext.QuestionCategoryMappings.Add(new QuestionCategoryMapping
|
||
{
|
||
QuestionId = questionId,
|
||
CategoryId = oldCategoryId,
|
||
CreateTime = DateTime.Now
|
||
});
|
||
dbContext.SaveChanges();
|
||
|
||
// 创建新分类
|
||
var newCategoryId = CreateCategory(dbContext, assessmentTypeId, seed.Get + 100);
|
||
|
||
var service = new AssessmentService(dbContext, _mockLogger.Object);
|
||
|
||
// Act: 批量更新映射(用新分类替换旧分类)
|
||
var request = new BatchUpdateMappingsRequest
|
||
{
|
||
QuestionId = questionId,
|
||
CategoryIds = new List<long> { newCategoryId }
|
||
};
|
||
|
||
try
|
||
{
|
||
var result = service.BatchUpdateMappingsAsync(request).GetAwaiter().GetResult();
|
||
if (!result) return false;
|
||
|
||
// Assert: 验证旧映射已删除,新映射已创建
|
||
var mappings = dbContext.QuestionCategoryMappings
|
||
.Where(m => m.QuestionId == questionId)
|
||
.ToList();
|
||
|
||
// 应该只有一个映射(新的)
|
||
if (mappings.Count != 1) return false;
|
||
if (mappings[0].CategoryId != newCategoryId) return false;
|
||
|
||
// 旧映射应该不存在
|
||
var oldMappingExists = dbContext.QuestionCategoryMappings
|
||
.Any(m => m.QuestionId == questionId && m.CategoryId == oldCategoryId);
|
||
|
||
return !oldMappingExists;
|
||
}
|
||
catch (BusinessException)
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Property 11: 批量更新失败时不应有任何映射被更新(回滚)
|
||
/// 当提供无效的分类ID时,所有操作应回滚
|
||
///
|
||
/// **Validates: Requirements 7.6**
|
||
/// </summary>
|
||
[Property(MaxTest = 100)]
|
||
public bool Atomicity_FailedUpdate_NoMappingsChanged(PositiveInt seed)
|
||
{
|
||
// Arrange: 创建题目和分类,并创建初始映射
|
||
using var dbContext = CreateDbContext();
|
||
var assessmentTypeId = CreateAssessmentType(dbContext, seed.Get);
|
||
var questionId = CreateQuestion(dbContext, assessmentTypeId, seed.Get);
|
||
|
||
// 创建初始分类和映射
|
||
var existingCategoryId = CreateCategory(dbContext, assessmentTypeId, seed.Get);
|
||
dbContext.QuestionCategoryMappings.Add(new QuestionCategoryMapping
|
||
{
|
||
QuestionId = questionId,
|
||
CategoryId = existingCategoryId,
|
||
CreateTime = DateTime.Now
|
||
});
|
||
dbContext.SaveChanges();
|
||
|
||
var service = new AssessmentService(dbContext, _mockLogger.Object);
|
||
|
||
// Act: 尝试批量更新映射(包含无效的分类ID)
|
||
var request = new BatchUpdateMappingsRequest
|
||
{
|
||
QuestionId = questionId,
|
||
CategoryIds = new List<long> { existingCategoryId, 999999 } // 999999 是无效的分类ID
|
||
};
|
||
|
||
try
|
||
{
|
||
service.BatchUpdateMappingsAsync(request).GetAwaiter().GetResult();
|
||
return false; // 应该抛出异常
|
||
}
|
||
catch (BusinessException ex)
|
||
{
|
||
// Assert: 验证原有映射未被修改
|
||
if (ex.Code != ErrorCodes.CategoryNotFound) return false;
|
||
|
||
var mappings = dbContext.QuestionCategoryMappings
|
||
.Where(m => m.QuestionId == questionId)
|
||
.ToList();
|
||
|
||
// 原有映射应该保持不变
|
||
return mappings.Count == 1 && mappings[0].CategoryId == existingCategoryId;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Property 11: 空分类列表应清除所有映射
|
||
///
|
||
/// **Validates: Requirements 7.5, 7.6**
|
||
/// </summary>
|
||
[Property(MaxTest = 100)]
|
||
public bool Atomicity_EmptyCategoryList_ClearsAllMappings(PositiveInt seed)
|
||
{
|
||
// Arrange: 创建题目和分类,并创建初始映射
|
||
using var dbContext = CreateDbContext();
|
||
var assessmentTypeId = CreateAssessmentType(dbContext, seed.Get);
|
||
var questionId = CreateQuestion(dbContext, assessmentTypeId, seed.Get);
|
||
|
||
// 创建多个初始映射
|
||
for (int i = 0; i < 3; i++)
|
||
{
|
||
var categoryId = CreateCategory(dbContext, assessmentTypeId, seed.Get + i);
|
||
dbContext.QuestionCategoryMappings.Add(new QuestionCategoryMapping
|
||
{
|
||
QuestionId = questionId,
|
||
CategoryId = categoryId,
|
||
CreateTime = DateTime.Now
|
||
});
|
||
}
|
||
dbContext.SaveChanges();
|
||
|
||
var service = new AssessmentService(dbContext, _mockLogger.Object);
|
||
|
||
// Act: 批量更新映射(空分类列表)
|
||
var request = new BatchUpdateMappingsRequest
|
||
{
|
||
QuestionId = questionId,
|
||
CategoryIds = new List<long>() // 空列表
|
||
};
|
||
|
||
try
|
||
{
|
||
var result = service.BatchUpdateMappingsAsync(request).GetAwaiter().GetResult();
|
||
if (!result) return false;
|
||
|
||
// Assert: 验证所有映射都已删除
|
||
var mappings = dbContext.QuestionCategoryMappings
|
||
.Where(m => m.QuestionId == questionId)
|
||
.ToList();
|
||
|
||
return mappings.Count == 0;
|
||
}
|
||
catch (BusinessException)
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Property 11: 批量更新应去重分类ID
|
||
///
|
||
/// **Validates: Requirements 7.4, 7.6**
|
||
/// </summary>
|
||
[Property(MaxTest = 100)]
|
||
public bool Atomicity_DuplicateCategoryIds_ShouldBeDeduplicated(PositiveInt seed)
|
||
{
|
||
// Arrange: 创建题目和分类
|
||
using var dbContext = CreateDbContext();
|
||
var assessmentTypeId = CreateAssessmentType(dbContext, seed.Get);
|
||
var questionId = CreateQuestion(dbContext, assessmentTypeId, seed.Get);
|
||
var categoryId = CreateCategory(dbContext, assessmentTypeId, seed.Get);
|
||
|
||
var service = new AssessmentService(dbContext, _mockLogger.Object);
|
||
|
||
// Act: 批量更新映射(包含重复的分类ID)
|
||
var request = new BatchUpdateMappingsRequest
|
||
{
|
||
QuestionId = questionId,
|
||
CategoryIds = new List<long> { categoryId, categoryId, categoryId } // 重复的分类ID
|
||
};
|
||
|
||
try
|
||
{
|
||
var result = service.BatchUpdateMappingsAsync(request).GetAwaiter().GetResult();
|
||
if (!result) return false;
|
||
|
||
// Assert: 验证只创建了一个映射(去重后)
|
||
var mappings = dbContext.QuestionCategoryMappings
|
||
.Where(m => m.QuestionId == questionId)
|
||
.ToList();
|
||
|
||
return mappings.Count == 1 && mappings[0].CategoryId == categoryId;
|
||
}
|
||
catch (BusinessException)
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Property 11: 对不存在的题目进行批量更新应失败
|
||
///
|
||
/// **Validates: Requirements 7.3, 7.6**
|
||
/// </summary>
|
||
[Property(MaxTest = 100)]
|
||
public bool Atomicity_InvalidQuestionId_ShouldFail(PositiveInt seed)
|
||
{
|
||
// Arrange
|
||
using var dbContext = CreateDbContext();
|
||
var assessmentTypeId = CreateAssessmentType(dbContext, seed.Get);
|
||
var categoryId = CreateCategory(dbContext, assessmentTypeId, seed.Get);
|
||
|
||
var service = new AssessmentService(dbContext, _mockLogger.Object);
|
||
|
||
// Act: 尝试对不存在的题目进行批量更新
|
||
var request = new BatchUpdateMappingsRequest
|
||
{
|
||
QuestionId = 999999, // 不存在的题目ID
|
||
CategoryIds = new List<long> { categoryId }
|
||
};
|
||
|
||
try
|
||
{
|
||
service.BatchUpdateMappingsAsync(request).GetAwaiter().GetResult();
|
||
return false; // 应该抛出异常
|
||
}
|
||
catch (BusinessException ex)
|
||
{
|
||
return ex.Code == ErrorCodes.QuestionNotFound;
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 辅助方法
|
||
|
||
/// <summary>
|
||
/// 创建内存数据库上下文
|
||
/// </summary>
|
||
private AdminBusinessDbContext CreateDbContext()
|
||
{
|
||
var options = new DbContextOptionsBuilder<AdminBusinessDbContext>()
|
||
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
|
||
.ConfigureWarnings(w => w.Ignore(Microsoft.EntityFrameworkCore.Diagnostics.InMemoryEventId.TransactionIgnoredWarning))
|
||
.Options;
|
||
|
||
return new AdminBusinessDbContext(options);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建测评类型并返回ID
|
||
/// </summary>
|
||
private long CreateAssessmentType(AdminBusinessDbContext dbContext, int seed)
|
||
{
|
||
var assessmentType = new AssessmentType
|
||
{
|
||
Name = $"Test Assessment {seed}",
|
||
Code = $"TEST_{seed}_{Guid.NewGuid():N}".Substring(0, 20),
|
||
Price = 99.99m,
|
||
QuestionCount = 80,
|
||
Sort = 1,
|
||
Status = 1,
|
||
CreateTime = DateTime.Now,
|
||
UpdateTime = DateTime.Now,
|
||
IsDeleted = false
|
||
};
|
||
dbContext.AssessmentTypes.Add(assessmentType);
|
||
dbContext.SaveChanges();
|
||
return assessmentType.Id;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建题目并返回ID
|
||
/// </summary>
|
||
private long CreateQuestion(AdminBusinessDbContext dbContext, long assessmentTypeId, int seed)
|
||
{
|
||
var question = new Question
|
||
{
|
||
AssessmentTypeId = assessmentTypeId,
|
||
QuestionNo = seed,
|
||
Content = $"Test Question {seed}",
|
||
Sort = 1,
|
||
Status = 1,
|
||
CreateTime = DateTime.Now,
|
||
UpdateTime = DateTime.Now,
|
||
IsDeleted = false
|
||
};
|
||
dbContext.Questions.Add(question);
|
||
dbContext.SaveChanges();
|
||
return question.Id;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建分类并返回ID
|
||
/// </summary>
|
||
private long CreateCategory(AdminBusinessDbContext dbContext, long assessmentTypeId, int seed)
|
||
{
|
||
var category = new ReportCategory
|
||
{
|
||
AssessmentTypeId = assessmentTypeId,
|
||
ParentId = 0,
|
||
Name = $"Test Category {seed}",
|
||
Code = $"CAT_{seed}_{Guid.NewGuid():N}".Substring(0, 20),
|
||
CategoryType = (seed % 8) + 1,
|
||
ScoreRule = (seed % 2) + 1,
|
||
Sort = 1,
|
||
CreateTime = DateTime.Now,
|
||
UpdateTime = DateTime.Now,
|
||
IsDeleted = false
|
||
};
|
||
dbContext.ReportCategories.Add(category);
|
||
dbContext.SaveChanges();
|
||
return category.Id;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建测试数据(测评类型、题目、分类)
|
||
/// </summary>
|
||
private (long assessmentTypeId, long questionId, long categoryId) CreateTestData(
|
||
AdminBusinessDbContext dbContext, int seed)
|
||
{
|
||
var assessmentTypeId = CreateAssessmentType(dbContext, seed);
|
||
var questionId = CreateQuestion(dbContext, assessmentTypeId, seed);
|
||
var categoryId = CreateCategory(dbContext, assessmentTypeId, seed);
|
||
return (assessmentTypeId, questionId, categoryId);
|
||
}
|
||
|
||
#endregion
|
||
}
|