using FsCheck; using FsCheck.Xunit; using HoneyBox.Admin.Business.Models; using System.Text.Json; using Xunit; namespace HoneyBox.Tests.Services; /// /// 响应格式属性测试 /// Property 16: API Response Format Consistency - API响应格式一致性 /// Property 17: Paginated Response Format - 分页响应格式一致性 /// public class ResponseFormatPropertyTests { /// /// Property 16: ApiResponse 成功响应格式一致性 /// [Property(MaxTest = 100)] public Property Property16_ApiResponse_Success_HasConsistentFormat() { return Prop.ForAll( Arb.From().Filter(s => !string.IsNullOrEmpty(s)), message => { var response = ApiResponse.Success(message); return response.Code == 0 && response.Message == message && response.Data == null; }); } /// /// Property 16: ApiResponse 带数据的成功响应格式一致性 /// [Property(MaxTest = 100)] public Property Property16_ApiResponse_SuccessWithData_HasConsistentFormat() { return Prop.ForAll( Arb.From(), Arb.From().Filter(s => !string.IsNullOrEmpty(s)), (data, message) => { var response = ApiResponse.Success(data, message); return response.Code == 0 && response.Message == message && response.Data != null && (int)response.Data == data; }); } /// /// Property 16: ApiResponse 错误响应格式一致性 /// [Property(MaxTest = 100)] public Property Property16_ApiResponse_Error_HasConsistentFormat() { return Prop.ForAll( Arb.From().Filter(c => c != 0), Arb.From().Filter(s => !string.IsNullOrEmpty(s)), (code, message) => { var response = ApiResponse.Error(code, message); return response.Code == code && response.Message == message && response.Data == null; }); } /// /// Property 16: ApiResponse 泛型成功响应格式一致性 /// [Property(MaxTest = 100)] public Property Property16_GenericApiResponse_Success_HasConsistentFormat() { return Prop.ForAll( Arb.From(), data => { var response = ApiResponse.Success(data); return response.Code == 0 && response.Message == "success" && response.Data == data; }); } /// /// Property 16: ApiResponse 泛型错误响应格式一致性 /// [Property(MaxTest = 100)] public Property Property16_GenericApiResponse_Error_HasConsistentFormat() { return Prop.ForAll( Arb.From().Filter(c => c != 0), Arb.From().Filter(s => !string.IsNullOrEmpty(s)), (code, message) => { var response = ApiResponse.Error(code, message); return response.Code == code && response.Message == message && EqualityComparer.Default.Equals(response.Data, default); }); } /// /// Property 17: PagedResult 分页响应格式一致性 /// [Property(MaxTest = 100)] public Property Property17_PagedResult_HasConsistentFormat() { return Prop.ForAll( Arb.From().Filter(p => p > 0), Arb.From().Filter(ps => ps > 0 && ps <= 100), Arb.From().Filter(t => t >= 0), (page, pageSize, total) => { var items = Enumerable.Range(1, Math.Min(pageSize, total)).ToList(); var result = new PagedResult { Page = page, PageSize = pageSize, Total = total, Items = items }; // 验证分页结果格式 return result.Page == page && result.PageSize == pageSize && result.Total == total && result.Items != null && result.Items.Count <= pageSize; }); } /// /// Property 17: PagedResult 总页数计算正确性 /// [Property(MaxTest = 100)] public Property Property17_PagedResult_TotalPages_CalculatedCorrectly() { return Prop.ForAll( Arb.From().Filter(ps => ps > 0 && ps <= 100), Arb.From().Filter(t => t >= 0 && t <= 10000), (pageSize, total) => { var result = new PagedResult { Page = 1, PageSize = pageSize, Total = total, Items = new List() }; var expectedTotalPages = (int)Math.Ceiling((double)total / pageSize); return result.TotalPages == expectedTotalPages; }); } /// /// Property 17: PagedResult 空结果处理 /// [Fact] public void Property17_PagedResult_EmptyResult_HasCorrectFormat() { var result = new PagedResult { Page = 1, PageSize = 20, Total = 0, Items = new List() }; Assert.Equal(1, result.Page); Assert.Equal(20, result.PageSize); Assert.Equal(0, result.Total); Assert.Equal(0, result.TotalPages); Assert.Empty(result.Items); } /// /// Property 16: BusinessErrorCodes 错误码唯一性 /// [Fact] public void Property16_BusinessErrorCodes_AreUnique() { var errorCodes = new[] { BusinessErrorCodes.ValidationError, BusinessErrorCodes.NotFound, BusinessErrorCodes.Unauthorized, BusinessErrorCodes.Forbidden, BusinessErrorCodes.InternalError, BusinessErrorCodes.BusinessError }; var uniqueCodes = errorCodes.Distinct().ToList(); Assert.Equal(errorCodes.Length, uniqueCodes.Count); } /// /// Property 16: BusinessErrorCodes 错误码非零 /// [Fact] public void Property16_BusinessErrorCodes_AreNonZero() { var errorCodes = new[] { BusinessErrorCodes.ValidationError, BusinessErrorCodes.NotFound, BusinessErrorCodes.Unauthorized, BusinessErrorCodes.Forbidden, BusinessErrorCodes.InternalError, BusinessErrorCodes.BusinessError }; foreach (var code in errorCodes) { Assert.NotEqual(0, code); } } /// /// Property 16: ApiResponse JSON 序列化格式一致性 /// [Fact] public void Property16_ApiResponse_JsonSerialization_HasExpectedFormat() { var response = ApiResponse.Success("test data", "操作成功"); var json = JsonSerializer.Serialize(response, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); var doc = JsonDocument.Parse(json); var root = doc.RootElement; Assert.True(root.TryGetProperty("code", out var codeElement)); Assert.True(root.TryGetProperty("message", out var messageElement)); Assert.True(root.TryGetProperty("data", out var dataElement)); Assert.Equal(0, codeElement.GetInt32()); Assert.Equal("操作成功", messageElement.GetString()); Assert.Equal("test data", dataElement.GetString()); } /// /// Property 17: PagedResult JSON 序列化格式一致性 /// [Fact] public void Property17_PagedResult_JsonSerialization_HasExpectedFormat() { var result = new PagedResult { Page = 1, PageSize = 10, Total = 100, Items = new List { "item1", "item2" } }; var json = JsonSerializer.Serialize(result, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); var doc = JsonDocument.Parse(json); var root = doc.RootElement; Assert.True(root.TryGetProperty("page", out var pageElement)); Assert.True(root.TryGetProperty("pageSize", out var pageSizeElement)); Assert.True(root.TryGetProperty("total", out var totalElement)); Assert.True(root.TryGetProperty("totalPages", out var totalPagesElement)); Assert.True(root.TryGetProperty("items", out var itemsElement)); Assert.Equal(1, pageElement.GetInt32()); Assert.Equal(10, pageSizeElement.GetInt32()); Assert.Equal(100, totalElement.GetInt32()); Assert.Equal(10, totalPagesElement.GetInt32()); Assert.Equal(2, itemsElement.GetArrayLength()); } }