using FsCheck; using FsCheck.Xunit; using MiAssessment.Admin.Business.Services; using Xunit; namespace MiAssessment.Tests.Services; /// /// UploadService 属性测试 /// **Validates: Requirements 1.2, 1.3, 1.4, 1.5** /// public class UploadServicePropertyTests { /// /// 允许的图片扩展名 /// private static readonly string[] AllowedExtensions = { ".jpg", ".jpeg", ".png", ".gif", ".webp" }; /// /// 不允许的扩展名示例 /// private static readonly string[] DisallowedExtensions = { ".txt", ".pdf", ".exe", ".doc", ".html", ".js", ".css", ".zip", ".rar", ".mp4" }; #region Property 1: 文件格式验证 /// /// **Feature: image-upload-feature, Property 1: 文件格式验证** /// *For any* 上传的文件,如果文件扩展名不在允许列表(jpg, jpeg, png, gif, webp)中, /// THE Upload_Service SHALL 返回格式错误。 /// **Validates: Requirements 1.2, 1.4** /// [Property(MaxTest = 100)] public bool InvalidExtension_ShouldBeRejected(PositiveInt seed) { // 从不允许的扩展名中选择一个 var extension = DisallowedExtensions[seed.Get % DisallowedExtensions.Length]; // 验证该扩展名被拒绝 return !UploadService.IsValidExtension(extension); } /// /// **Feature: image-upload-feature, Property 1: 文件格式验证** /// *For any* 允许的图片扩展名,THE Upload_Service SHALL 接受该格式。 /// **Validates: Requirements 1.4** /// [Property(MaxTest = 100)] public bool ValidExtension_ShouldBeAccepted(PositiveInt seed) { // 从允许的扩展名中选择一个 var extension = AllowedExtensions[seed.Get % AllowedExtensions.Length]; // 验证该扩展名被接受 return UploadService.IsValidExtension(extension); } /// /// **Feature: image-upload-feature, Property 1: 文件格式验证** /// *For any* 允许的图片扩展名(大写形式),THE Upload_Service SHALL 接受该格式(大小写不敏感)。 /// **Validates: Requirements 1.4** /// [Property(MaxTest = 100)] public bool ValidExtension_CaseInsensitive_ShouldBeAccepted(PositiveInt seed) { // 从允许的扩展名中选择一个并转为大写 var extension = AllowedExtensions[seed.Get % AllowedExtensions.Length].ToUpperInvariant(); // 验证大写扩展名也被接受 return UploadService.IsValidExtension(extension); } #endregion #region Property 2: 文件大小验证 /// /// **Feature: image-upload-feature, Property 2: 文件大小验证** /// *For any* 上传的文件,如果文件大小超过10MB,THE Upload_Service SHALL 返回大小超限错误。 /// **Validates: Requirements 1.3** /// [Property(MaxTest = 100)] public bool FileSizeExceeding10MB_ShouldBeRejected(PositiveInt extraBytes) { // 10MB + 额外字节 var maxSize = 10L * 1024 * 1024; var fileSize = maxSize + extraBytes.Get; // 验证超过10MB的文件被拒绝 return !UploadService.IsValidFileSize(fileSize); } /// /// **Feature: image-upload-feature, Property 2: 文件大小验证** /// *For any* 文件大小在1字节到10MB之间,THE Upload_Service SHALL 接受该文件。 /// **Validates: Requirements 1.3** /// [Property(MaxTest = 100)] public bool FileSizeWithin10MB_ShouldBeAccepted(PositiveInt seed) { // 生成1字节到10MB之间的文件大小 var maxSize = 10L * 1024 * 1024; var fileSize = (seed.Get % maxSize) + 1; // 确保至少1字节 // 验证在范围内的文件被接受 return UploadService.IsValidFileSize(fileSize); } /// /// **Feature: image-upload-feature, Property 2: 文件大小验证** /// *For any* 文件大小为0或负数,THE Upload_Service SHALL 拒绝该文件。 /// **Validates: Requirements 1.3** /// [Property(MaxTest = 100)] public bool ZeroOrNegativeFileSize_ShouldBeRejected(NegativeInt negativeSize) { // 验证0和负数被拒绝 return !UploadService.IsValidFileSize(0) && !UploadService.IsValidFileSize(negativeSize.Get); } #endregion #region Property 3: 文件名唯一性 /// /// **Feature: image-upload-feature, Property 3: 文件名唯一性** /// *For any* 两次上传操作,即使上传相同的文件,生成的文件名 SHALL 不同。 /// **Validates: Requirements 1.5** /// [Property(MaxTest = 100)] public bool GeneratedFileNames_ShouldBeUnique(PositiveInt seed) { // 使用相同的原始文件名 var extension = AllowedExtensions[seed.Get % AllowedExtensions.Length]; var originalFileName = $"test_file{extension}"; // 生成两个唯一文件名 var name1 = UploadService.GenerateUniqueFileName(originalFileName); var name2 = UploadService.GenerateUniqueFileName(originalFileName); // 验证两个文件名不同 return name1 != name2; } /// /// **Feature: image-upload-feature, Property 3: 文件名唯一性** /// *For any* 原始文件名,生成的唯一文件名 SHALL 保留原始扩展名。 /// **Validates: Requirements 1.5** /// [Property(MaxTest = 100)] public bool GeneratedFileName_ShouldPreserveExtension(PositiveInt seed) { // 选择一个扩展名 var extension = AllowedExtensions[seed.Get % AllowedExtensions.Length]; var originalFileName = $"original_file_{seed.Get}{extension}"; // 生成唯一文件名 var uniqueName = UploadService.GenerateUniqueFileName(originalFileName); // 验证扩展名被保留(转为小写) return uniqueName.EndsWith(extension.ToLowerInvariant()); } /// /// **Feature: image-upload-feature, Property 3: 文件名唯一性** /// *For any* 大写扩展名的文件,生成的唯一文件名 SHALL 将扩展名转为小写。 /// **Validates: Requirements 1.5** /// [Property(MaxTest = 100)] public bool GeneratedFileName_ShouldNormalizeExtensionToLowercase(PositiveInt seed) { // 选择一个扩展名并转为大写 var extension = AllowedExtensions[seed.Get % AllowedExtensions.Length].ToUpperInvariant(); var originalFileName = $"FILE_{seed.Get}{extension}"; // 生成唯一文件名 var uniqueName = UploadService.GenerateUniqueFileName(originalFileName); // 验证扩展名被转为小写 return uniqueName.EndsWith(extension.ToLowerInvariant()); } /// /// **Feature: image-upload-feature, Property 3: 文件名唯一性** /// *For any* 多次生成的文件名,所有文件名 SHALL 互不相同。 /// **Validates: Requirements 1.5** /// [Property(MaxTest = 50)] public bool MultipleGeneratedFileNames_ShouldAllBeUnique(PositiveInt seed) { var originalFileName = "test.jpg"; var generatedNames = new HashSet(); // 生成多个文件名 var count = (seed.Get % 10) + 5; // 5-14个文件名 for (var i = 0; i < count; i++) { var name = UploadService.GenerateUniqueFileName(originalFileName); if (!generatedNames.Add(name)) { return false; // 发现重复 } } return true; } #endregion }