WorkCamera/server/Zr.Admin.NET/ZR.Tests/V3RecordSavePropertyTests.cs
2026-01-05 21:20:55 +08:00

254 lines
11 KiB
C#

using FsCheck;
using FsCheck.Xunit;
using Infrastructure;
using Microsoft.Extensions.Configuration;
using Xunit;
using ZR.Common;
using ZR.Model.Business.Dto;
namespace ZR.Tests
{
/// <summary>
/// V3 记录保存属性测试
/// **Feature: work-camera-2.0, Property 3: V3 Record Save Integrity**
/// **Validates: Requirements 3.1, 3.4**
/// </summary>
public class V3RecordSavePropertyTests : IClassFixture<TestFixture>
{
private const string ValidCosDomain = "https://miaoyu-1308826010.cos.ap-shanghai.myqcloud.com";
private const string ValidPrefix = "workfiles";
public V3RecordSavePropertyTests(TestFixture fixture)
{
// 确保配置已初始化
}
/// <summary>
/// 生成有效的COS URL
/// </summary>
private static string GenerateValidCosUrl(int year, int month, int day, string category, int timestampPart, int random, string ext)
{
return $"{ValidCosDomain}/{ValidPrefix}/{year:D4}{month:D2}/{year:D4}{month:D2}{day:D2}/{category}/{timestampPart}_{random}{ext}";
}
/// <summary>
/// Property 3: V3 Record Save Integrity - Input Validation
///
/// *For any* valid V3 save request with N image URLs (1-15), the system SHALL:
/// - Accept the request when all URLs are valid COS URLs
/// - The image count in the request equals N
///
/// **Validates: Requirements 3.1, 3.4**
/// </summary>
[Property(MaxTest = 100)]
public Property ValidV3Request_HasCorrectImageCount()
{
// Generate valid V3 requests
var requestGen = from imageCount in Gen.Choose(1, 15)
from workerCount in Gen.Choose(1, 5)
from deptName in Gen.Elements("技术部", "市场部", "财务部", "人事部", "运营部")
from content in Gen.Elements("设备检修", "日常巡检", "故障处理", "安装调试", "维护保养")
from year in Gen.Choose(2020, 2030)
from month in Gen.Choose(1, 12)
from day in Gen.Choose(1, 28)
select new { imageCount, workerCount, deptName, content, year, month, day };
return Prop.ForAll(
requestGen.ToArbitrary(),
req =>
{
// Generate unique worker names
var allWorkers = new[] { "张三", "李四", "王五", "赵六", "钱七" };
var workers = allWorkers.Take(req.workerCount).ToList();
// Generate valid COS URLs
var imageUrls = new List<string>();
for (int i = 0; i < req.imageCount; i++)
{
var url = GenerateValidCosUrl(req.year, req.month, req.day, "当日照片", 100000 + i, 1000 + i, ".jpg");
imageUrls.Add(url);
}
var dto = new CamRecordWorkV3Dto
{
DeptName = req.deptName,
Content = req.content,
RecordTime = new DateTime(req.year, req.month, req.day, 10, 30, 0),
Workers = workers,
ImageUrls = imageUrls,
Longitude = "121.4737",
Latitude = "31.2304",
Address = "上海市浦东新区",
StatusName = "正常"
};
// Verify: Image count matches
if (dto.ImageUrls.Count != req.imageCount)
return false.Label($"Expected {req.imageCount} images, got {dto.ImageUrls.Count}");
// Verify: All URLs are valid COS URLs
foreach (var url in dto.ImageUrls)
{
if (!CosService.IsValidCosUrl(url))
return false.Label($"Invalid COS URL: {url}");
}
// Verify: Workers count matches
if (dto.Workers.Count != req.workerCount)
return false.Label($"Expected {req.workerCount} workers, got {dto.Workers.Count}");
return true.Label("All assertions passed");
});
}
/// <summary>
/// Property 3: V3 Record Save Integrity - URL Validation Consistency
///
/// *For any* V3 save request, all image URLs must pass COS validation.
/// If any URL is invalid, the entire request should be rejected.
///
/// **Validates: Requirements 3.1, 3.4**
/// </summary>
[Property(MaxTest = 100)]
public Property AllImageUrls_MustBeValidCosUrls()
{
// Generate requests with valid COS URLs
var requestGen = from imageCount in Gen.Choose(1, 15)
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("当日照片", "参与人员/张三", "工作内容/设备检修", "部门/技术部")
select new { imageCount, year, month, day, category };
return Prop.ForAll(
requestGen.ToArbitrary(),
req =>
{
// Generate valid COS URLs
var imageUrls = new List<string>();
for (int i = 0; i < req.imageCount; i++)
{
var url = GenerateValidCosUrl(req.year, req.month, req.day, req.category, 100000 + i, 1000 + i, ".jpg");
imageUrls.Add(url);
}
// Verify: All URLs pass validation
var allValid = imageUrls.All(url => CosService.IsValidCosUrl(url));
return allValid.Label($"All {req.imageCount} URLs should be valid COS URLs");
});
}
/// <summary>
/// Property 3: V3 Record Save Integrity - Image Count Limit
///
/// *For any* V3 save request, the image count must be between 1 and 15.
///
/// **Validates: Requirements 3.1, 3.4**
/// </summary>
[Property(MaxTest = 100)]
public Property ImageCount_MustBeWithinLimit()
{
// Generate image counts within valid range
var countGen = Gen.Choose(1, 15);
return Prop.ForAll(
countGen.ToArbitrary(),
imageCount =>
{
// Verify: Count is within valid range
var isValid = imageCount >= 1 && imageCount <= 15;
return isValid.Label($"Image count {imageCount} should be within 1-15 range");
});
}
/// <summary>
/// Property 3: V3 Record Save Integrity - Request with Invalid URLs Rejected
///
/// *For any* V3 save request containing at least one invalid URL,
/// the validation should fail.
///
/// **Validates: Requirements 3.1, 3.4**
/// </summary>
[Property(MaxTest = 100)]
public Property RequestWithInvalidUrl_ShouldFailValidation()
{
// Generate requests with one invalid URL mixed in
var requestGen = from validCount in Gen.Choose(1, 10)
from invalidDomain in Gen.Elements(
"https://other-bucket.cos.ap-shanghai.myqcloud.com",
"https://example.com",
"http://miaoyu-1308826010.cos.ap-shanghai.myqcloud.com")
from year in Gen.Choose(2020, 2030)
from month in Gen.Choose(1, 12)
from day in Gen.Choose(1, 28)
select new { validCount, invalidDomain, year, month, day };
return Prop.ForAll(
requestGen.ToArbitrary(),
req =>
{
// Generate valid COS URLs
var imageUrls = new List<string>();
for (int i = 0; i < req.validCount; i++)
{
var url = GenerateValidCosUrl(req.year, req.month, req.day, "当日照片", 100000 + i, 1000 + i, ".jpg");
imageUrls.Add(url);
}
// Add one invalid URL
var invalidUrl = $"{req.invalidDomain}/workfiles/{req.year:D4}{req.month:D2}/{req.year:D4}{req.month:D2}{req.day:D2}/当日照片/test.jpg";
imageUrls.Add(invalidUrl);
// Verify: At least one URL fails validation
var hasInvalidUrl = imageUrls.Any(url => !CosService.IsValidCosUrl(url));
return hasInvalidUrl.Label("Request with invalid URL should fail validation");
});
}
/// <summary>
/// Property 3: V3 Record Save Integrity - First Image as Cover
///
/// *For any* valid V3 save request with N images, the first image URL
/// should be used as the cover image (ImageUrl field in work record).
///
/// **Validates: Requirements 3.1, 3.4**
/// </summary>
[Property(MaxTest = 100)]
public Property FirstImage_ShouldBeCoverImage()
{
// Generate requests with multiple images
var requestGen = from imageCount in Gen.Choose(1, 15)
from year in Gen.Choose(2020, 2030)
from month in Gen.Choose(1, 12)
from day in Gen.Choose(1, 28)
select new { imageCount, year, month, day };
return Prop.ForAll(
requestGen.ToArbitrary(),
req =>
{
// Generate valid COS URLs
var imageUrls = new List<string>();
for (int i = 0; i < req.imageCount; i++)
{
var url = GenerateValidCosUrl(req.year, req.month, req.day, "当日照片", 100000 + i, 1000 + i, ".jpg");
imageUrls.Add(url);
}
// Verify: First URL exists and is valid
var firstUrl = imageUrls.FirstOrDefault();
if (string.IsNullOrEmpty(firstUrl))
return false.Label("First URL should not be empty");
if (!CosService.IsValidCosUrl(firstUrl))
return false.Label("First URL should be a valid COS URL");
return true.Label("First image can be used as cover image");
});
}
}
}