HaniBlindBox/server/HoneyBox/tests/HoneyBox.Tests/Services/ResponseFormatPropertyTests.cs
2026-01-17 03:24:20 +08:00

286 lines
9.3 KiB
C#

using FsCheck;
using FsCheck.Xunit;
using HoneyBox.Admin.Business.Models;
using System.Text.Json;
using Xunit;
namespace HoneyBox.Tests.Services;
/// <summary>
/// 响应格式属性测试
/// Property 16: API Response Format Consistency - API响应格式一致性
/// Property 17: Paginated Response Format - 分页响应格式一致性
/// </summary>
public class ResponseFormatPropertyTests
{
/// <summary>
/// Property 16: ApiResponse 成功响应格式一致性
/// </summary>
[Property(MaxTest = 100)]
public Property Property16_ApiResponse_Success_HasConsistentFormat()
{
return Prop.ForAll(
Arb.From<string>().Filter(s => !string.IsNullOrEmpty(s)),
message =>
{
var response = ApiResponse.Success(message);
return response.Code == 0 &&
response.Message == message &&
response.Data == null;
});
}
/// <summary>
/// Property 16: ApiResponse 带数据的成功响应格式一致性
/// </summary>
[Property(MaxTest = 100)]
public Property Property16_ApiResponse_SuccessWithData_HasConsistentFormat()
{
return Prop.ForAll(
Arb.From<int>(),
Arb.From<string>().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;
});
}
/// <summary>
/// Property 16: ApiResponse 错误响应格式一致性
/// </summary>
[Property(MaxTest = 100)]
public Property Property16_ApiResponse_Error_HasConsistentFormat()
{
return Prop.ForAll(
Arb.From<int>().Filter(c => c != 0),
Arb.From<string>().Filter(s => !string.IsNullOrEmpty(s)),
(code, message) =>
{
var response = ApiResponse.Error(code, message);
return response.Code == code &&
response.Message == message &&
response.Data == null;
});
}
/// <summary>
/// Property 16: ApiResponse<T> 泛型成功响应格式一致性
/// </summary>
[Property(MaxTest = 100)]
public Property Property16_GenericApiResponse_Success_HasConsistentFormat()
{
return Prop.ForAll(
Arb.From<int>(),
data =>
{
var response = ApiResponse<int>.Success(data);
return response.Code == 0 &&
response.Message == "success" &&
response.Data == data;
});
}
/// <summary>
/// Property 16: ApiResponse<T> 泛型错误响应格式一致性
/// </summary>
[Property(MaxTest = 100)]
public Property Property16_GenericApiResponse_Error_HasConsistentFormat()
{
return Prop.ForAll(
Arb.From<int>().Filter(c => c != 0),
Arb.From<string>().Filter(s => !string.IsNullOrEmpty(s)),
(code, message) =>
{
var response = ApiResponse<int>.Error(code, message);
return response.Code == code &&
response.Message == message &&
EqualityComparer<int>.Default.Equals(response.Data, default);
});
}
/// <summary>
/// Property 17: PagedResult 分页响应格式一致性
/// </summary>
[Property(MaxTest = 100)]
public Property Property17_PagedResult_HasConsistentFormat()
{
return Prop.ForAll(
Arb.From<int>().Filter(p => p > 0),
Arb.From<int>().Filter(ps => ps > 0 && ps <= 100),
Arb.From<int>().Filter(t => t >= 0),
(page, pageSize, total) =>
{
var items = Enumerable.Range(1, Math.Min(pageSize, total)).ToList();
var result = new PagedResult<int>
{
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;
});
}
/// <summary>
/// Property 17: PagedResult 总页数计算正确性
/// </summary>
[Property(MaxTest = 100)]
public Property Property17_PagedResult_TotalPages_CalculatedCorrectly()
{
return Prop.ForAll(
Arb.From<int>().Filter(ps => ps > 0 && ps <= 100),
Arb.From<int>().Filter(t => t >= 0 && t <= 10000),
(pageSize, total) =>
{
var result = new PagedResult<int>
{
Page = 1,
PageSize = pageSize,
Total = total,
Items = new List<int>()
};
var expectedTotalPages = (int)Math.Ceiling((double)total / pageSize);
return result.TotalPages == expectedTotalPages;
});
}
/// <summary>
/// Property 17: PagedResult 空结果处理
/// </summary>
[Fact]
public void Property17_PagedResult_EmptyResult_HasCorrectFormat()
{
var result = new PagedResult<int>
{
Page = 1,
PageSize = 20,
Total = 0,
Items = new List<int>()
};
Assert.Equal(1, result.Page);
Assert.Equal(20, result.PageSize);
Assert.Equal(0, result.Total);
Assert.Equal(0, result.TotalPages);
Assert.Empty(result.Items);
}
/// <summary>
/// Property 16: BusinessErrorCodes 错误码唯一性
/// </summary>
[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);
}
/// <summary>
/// Property 16: BusinessErrorCodes 错误码非零
/// </summary>
[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);
}
}
/// <summary>
/// Property 16: ApiResponse JSON 序列化格式一致性
/// </summary>
[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());
}
/// <summary>
/// Property 17: PagedResult JSON 序列化格式一致性
/// </summary>
[Fact]
public void Property17_PagedResult_JsonSerialization_HasExpectedFormat()
{
var result = new PagedResult<string>
{
Page = 1,
PageSize = 10,
Total = 100,
Items = new List<string> { "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());
}
}