using FsCheck; using FsCheck.Xunit; using Infrastructure; using Microsoft.Extensions.Configuration; using Xunit; using ZR.Common; namespace ZR.Tests { /// /// COS URL 验证属性测试 /// **Feature: work-camera-2.0, Property 2: COS URL Validation** /// **Validates: Requirements 3.3** /// public class CosUrlValidationPropertyTests : IClassFixture { private const string ValidCosDomain = "https://miaoyu-1308826010.cos.ap-shanghai.myqcloud.com"; private const string ValidPrefix = "workfiles"; public CosUrlValidationPropertyTests(TestFixture fixture) { // 确保配置已初始化 } /// /// Property 2: COS URL Validation /// /// *For any* URL submitted to the V3 interface, the system SHALL accept only URLs matching /// the COS domain pattern `https://miaoyu-1308826010.cos.ap-shanghai.myqcloud.com/workfiles/...` /// and reject all other URLs. /// /// **Validates: Requirements 3.3** /// [Property(MaxTest = 100)] public Property ValidCosUrls_AreAccepted() { // Generate valid COS URLs var validUrlGen = from year in Gen.Choose(2020, 2030) from month in Gen.Choose(1, 12) from day in Gen.Choose(1, 28) from category in Gen.Elements("当日照片", "参与人员/张三", "工作内容/设备检修", "部门/技术部") from timestampPart in Gen.Choose(100000, 999999) from random in Gen.Choose(1000, 9999) from ext in Gen.Elements(".jpg", ".png", ".jpeg") select $"{ValidCosDomain}/{ValidPrefix}/{year:D4}{month:D2}/{year:D4}{month:D2}{day:D2}/{category}/{timestampPart}_{random}{ext}"; return Prop.ForAll( validUrlGen.ToArbitrary(), url => { var result = CosService.IsValidCosUrl(url); return result.Label($"Valid COS URL should be accepted: {url}"); }); } /// /// Property 2: COS URL Validation - Invalid domain URLs are rejected /// /// *For any* URL with a different domain, the system SHALL reject it. /// /// **Validates: Requirements 3.3** /// [Property(MaxTest = 100)] public Property InvalidDomainUrls_AreRejected() { // Generate URLs with invalid domains var invalidDomainGen = from domain in Gen.Elements( "https://other-bucket.cos.ap-shanghai.myqcloud.com", "https://example.com", "http://miaoyu-1308826010.cos.ap-shanghai.myqcloud.com", // http instead of https "https://miaoyu.cos.ap-shanghai.myqcloud.com", "https://localhost:8080", "https://192.168.1.1") from path in Gen.Elements( "/workfiles/202501/20250105/当日照片/test.jpg", "/files/image.png", "/test.jpg") select domain + path; return Prop.ForAll( invalidDomainGen.ToArbitrary(), url => { var result = CosService.IsValidCosUrl(url); return (!result).Label($"Invalid domain URL should be rejected: {url}"); }); } /// /// Property 2: COS URL Validation - Invalid prefix URLs are rejected /// /// *For any* URL with the correct domain but wrong prefix, the system SHALL reject it. /// /// **Validates: Requirements 3.3** /// [Property(MaxTest = 100)] public Property InvalidPrefixUrls_AreRejected() { // Generate URLs with invalid prefixes var invalidPrefixGen = from prefix in Gen.Elements( "files", "images", "uploads", "data", "work", "workfile") // missing 's' from path in Gen.Elements( "/202501/20250105/当日照片/test.jpg", "/test.jpg") select $"{ValidCosDomain}/{prefix}{path}"; return Prop.ForAll( invalidPrefixGen.ToArbitrary(), url => { var result = CosService.IsValidCosUrl(url); return (!result).Label($"Invalid prefix URL should be rejected: {url}"); }); } /// /// Property 2: COS URL Validation - Empty and null URLs are rejected /// /// **Validates: Requirements 3.3** /// [Property(MaxTest = 100)] public Property EmptyOrWhitespaceUrls_AreRejected() { // Generate empty or whitespace strings var emptyGen = Gen.Elements("", " ", " ", "\t", "\n", "\r\n", " "); return Prop.ForAll( emptyGen.ToArbitrary(), url => { var result = CosService.IsValidCosUrl(url); return (!result).Label($"Empty/whitespace URL should be rejected"); }); } } }