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
}