- 系统配置管理模块 (Config) - 内容管理模块 (Banner, Promotion) - 测评管理模块 (Type, Question, Category, Mapping, Conclusion) - 用户管理模块 (User) - 订单管理模块 (Order) - 规划师管理模块 (Planner) - 分销管理模块 (InviteCode, Commission, Withdrawal) - 数据统计仪表盘模块 (Dashboard) - 权限控制集成 - 服务注册配置 全部381个测试通过
769 lines
26 KiB
C#
769 lines
26 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>
|
|
/// Question 属性测试
|
|
/// 验证题目服务的正确性属性
|
|
/// </summary>
|
|
public class QuestionPropertyTests
|
|
{
|
|
private readonly Mock<ILogger<AssessmentService>> _mockLogger = new();
|
|
|
|
#region Property 5: Required Field Validation
|
|
|
|
/// <summary>
|
|
/// Property 5: 创建题目时缺少测评类型ID应该失败
|
|
/// *For any* create operation with missing required fields, the service SHALL return a validation error and SHALL NOT create the entity.
|
|
///
|
|
/// **Validates: Requirements 5.2**
|
|
/// </summary>
|
|
[Property(MaxTest = 100)]
|
|
public bool RequiredFieldValidation_MissingAssessmentTypeId_ShouldFail(PositiveInt seed)
|
|
{
|
|
// Arrange
|
|
using var dbContext = CreateDbContext();
|
|
var service = new AssessmentService(dbContext, _mockLogger.Object);
|
|
|
|
var request = new CreateQuestionRequest
|
|
{
|
|
AssessmentTypeId = 0, // 无效的测评类型ID
|
|
QuestionNo = seed.Get,
|
|
Content = $"Test content {seed.Get}",
|
|
Sort = 1,
|
|
Status = 1
|
|
};
|
|
|
|
// Act & Assert
|
|
try
|
|
{
|
|
service.CreateQuestionAsync(request).GetAwaiter().GetResult();
|
|
return false; // 应该抛出异常
|
|
}
|
|
catch (BusinessException ex)
|
|
{
|
|
// 验证没有创建任何题目
|
|
var questionCount = dbContext.Questions.Count();
|
|
return ex.Code == ErrorCodes.ParamError && questionCount == 0;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Property 5: 创建题目时缺少题号应该失败
|
|
/// *For any* create operation with missing required fields, the service SHALL return a validation error and SHALL NOT create the entity.
|
|
///
|
|
/// **Validates: Requirements 5.2**
|
|
/// </summary>
|
|
[Property(MaxTest = 100)]
|
|
public bool RequiredFieldValidation_MissingQuestionNo_ShouldFail(PositiveInt seed)
|
|
{
|
|
// Arrange
|
|
using var dbContext = CreateDbContext();
|
|
|
|
// 先创建一个测评类型
|
|
var assessmentType = CreateAssessmentType(dbContext, seed.Get);
|
|
|
|
var service = new AssessmentService(dbContext, _mockLogger.Object);
|
|
|
|
var request = new CreateQuestionRequest
|
|
{
|
|
AssessmentTypeId = assessmentType.Id,
|
|
QuestionNo = 0, // 无效的题号
|
|
Content = $"Test content {seed.Get}",
|
|
Sort = 1,
|
|
Status = 1
|
|
};
|
|
|
|
// Act & Assert
|
|
try
|
|
{
|
|
service.CreateQuestionAsync(request).GetAwaiter().GetResult();
|
|
return false; // 应该抛出异常
|
|
}
|
|
catch (BusinessException ex)
|
|
{
|
|
// 验证没有创建任何题目
|
|
var questionCount = dbContext.Questions.Count();
|
|
return ex.Code == ErrorCodes.ParamError && questionCount == 0;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Property 5: 创建题目时缺少内容应该失败
|
|
/// *For any* create operation with missing required fields, the service SHALL return a validation error and SHALL NOT create the entity.
|
|
///
|
|
/// **Validates: Requirements 5.2**
|
|
/// </summary>
|
|
[Property(MaxTest = 100)]
|
|
public bool RequiredFieldValidation_MissingContent_ShouldFail(PositiveInt seed)
|
|
{
|
|
// Arrange
|
|
using var dbContext = CreateDbContext();
|
|
|
|
// 先创建一个测评类型
|
|
var assessmentType = CreateAssessmentType(dbContext, seed.Get);
|
|
|
|
var service = new AssessmentService(dbContext, _mockLogger.Object);
|
|
|
|
var request = new CreateQuestionRequest
|
|
{
|
|
AssessmentTypeId = assessmentType.Id,
|
|
QuestionNo = seed.Get,
|
|
Content = "", // 空内容
|
|
Sort = 1,
|
|
Status = 1
|
|
};
|
|
|
|
// Act & Assert
|
|
try
|
|
{
|
|
service.CreateQuestionAsync(request).GetAwaiter().GetResult();
|
|
return false; // 应该抛出异常
|
|
}
|
|
catch (BusinessException ex)
|
|
{
|
|
// 验证没有创建任何题目
|
|
var questionCount = dbContext.Questions.Count();
|
|
return ex.Code == ErrorCodes.ParamError && questionCount == 0;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Property 5: 创建题目时内容为空白字符串应该失败
|
|
/// *For any* create operation with missing required fields, the service SHALL return a validation error and SHALL NOT create the entity.
|
|
///
|
|
/// **Validates: Requirements 5.2**
|
|
/// </summary>
|
|
[Property(MaxTest = 100)]
|
|
public bool RequiredFieldValidation_WhitespaceContent_ShouldFail(PositiveInt seed)
|
|
{
|
|
// Arrange
|
|
using var dbContext = CreateDbContext();
|
|
|
|
// 先创建一个测评类型
|
|
var assessmentType = CreateAssessmentType(dbContext, seed.Get);
|
|
|
|
var service = new AssessmentService(dbContext, _mockLogger.Object);
|
|
|
|
var request = new CreateQuestionRequest
|
|
{
|
|
AssessmentTypeId = assessmentType.Id,
|
|
QuestionNo = seed.Get,
|
|
Content = " ", // 空白字符串
|
|
Sort = 1,
|
|
Status = 1
|
|
};
|
|
|
|
// Act & Assert
|
|
try
|
|
{
|
|
service.CreateQuestionAsync(request).GetAwaiter().GetResult();
|
|
return false; // 应该抛出异常
|
|
}
|
|
catch (BusinessException ex)
|
|
{
|
|
// 验证没有创建任何题目
|
|
var questionCount = dbContext.Questions.Count();
|
|
return ex.Code == ErrorCodes.ParamError && questionCount == 0;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Property 5: 创建题目时测评类型不存在应该失败
|
|
/// *For any* create operation with missing required fields, the service SHALL return a validation error and SHALL NOT create the entity.
|
|
///
|
|
/// **Validates: Requirements 5.2**
|
|
/// </summary>
|
|
[Property(MaxTest = 100)]
|
|
public bool RequiredFieldValidation_NonExistentAssessmentType_ShouldFail(PositiveInt seed)
|
|
{
|
|
// Arrange
|
|
using var dbContext = CreateDbContext();
|
|
var service = new AssessmentService(dbContext, _mockLogger.Object);
|
|
|
|
var request = new CreateQuestionRequest
|
|
{
|
|
AssessmentTypeId = seed.Get + 1000, // 不存在的测评类型ID
|
|
QuestionNo = seed.Get,
|
|
Content = $"Test content {seed.Get}",
|
|
Sort = 1,
|
|
Status = 1
|
|
};
|
|
|
|
// Act & Assert
|
|
try
|
|
{
|
|
service.CreateQuestionAsync(request).GetAwaiter().GetResult();
|
|
return false; // 应该抛出异常
|
|
}
|
|
catch (BusinessException ex)
|
|
{
|
|
// 验证没有创建任何题目
|
|
var questionCount = dbContext.Questions.Count();
|
|
return ex.Code == ErrorCodes.AssessmentTypeNotFound && questionCount == 0;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Property 5: 提供所有必填字段时创建题目应该成功
|
|
///
|
|
/// **Validates: Requirements 5.2**
|
|
/// </summary>
|
|
[Property(MaxTest = 100)]
|
|
public bool RequiredFieldValidation_AllRequiredFieldsProvided_ShouldSucceed(PositiveInt seed)
|
|
{
|
|
// Arrange
|
|
using var dbContext = CreateDbContext();
|
|
|
|
// 先创建一个测评类型
|
|
var assessmentType = CreateAssessmentType(dbContext, seed.Get);
|
|
|
|
var service = new AssessmentService(dbContext, _mockLogger.Object);
|
|
|
|
var request = new CreateQuestionRequest
|
|
{
|
|
AssessmentTypeId = assessmentType.Id,
|
|
QuestionNo = seed.Get,
|
|
Content = $"Test content {seed.Get}",
|
|
Sort = 1,
|
|
Status = 1
|
|
};
|
|
|
|
// Act
|
|
try
|
|
{
|
|
var questionId = service.CreateQuestionAsync(request).GetAwaiter().GetResult();
|
|
|
|
// Assert: 验证题目已创建
|
|
var createdQuestion = dbContext.Questions.FirstOrDefault(q => q.Id == questionId);
|
|
return createdQuestion != null
|
|
&& createdQuestion.AssessmentTypeId == assessmentType.Id
|
|
&& createdQuestion.QuestionNo == seed.Get
|
|
&& createdQuestion.Content == $"Test content {seed.Get}";
|
|
}
|
|
catch (BusinessException)
|
|
{
|
|
return false; // 不应该抛出异常
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Property 8: Unique Constraint Enforcement (QuestionNo)
|
|
|
|
/// <summary>
|
|
/// Property 8: 同一测评类型内创建重复题号应该失败
|
|
/// *For any* Question within the same AssessmentTypeId, the QuestionNo SHALL be unique.
|
|
///
|
|
/// **Validates: Requirements 5.3**
|
|
/// </summary>
|
|
[Property(MaxTest = 100)]
|
|
public bool UniqueQuestionNoEnforcement_DuplicateQuestionNoInSameType_ShouldFail(PositiveInt seed)
|
|
{
|
|
// Arrange
|
|
using var dbContext = CreateDbContext();
|
|
|
|
// 先创建一个测评类型
|
|
var assessmentType = CreateAssessmentType(dbContext, seed.Get);
|
|
|
|
// 创建一个已存在的题目
|
|
var existingQuestion = new Question
|
|
{
|
|
AssessmentTypeId = assessmentType.Id,
|
|
QuestionNo = seed.Get,
|
|
Content = $"Existing question {seed.Get}",
|
|
Sort = 1,
|
|
Status = 1,
|
|
CreateTime = DateTime.Now,
|
|
UpdateTime = DateTime.Now,
|
|
IsDeleted = false
|
|
};
|
|
dbContext.Questions.Add(existingQuestion);
|
|
dbContext.SaveChanges();
|
|
|
|
var service = new AssessmentService(dbContext, _mockLogger.Object);
|
|
|
|
// Act: 尝试创建具有相同题号的题目
|
|
var request = new CreateQuestionRequest
|
|
{
|
|
AssessmentTypeId = assessmentType.Id,
|
|
QuestionNo = seed.Get, // 使用相同的题号
|
|
Content = $"New question {seed.Get}",
|
|
Sort = 2,
|
|
Status = 1
|
|
};
|
|
|
|
// Assert
|
|
try
|
|
{
|
|
service.CreateQuestionAsync(request).GetAwaiter().GetResult();
|
|
return false; // 应该抛出异常
|
|
}
|
|
catch (BusinessException ex)
|
|
{
|
|
return ex.Code == ErrorCodes.QuestionNoExists;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Property 8: 不同测评类型内可以使用相同题号
|
|
/// *For any* Question within the same AssessmentTypeId, the QuestionNo SHALL be unique.
|
|
///
|
|
/// **Validates: Requirements 5.3**
|
|
/// </summary>
|
|
[Property(MaxTest = 100)]
|
|
public bool UniqueQuestionNoEnforcement_SameQuestionNoInDifferentTypes_ShouldSucceed(PositiveInt seed)
|
|
{
|
|
// Arrange
|
|
using var dbContext = CreateDbContext();
|
|
|
|
// 创建两个测评类型
|
|
var assessmentType1 = CreateAssessmentType(dbContext, seed.Get, "TYPE_A");
|
|
var assessmentType2 = CreateAssessmentType(dbContext, seed.Get + 1000, "TYPE_B");
|
|
|
|
// 在第一个测评类型中创建题目
|
|
var existingQuestion = new Question
|
|
{
|
|
AssessmentTypeId = assessmentType1.Id,
|
|
QuestionNo = seed.Get,
|
|
Content = $"Question in type 1 {seed.Get}",
|
|
Sort = 1,
|
|
Status = 1,
|
|
CreateTime = DateTime.Now,
|
|
UpdateTime = DateTime.Now,
|
|
IsDeleted = false
|
|
};
|
|
dbContext.Questions.Add(existingQuestion);
|
|
dbContext.SaveChanges();
|
|
|
|
var service = new AssessmentService(dbContext, _mockLogger.Object);
|
|
|
|
// Act: 在第二个测评类型中创建具有相同题号的题目
|
|
var request = new CreateQuestionRequest
|
|
{
|
|
AssessmentTypeId = assessmentType2.Id,
|
|
QuestionNo = seed.Get, // 使用相同的题号,但在不同的测评类型中
|
|
Content = $"Question in type 2 {seed.Get}",
|
|
Sort = 1,
|
|
Status = 1
|
|
};
|
|
|
|
// Assert
|
|
try
|
|
{
|
|
var newQuestionId = service.CreateQuestionAsync(request).GetAwaiter().GetResult();
|
|
var newQuestion = dbContext.Questions.FirstOrDefault(q => q.Id == newQuestionId);
|
|
return newQuestion != null
|
|
&& newQuestion.AssessmentTypeId == assessmentType2.Id
|
|
&& newQuestion.QuestionNo == seed.Get;
|
|
}
|
|
catch (BusinessException)
|
|
{
|
|
return false; // 不应该抛出异常
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Property 8: 更新题目时使用已存在的题号应该失败
|
|
/// *For any* Question within the same AssessmentTypeId, the QuestionNo SHALL be unique.
|
|
///
|
|
/// **Validates: Requirements 5.3**
|
|
/// </summary>
|
|
[Property(MaxTest = 100)]
|
|
public bool UniqueQuestionNoEnforcement_UpdateWithDuplicateQuestionNo_ShouldFail(PositiveInt seed)
|
|
{
|
|
// Arrange
|
|
using var dbContext = CreateDbContext();
|
|
|
|
// 创建一个测评类型
|
|
var assessmentType = CreateAssessmentType(dbContext, seed.Get);
|
|
|
|
// 创建两个题目
|
|
var question1 = new Question
|
|
{
|
|
AssessmentTypeId = assessmentType.Id,
|
|
QuestionNo = seed.Get,
|
|
Content = $"Question 1 {seed.Get}",
|
|
Sort = 1,
|
|
Status = 1,
|
|
CreateTime = DateTime.Now,
|
|
UpdateTime = DateTime.Now,
|
|
IsDeleted = false
|
|
};
|
|
|
|
var question2 = new Question
|
|
{
|
|
AssessmentTypeId = assessmentType.Id,
|
|
QuestionNo = seed.Get + 1,
|
|
Content = $"Question 2 {seed.Get}",
|
|
Sort = 2,
|
|
Status = 1,
|
|
CreateTime = DateTime.Now,
|
|
UpdateTime = DateTime.Now,
|
|
IsDeleted = false
|
|
};
|
|
|
|
dbContext.Questions.AddRange(question1, question2);
|
|
dbContext.SaveChanges();
|
|
|
|
var service = new AssessmentService(dbContext, _mockLogger.Object);
|
|
|
|
// Act: 尝试将 question2 的题号更新为 question1 的题号
|
|
var updateRequest = new UpdateQuestionRequest
|
|
{
|
|
Id = question2.Id,
|
|
AssessmentTypeId = assessmentType.Id,
|
|
QuestionNo = seed.Get, // 使用 question1 的题号
|
|
Content = $"Updated question 2 {seed.Get}",
|
|
Sort = 2,
|
|
Status = 1
|
|
};
|
|
|
|
// Assert
|
|
try
|
|
{
|
|
service.UpdateQuestionAsync(updateRequest).GetAwaiter().GetResult();
|
|
return false; // 应该抛出异常
|
|
}
|
|
catch (BusinessException ex)
|
|
{
|
|
return ex.Code == ErrorCodes.QuestionNoExists;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Property 8: 更新题目时保持自身题号不变应该成功
|
|
/// *For any* Question within the same AssessmentTypeId, the QuestionNo SHALL be unique.
|
|
///
|
|
/// **Validates: Requirements 5.3**
|
|
/// </summary>
|
|
[Property(MaxTest = 100)]
|
|
public bool UniqueQuestionNoEnforcement_UpdateWithSameQuestionNo_ShouldSucceed(PositiveInt seed)
|
|
{
|
|
// Arrange
|
|
using var dbContext = CreateDbContext();
|
|
|
|
// 创建一个测评类型
|
|
var assessmentType = CreateAssessmentType(dbContext, seed.Get);
|
|
|
|
// 创建一个题目
|
|
var question = new Question
|
|
{
|
|
AssessmentTypeId = assessmentType.Id,
|
|
QuestionNo = seed.Get,
|
|
Content = $"Original question {seed.Get}",
|
|
Sort = 1,
|
|
Status = 1,
|
|
CreateTime = DateTime.Now,
|
|
UpdateTime = DateTime.Now,
|
|
IsDeleted = false
|
|
};
|
|
|
|
dbContext.Questions.Add(question);
|
|
dbContext.SaveChanges();
|
|
|
|
var service = new AssessmentService(dbContext, _mockLogger.Object);
|
|
|
|
// Act: 更新题目,保持题号不变
|
|
var updateRequest = new UpdateQuestionRequest
|
|
{
|
|
Id = question.Id,
|
|
AssessmentTypeId = assessmentType.Id,
|
|
QuestionNo = seed.Get, // 保持相同的题号
|
|
Content = $"Updated question {seed.Get}",
|
|
Sort = 2,
|
|
Status = 1
|
|
};
|
|
|
|
var result = service.UpdateQuestionAsync(updateRequest).GetAwaiter().GetResult();
|
|
|
|
// Assert
|
|
var updatedQuestion = dbContext.Questions.FirstOrDefault(q => q.Id == question.Id);
|
|
return result
|
|
&& updatedQuestion != null
|
|
&& updatedQuestion.QuestionNo == seed.Get
|
|
&& updatedQuestion.Content == $"Updated question {seed.Get}";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Property 8: 软删除的题目题号可以被重用
|
|
/// *For any* Question within the same AssessmentTypeId, the QuestionNo SHALL be unique.
|
|
///
|
|
/// **Validates: Requirements 5.3**
|
|
/// </summary>
|
|
[Property(MaxTest = 100)]
|
|
public bool UniqueQuestionNoEnforcement_DeletedQuestionNoCanBeReused(PositiveInt seed)
|
|
{
|
|
// Arrange
|
|
using var dbContext = CreateDbContext();
|
|
|
|
// 创建一个测评类型
|
|
var assessmentType = CreateAssessmentType(dbContext, seed.Get);
|
|
|
|
// 创建并软删除一个题目
|
|
var deletedQuestion = new Question
|
|
{
|
|
AssessmentTypeId = assessmentType.Id,
|
|
QuestionNo = seed.Get,
|
|
Content = $"Deleted question {seed.Get}",
|
|
Sort = 1,
|
|
Status = 1,
|
|
CreateTime = DateTime.Now,
|
|
UpdateTime = DateTime.Now,
|
|
IsDeleted = true // 已软删除
|
|
};
|
|
|
|
dbContext.Questions.Add(deletedQuestion);
|
|
dbContext.SaveChanges();
|
|
|
|
var service = new AssessmentService(dbContext, _mockLogger.Object);
|
|
|
|
// Act: 尝试创建具有相同题号的新题目
|
|
var request = new CreateQuestionRequest
|
|
{
|
|
AssessmentTypeId = assessmentType.Id,
|
|
QuestionNo = seed.Get, // 使用已删除题目的题号
|
|
Content = $"New question {seed.Get}",
|
|
Sort = 1,
|
|
Status = 1
|
|
};
|
|
|
|
// Assert
|
|
try
|
|
{
|
|
var newQuestionId = service.CreateQuestionAsync(request).GetAwaiter().GetResult();
|
|
var newQuestion = dbContext.Questions.FirstOrDefault(q => q.Id == newQuestionId);
|
|
return newQuestion != null
|
|
&& newQuestion.QuestionNo == seed.Get
|
|
&& !newQuestion.IsDeleted;
|
|
}
|
|
catch (BusinessException)
|
|
{
|
|
return false; // 不应该抛出异常
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Property 8: 批量导入时重复题号应该被检测并报错
|
|
/// *For any* Question within the same AssessmentTypeId, the QuestionNo SHALL be unique.
|
|
///
|
|
/// **Validates: Requirements 5.3**
|
|
/// </summary>
|
|
[Property(MaxTest = 50)]
|
|
public bool UniqueQuestionNoEnforcement_BatchImportWithDuplicateQuestionNo_ShouldReportError(PositiveInt seed)
|
|
{
|
|
// Arrange
|
|
using var dbContext = CreateDbContext();
|
|
|
|
// 创建一个测评类型
|
|
var assessmentType = CreateAssessmentType(dbContext, seed.Get);
|
|
|
|
// 创建一个已存在的题目
|
|
var existingQuestion = new Question
|
|
{
|
|
AssessmentTypeId = assessmentType.Id,
|
|
QuestionNo = seed.Get,
|
|
Content = $"Existing question {seed.Get}",
|
|
Sort = 1,
|
|
Status = 1,
|
|
CreateTime = DateTime.Now,
|
|
UpdateTime = DateTime.Now,
|
|
IsDeleted = false
|
|
};
|
|
dbContext.Questions.Add(existingQuestion);
|
|
dbContext.SaveChanges();
|
|
|
|
var service = new AssessmentService(dbContext, _mockLogger.Object);
|
|
|
|
// Act: 批量导入包含重复题号的题目
|
|
var request = new BatchImportQuestionsRequest
|
|
{
|
|
AssessmentTypeId = assessmentType.Id,
|
|
Questions = new List<BatchImportQuestionItem>
|
|
{
|
|
new BatchImportQuestionItem
|
|
{
|
|
QuestionNo = seed.Get, // 与已存在的题目重复
|
|
Content = $"Import question 1 {seed.Get}",
|
|
Sort = 1,
|
|
Status = 1
|
|
},
|
|
new BatchImportQuestionItem
|
|
{
|
|
QuestionNo = seed.Get + 1, // 新题号
|
|
Content = $"Import question 2 {seed.Get}",
|
|
Sort = 2,
|
|
Status = 1
|
|
}
|
|
}
|
|
};
|
|
|
|
var result = service.BatchImportQuestionsAsync(request).GetAwaiter().GetResult();
|
|
|
|
// Assert: 应该有一个失败(重复题号),一个成功
|
|
return result.FailedCount == 1
|
|
&& result.SuccessCount == 1
|
|
&& result.Errors.Any(e => e.QuestionNo == seed.Get && e.ErrorMessage.Contains("已存在"));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Property 8: 批量导入时导入数据内部重复题号应该被检测并报错
|
|
/// *For any* Question within the same AssessmentTypeId, the QuestionNo SHALL be unique.
|
|
///
|
|
/// **Validates: Requirements 5.3**
|
|
/// </summary>
|
|
[Property(MaxTest = 50)]
|
|
public bool UniqueQuestionNoEnforcement_BatchImportWithInternalDuplicates_ShouldReportError(PositiveInt seed)
|
|
{
|
|
// Arrange
|
|
using var dbContext = CreateDbContext();
|
|
|
|
// 创建一个测评类型
|
|
var assessmentType = CreateAssessmentType(dbContext, seed.Get);
|
|
|
|
var service = new AssessmentService(dbContext, _mockLogger.Object);
|
|
|
|
// Act: 批量导入包含内部重复题号的题目
|
|
var request = new BatchImportQuestionsRequest
|
|
{
|
|
AssessmentTypeId = assessmentType.Id,
|
|
Questions = new List<BatchImportQuestionItem>
|
|
{
|
|
new BatchImportQuestionItem
|
|
{
|
|
QuestionNo = seed.Get,
|
|
Content = $"Import question 1 {seed.Get}",
|
|
Sort = 1,
|
|
Status = 1
|
|
},
|
|
new BatchImportQuestionItem
|
|
{
|
|
QuestionNo = seed.Get, // 与第一个重复
|
|
Content = $"Import question 2 {seed.Get}",
|
|
Sort = 2,
|
|
Status = 1
|
|
},
|
|
new BatchImportQuestionItem
|
|
{
|
|
QuestionNo = seed.Get + 1, // 新题号
|
|
Content = $"Import question 3 {seed.Get}",
|
|
Sort = 3,
|
|
Status = 1
|
|
}
|
|
}
|
|
};
|
|
|
|
var result = service.BatchImportQuestionsAsync(request).GetAwaiter().GetResult();
|
|
|
|
// Assert: 第一个成功,第二个失败(内部重复),第三个成功
|
|
return result.FailedCount == 1
|
|
&& result.SuccessCount == 2
|
|
&& result.Errors.Any(e => e.ErrorMessage.Contains("重复"));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Property 8: 多个不同题号的题目应该都能创建成功
|
|
/// *For any* Question within the same AssessmentTypeId, the QuestionNo SHALL be unique.
|
|
///
|
|
/// **Validates: Requirements 5.3**
|
|
/// </summary>
|
|
[Property(MaxTest = 50)]
|
|
public bool UniqueQuestionNoEnforcement_MultipleUniqueQuestionNosAllSucceed(PositiveInt seed)
|
|
{
|
|
// Arrange
|
|
using var dbContext = CreateDbContext();
|
|
|
|
// 创建一个测评类型
|
|
var assessmentType = CreateAssessmentType(dbContext, seed.Get);
|
|
|
|
var service = new AssessmentService(dbContext, _mockLogger.Object);
|
|
|
|
var createdIds = new List<long>();
|
|
|
|
// Act: 创建多个具有不同题号的题目
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
var request = new CreateQuestionRequest
|
|
{
|
|
AssessmentTypeId = assessmentType.Id,
|
|
QuestionNo = seed.Get + i,
|
|
Content = $"Question {seed.Get}_{i}",
|
|
Sort = i,
|
|
Status = 1
|
|
};
|
|
|
|
try
|
|
{
|
|
var id = service.CreateQuestionAsync(request).GetAwaiter().GetResult();
|
|
createdIds.Add(id);
|
|
}
|
|
catch (BusinessException)
|
|
{
|
|
return false; // 不应该抛出异常
|
|
}
|
|
}
|
|
|
|
// Assert: 所有题目都应该创建成功
|
|
var allCreated = createdIds.Count == 3;
|
|
var allUnique = dbContext.Questions
|
|
.Where(q => q.AssessmentTypeId == assessmentType.Id && !q.IsDeleted)
|
|
.Select(q => q.QuestionNo)
|
|
.Distinct()
|
|
.Count() == 3;
|
|
|
|
return allCreated && allUnique;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region 辅助方法
|
|
|
|
/// <summary>
|
|
/// 创建内存数据库上下文
|
|
/// </summary>
|
|
private AdminBusinessDbContext CreateDbContext()
|
|
{
|
|
var options = new DbContextOptionsBuilder<AdminBusinessDbContext>()
|
|
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
|
|
.Options;
|
|
|
|
return new AdminBusinessDbContext(options);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 创建测评类型
|
|
/// </summary>
|
|
private AssessmentType CreateAssessmentType(AdminBusinessDbContext dbContext, int seed, string? codePrefix = null)
|
|
{
|
|
var code = codePrefix ?? $"CODE_{seed}";
|
|
var assessmentType = new AssessmentType
|
|
{
|
|
Name = $"Test Type {seed}",
|
|
Code = code,
|
|
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;
|
|
}
|
|
|
|
#endregion
|
|
}
|