HaniBlindBox/server/HoneyBox/tests/HoneyBox.Tests/Integration/ApiResponseFormatTests.cs
2026-01-25 19:10:31 +08:00

1486 lines
45 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Text.Json;
using System.Text.Json.Serialization;
using HoneyBox.Model.Base;
using HoneyBox.Model.Models;
using HoneyBox.Model.Models.Order;
using HoneyBox.Model.Models.Goods;
using HoneyBox.Model.Models.Payment;
using HoneyBox.Model.Models.Lottery;
using Xunit;
namespace HoneyBox.Tests.Integration;
/// <summary>
/// API响应格式验证测试
/// 验证所有接口响应格式与PHP API一致
/// Requirements: 18.1-18.4
/// </summary>
public class ApiResponseFormatTests
{
private readonly JsonSerializerOptions _jsonOptions;
public ApiResponseFormatTests()
{
_jsonOptions = new JsonSerializerOptions
{
PropertyNamingPolicy = null, // Use JsonPropertyName attributes
WriteIndented = false
};
}
#region ApiResponse Base Format Tests
/// <summary>
/// 验证ApiResponse基类使用正确的snake_case字段名
/// Requirements: 18.1, 18.3
/// </summary>
[Fact]
public void ApiResponse_ShouldUseCorrectFieldNames()
{
// Arrange
var response = ApiResponse.Success("success");
// Act
var json = JsonSerializer.Serialize(response, _jsonOptions);
// Assert
Assert.Contains("\"status\":", json);
Assert.Contains("\"msg\":", json);
Assert.DoesNotContain("\"Status\":", json);
Assert.DoesNotContain("\"Msg\":", json);
}
/// <summary>
/// 验证ApiResponse<T>包含data字段
/// Requirements: 18.1, 18.3
/// </summary>
[Fact]
public void ApiResponseGeneric_ShouldIncludeDataField()
{
// Arrange
var response = ApiResponse<string>.Success("test data");
// Act
var json = JsonSerializer.Serialize(response, _jsonOptions);
// Assert
Assert.Contains("\"status\":", json);
Assert.Contains("\"msg\":", json);
Assert.Contains("\"data\":", json);
Assert.DoesNotContain("\"Data\":", json);
}
/// <summary>
/// 验证成功响应status=1
/// Requirements: 18.1
/// </summary>
[Fact]
public void ApiResponse_Success_ShouldHaveStatus1()
{
// Arrange & Act
var response = ApiResponse.Success();
// Assert
Assert.Equal(1, response.Status);
}
/// <summary>
/// 验证失败响应status=0
/// Requirements: 18.1
/// </summary>
[Fact]
public void ApiResponse_Fail_ShouldHaveStatus0()
{
// Arrange & Act
var response = ApiResponse.Fail("error");
// Assert
Assert.Equal(0, response.Status);
}
/// <summary>
/// 验证未授权响应status=-1
/// Requirements: 18.1
/// </summary>
[Fact]
public void ApiResponse_Unauthorized_ShouldHaveStatusMinus1()
{
// Arrange & Act
var response = ApiResponse.Unauthorized();
// Assert
Assert.Equal(-1, response.Status);
}
#endregion
#region Order Models Format Tests
/// <summary>
/// 验证OrderMoneyRequest使用snake_case
/// Requirements: 18.2
/// </summary>
[Fact]
public void OrderMoneyRequest_ShouldUseSnakeCase()
{
// Arrange
var request = new OrderMoneyRequest
{
GoodsId = 1,
Num = 1,
PrizeNum = 1,
UseMoneyIs = 1,
UseIntegralIs = 1,
UseMoney2Is = 1
};
// Act
var json = JsonSerializer.Serialize(request, _jsonOptions);
// Assert
Assert.Contains("\"goods_id\":", json);
Assert.Contains("\"num\":", json);
Assert.Contains("\"prize_num\":", json);
Assert.Contains("\"use_money_is\":", json);
Assert.Contains("\"use_integral_is\":", json);
Assert.Contains("\"use_money2_is\":", json);
}
/// <summary>
/// 验证OrderCalculationDto使用snake_case
/// Requirements: 18.2
/// </summary>
[Fact]
public void OrderCalculationDto_ShouldUseSnakeCase()
{
// Arrange
var dto = new OrderCalculationDto
{
OrderTotal = "100.00",
Price = "10.00",
Money = "50.00",
Integral = "100.00",
Score = 20.00m,
UseMoney = "10.00",
UseIntegral = "5.00",
UseMoney2 = 3.00m,
CouponPrice = "2.00"
};
// Act
var json = JsonSerializer.Serialize(dto, _jsonOptions);
// Assert
Assert.Contains("\"order_total\":", json);
Assert.Contains("\"price\":", json);
Assert.Contains("\"money\":", json);
Assert.Contains("\"integral\":", json);
Assert.Contains("\"score\":", json);
Assert.Contains("\"use_money\":", json);
Assert.Contains("\"use_integral\":", json);
Assert.Contains("\"coupon_price\":", json);
}
/// <summary>
/// 验证OrderBuyResponseDto使用snake_case
/// Requirements: 18.2
/// </summary>
[Fact]
public void OrderBuyResponseDto_ShouldUseSnakeCase()
{
// Arrange
var dto = new OrderBuyResponseDto
{
Status = 1,
OrderNum = "TEST001"
};
// Act
var json = JsonSerializer.Serialize(dto, _jsonOptions);
// Assert
Assert.Contains("\"status\":", json);
Assert.Contains("\"order_num\":", json);
Assert.Contains("\"res\":", json);
}
/// <summary>
/// 验证OrderListDto使用snake_case
/// Requirements: 18.2
/// </summary>
[Fact]
public void OrderListDto_ShouldUseSnakeCase()
{
// Arrange
var dto = new OrderListDto
{
Id = 1,
OrderNum = "TEST001",
GoodsTitle = "Test",
GoodsImgUrl = "http://test.com/img.jpg",
OrderTotal = "100.00",
Price = "100.00",
PrizeNum = 1,
Status = 1,
AddTime = 1234567890,
PayTime = 1234567890,
OrderType = 1
};
// Act
var json = JsonSerializer.Serialize(dto, _jsonOptions);
// Assert
Assert.Contains("\"id\":", json);
Assert.Contains("\"order_num\":", json);
Assert.Contains("\"goods_title\":", json);
Assert.Contains("\"goods_imgurl\":", json);
Assert.Contains("\"order_total\":", json);
Assert.Contains("\"price\":", json);
Assert.Contains("\"prize_num\":", json);
Assert.Contains("\"status\":", json);
Assert.Contains("\"addtime\":", json);
Assert.Contains("\"pay_time\":", json);
Assert.Contains("\"order_type\":", json);
}
/// <summary>
/// 验证PrizeOrderLogDto使用snake_case
/// Requirements: 18.2
/// </summary>
[Fact]
public void PrizeOrderLogDto_ShouldUseSnakeCase()
{
// Arrange
var dto = new PrizeOrderLogDto
{
Id = 1,
UserId = 1,
GoodsListTitle = "Test",
GoodsListImgUrl = "http://test.com/img.jpg",
GoodsListPrice = "100.00",
GoodsListMoney = "50.00",
Status = 0,
LuckNo = 1,
ShangId = 1
};
// Act
var json = JsonSerializer.Serialize(dto, _jsonOptions);
// Assert
Assert.Contains("\"id\":", json);
Assert.Contains("\"user_id\":", json);
Assert.Contains("\"goodslist_title\":", json);
Assert.Contains("\"goodslist_imgurl\":", json);
Assert.Contains("\"goodslist_price\":", json);
Assert.Contains("\"goodslist_money\":", json);
Assert.Contains("\"status\":", json);
Assert.Contains("\"luck_no\":", json);
Assert.Contains("\"shang_id\":", json);
}
#endregion
#region Warehouse Models Format Tests
/// <summary>
/// 验证WarehouseIndexRequest使用snake_case
/// Requirements: 18.4
/// </summary>
[Fact]
public void WarehouseIndexRequest_ShouldUseSnakeCase()
{
// Arrange
var request = new WarehouseIndexRequest
{
Page = 1,
PageSize = 10,
Type = 1
};
// Act
var json = JsonSerializer.Serialize(request, _jsonOptions);
// Assert
Assert.Contains("\"page\":", json);
Assert.Contains("\"page_size\":", json);
Assert.Contains("\"type\":", json);
}
/// <summary>
/// 验证WarehouseItemDto使用snake_case
/// Requirements: 18.4
/// </summary>
[Fact]
public void WarehouseItemDto_ShouldUseSnakeCase()
{
// Arrange
var dto = new WarehouseItemDto
{
Id = 1,
GoodsListTitle = "Test",
GoodsListImgUrl = "http://test.com/img.jpg",
GoodsListPrice = "100.00",
GoodsListMoney = "50.00",
Status = 0,
AddTime = 1234567890,
ShangId = 1,
GoodsId = 1
};
// Act
var json = JsonSerializer.Serialize(dto, _jsonOptions);
// Assert
Assert.Contains("\"id\":", json);
Assert.Contains("\"goodslist_title\":", json);
Assert.Contains("\"goodslist_imgurl\":", json);
Assert.Contains("\"goodslist_price\":", json);
Assert.Contains("\"goodslist_money\":", json);
Assert.Contains("\"status\":", json);
Assert.Contains("\"addtime\":", json);
Assert.Contains("\"shang_id\":", json);
Assert.Contains("\"goods_id\":", json);
}
/// <summary>
/// 验证RecoveryResultDto使用snake_case
/// Requirements: 18.4
/// </summary>
[Fact]
public void RecoveryResultDto_ShouldUseSnakeCase()
{
// Arrange
var dto = new RecoveryResultDto
{
TotalMoney = "100.00",
Count = 5,
RecoveryNum = "REC001",
UserMoney = "150.00"
};
// Act
var json = JsonSerializer.Serialize(dto, _jsonOptions);
// Assert
Assert.Contains("\"total_money\":", json);
Assert.Contains("\"count\":", json);
Assert.Contains("\"recovery_num\":", json);
Assert.Contains("\"user_money\":", json);
}
/// <summary>
/// 验证SendRecordDto使用snake_case
/// Requirements: 18.4
/// </summary>
[Fact]
public void SendRecordDto_ShouldUseSnakeCase()
{
// Arrange
var dto = new SendRecordDto
{
Id = 1,
SendNum = "SEND001",
Name = "Test",
Mobile = "138****0000",
Address = "Test Address",
Status = 1,
StatusName = "待发货",
Count = 3,
Freight = "10.00",
AddTime = "2024-01-01 12:00:00"
};
// Act
var json = JsonSerializer.Serialize(dto, _jsonOptions);
// Assert
Assert.Contains("\"id\":", json);
Assert.Contains("\"send_num\":", json);
Assert.Contains("\"name\":", json);
Assert.Contains("\"mobile\":", json);
Assert.Contains("\"address\":", json);
Assert.Contains("\"status\":", json);
Assert.Contains("\"status_name\":", json);
Assert.Contains("\"count\":", json);
Assert.Contains("\"freight\":", json);
Assert.Contains("\"addtime\":", json);
}
/// <summary>
/// 验证LogisticsDto使用snake_case
/// Requirements: 18.4
/// </summary>
[Fact]
public void LogisticsDto_ShouldUseSnakeCase()
{
// Arrange
var dto = new LogisticsDto
{
CourierNumber = "SF123456",
CourierName = "顺丰速运",
CourierCode = "SF",
Count = 3,
SendNum = "SEND001",
DeliveryStatus = "已签收"
};
// Act
var json = JsonSerializer.Serialize(dto, _jsonOptions);
// Assert
Assert.Contains("\"courier_number\":", json);
Assert.Contains("\"courier_name\":", json);
Assert.Contains("\"courier_code\":", json);
Assert.Contains("\"count\":", json);
Assert.Contains("\"send_num\":", json);
Assert.Contains("\"delivery_status\":", json);
}
#endregion
#region PageResponse Format Tests
/// <summary>
/// 验证PageResponse使用snake_case
/// Requirements: 18.2, 18.4
/// </summary>
[Fact]
public void PageResponse_ShouldUseSnakeCase()
{
// Arrange
var response = new PageResponse<OrderListDto>
{
Data = new List<OrderListDto>(),
LastPage = 10,
Total = 100,
Page = 1,
PageSize = 10
};
// Act
var json = JsonSerializer.Serialize(response, _jsonOptions);
// Assert
Assert.Contains("\"data\":", json);
Assert.Contains("\"last_page\":", json);
Assert.Contains("\"total\":", json);
Assert.Contains("\"page\":", json);
Assert.Contains("\"page_size\":", json);
}
#endregion
#region WechatPayParams Format Tests
/// <summary>
/// 验证WechatPayParamsDto使用正确的字段名微信API要求的camelCase
/// Requirements: 18.2
/// </summary>
[Fact]
public void WechatPayParamsDto_ShouldUseCamelCaseForWechatApi()
{
// Arrange
var dto = new WechatPayParamsDto
{
AppId = "wx123456",
TimeStamp = "1234567890",
NonceStr = "abc123",
Package = "prepay_id=xxx",
SignType = "RSA",
PaySign = "sign123"
};
// Act
var json = JsonSerializer.Serialize(dto, _jsonOptions);
// Assert - 微信支付参数使用camelCase是正确的
Assert.Contains("\"appId\":", json);
Assert.Contains("\"timeStamp\":", json);
Assert.Contains("\"nonceStr\":", json);
Assert.Contains("\"package\":", json);
Assert.Contains("\"signType\":", json);
Assert.Contains("\"paySign\":", json);
}
#endregion
#region Complete Response Serialization Tests
/// <summary>
/// 验证完整的订单金额计算响应格式
/// Requirements: 18.1, 18.2
/// </summary>
[Fact]
public void CompleteOrderCalculationResponse_ShouldMatchPhpFormat()
{
// Arrange
var data = new OrderCalculationDto
{
OrderTotal = "100.00",
Price = "10.00",
Goods = new GoodsInfoDto { Id = 1, Title = "Test" },
Money = "50.00",
Integral = "100.00",
Score = 20.00m
};
var response = ApiResponse<OrderCalculationDto>.Success(data);
// Act
var json = JsonSerializer.Serialize(response, _jsonOptions);
// Assert - 验证顶层结构
Assert.Contains("\"status\":1", json);
Assert.Contains("\"msg\":\"success\"", json);
Assert.Contains("\"data\":", json);
// Assert - 验证数据字段
Assert.Contains("\"order_total\":\"100.00\"", json);
Assert.Contains("\"price\":\"10.00\"", json);
Assert.Contains("\"goods\":", json);
Assert.Contains("\"money\":\"50.00\"", json);
Assert.Contains("\"integral\":\"100.00\"", json);
Assert.Contains("\"score\":20", json);
}
/// <summary>
/// 验证完整的仓库首页响应格式
/// Requirements: 18.3, 18.4
/// </summary>
[Fact]
public void CompleteWarehouseIndexResponse_ShouldMatchPhpFormat()
{
// Arrange
var data = new WarehouseIndexResponseDto
{
Total = 100,
Data = new List<WarehouseGoodsGroupDto>
{
new WarehouseGoodsGroupDto
{
GoodsId = 1,
GoodsTitle = "Test",
OrderList = new List<WarehouseItemDto>
{
new WarehouseItemDto
{
Id = 1,
GoodsListTitle = "Prize",
GoodsListImgUrl = "http://test.com/img.jpg",
GoodsListPrice = "100.00",
GoodsListMoney = "50.00",
Status = 0
}
}
}
},
LastPage = 10
};
var response = ApiResponse<WarehouseIndexResponseDto>.Success(data);
// Act
var json = JsonSerializer.Serialize(response, _jsonOptions);
// Assert - 验证顶层结构
Assert.Contains("\"status\":1", json);
Assert.Contains("\"msg\":\"success\"", json);
Assert.Contains("\"data\":", json);
// Assert - 验证数据字段
Assert.Contains("\"total\":", json);
Assert.Contains("\"last_page\":", json);
Assert.Contains("\"goods_id\":", json);
Assert.Contains("\"goods_title\":", json);
Assert.Contains("\"goodslist_title\":", json);
Assert.Contains("\"goodslist_imgurl\":", json);
Assert.Contains("\"goodslist_price\":", json);
Assert.Contains("\"goodslist_money\":", json);
}
#endregion
#region Payment System Response Format Tests - Requirements 10.1-10.4
/// <summary>
/// 验证WechatPayResult使用正确的字段名
/// Requirements: 10.1, 10.3
/// </summary>
[Fact]
public void WechatPayResult_ShouldUseCorrectFieldNames()
{
// Arrange
var result = new WechatPayResult
{
Status = 1,
Msg = "success",
Data = new WechatPayData
{
AppId = "wx123456",
TimeStamp = "1234567890",
NonceStr = "abc123",
Package = "prepay_id=xxx",
SignType = "MD5",
PaySign = "sign123",
IsWeixin = 1
}
};
// Act
var json = JsonSerializer.Serialize(result, _jsonOptions);
// Assert - 验证顶层结构使用snake_case
Assert.Contains("\"status\":", json);
Assert.Contains("\"msg\":", json);
Assert.Contains("\"data\":", json);
Assert.DoesNotContain("\"Status\":", json);
Assert.DoesNotContain("\"Msg\":", json);
Assert.DoesNotContain("\"Data\":", json);
}
/// <summary>
/// 验证WechatPayData使用正确的字段名微信API要求的camelCase
/// Requirements: 10.2, 10.4
/// </summary>
[Fact]
public void WechatPayData_ShouldUseCamelCaseForWechatApi()
{
// Arrange
var data = new WechatPayData
{
AppId = "wx123456",
TimeStamp = "1234567890",
NonceStr = "abc123",
Package = "prepay_id=xxx",
SignType = "MD5",
PaySign = "sign123",
IsWeixin = 1
};
// Act
var json = JsonSerializer.Serialize(data, _jsonOptions);
// Assert - 微信支付参数使用camelCase与PHP API一致
Assert.Contains("\"appId\":", json);
Assert.Contains("\"timeStamp\":", json);
Assert.Contains("\"nonceStr\":", json);
Assert.Contains("\"package\":", json);
Assert.Contains("\"signType\":", json);
Assert.Contains("\"paySign\":", json);
Assert.Contains("\"is_weixin\":", json); // is_weixin使用snake_case
}
/// <summary>
/// 验证WechatPayResult成功响应status=1
/// Requirements: 10.3
/// </summary>
[Fact]
public void WechatPayResult_Success_ShouldHaveStatus1()
{
// Arrange
var result = new WechatPayResult
{
Status = 1,
Msg = "success",
Data = new WechatPayData()
};
// Assert
Assert.Equal(1, result.Status);
}
/// <summary>
/// 验证WechatPayResult失败响应status=0
/// Requirements: 10.3
/// </summary>
[Fact]
public void WechatPayResult_Fail_ShouldHaveStatus0()
{
// Arrange
var result = new WechatPayResult
{
Status = 0,
Msg = "支付失败"
};
// Assert
Assert.Equal(0, result.Status);
}
/// <summary>
/// 验证WechatPayRequest使用snake_case
/// Requirements: 10.2
/// </summary>
[Fact]
public void WechatPayRequest_ShouldUseSnakeCase()
{
// Arrange
var request = new WechatPayRequest
{
OrderNo = "TEST001",
Amount = 100.00m,
Body = "商品购买",
Attach = "order_yfs",
OpenId = "openid123",
UserId = 1
};
// Act
var json = JsonSerializer.Serialize(request, _jsonOptions);
// Assert
Assert.Contains("\"order_no\":", json);
Assert.Contains("\"amount\":", json);
Assert.Contains("\"body\":", json);
Assert.Contains("\"attach\":", json);
Assert.Contains("\"open_id\":", json);
Assert.Contains("\"user_id\":", json);
}
/// <summary>
/// 验证PaymentRecordDto使用snake_case
/// Requirements: 10.2
/// </summary>
[Fact]
public void PaymentRecordDto_ShouldUseSnakeCase()
{
// Arrange
var dto = new HoneyBox.Model.Models.Payment.PaymentRecordDto
{
Id = 1,
UserId = 1,
OrderNum = "TEST001",
ChangeMoney = "100.00",
Content = "支付测试",
PayType = 1,
PayTypeText = "微信支付",
CreatedAt = 1234567890
};
// Act
var json = JsonSerializer.Serialize(dto, _jsonOptions);
// Assert
Assert.Contains("\"id\":", json);
Assert.Contains("\"user_id\":", json);
Assert.Contains("\"order_num\":", json);
Assert.Contains("\"change_money\":", json);
Assert.Contains("\"content\":", json);
Assert.Contains("\"pay_type\":", json);
Assert.Contains("\"pay_type_text\":", json);
Assert.Contains("\"created_at\":", json);
}
/// <summary>
/// 验证完整的微信支付响应格式与PHP API一致
/// Requirements: 10.1-10.4
/// </summary>
[Fact]
public void CompleteWechatPayResponse_ShouldMatchPhpFormat()
{
// Arrange - 模拟PHP API返回的格式
// PHP: return ['status' => 1, 'data' => $res];
// $res = ['appId' => ..., 'timeStamp' => ..., 'nonceStr' => ..., 'package' => ..., 'signType' => ..., 'paySign' => ...]
var result = new WechatPayResult
{
Status = 1,
Msg = "success",
Data = new WechatPayData
{
AppId = "wx1234567890abcdef",
TimeStamp = "1735776000",
NonceStr = "abc123def456",
Package = "prepay_id=wx123456789",
SignType = "MD5",
PaySign = "ABCDEF1234567890",
IsWeixin = 1
}
};
// Act
var json = JsonSerializer.Serialize(result, _jsonOptions);
// Assert - 验证顶层结构
Assert.Contains("\"status\":1", json);
Assert.Contains("\"msg\":\"success\"", json);
Assert.Contains("\"data\":", json);
// Assert - 验证支付参数字段名与PHP一致
Assert.Contains("\"appId\":\"wx1234567890abcdef\"", json);
Assert.Contains("\"timeStamp\":\"1735776000\"", json);
Assert.Contains("\"nonceStr\":\"abc123def456\"", json);
Assert.Contains("\"package\":\"prepay_id=wx123456789\"", json);
Assert.Contains("\"signType\":\"MD5\"", json);
Assert.Contains("\"paySign\":\"ABCDEF1234567890\"", json);
Assert.Contains("\"is_weixin\":1", json);
}
/// <summary>
/// 验证微信支付失败响应格式
/// Requirements: 10.1, 10.3
/// </summary>
[Fact]
public void WechatPayFailResponse_ShouldMatchPhpFormat()
{
// Arrange - 模拟PHP API返回的失败格式
// PHP: return ['status' => 0, 'msg' => '支付失败'];
var result = new WechatPayResult
{
Status = 0,
Msg = "网络故障,请稍后重试(支付参数错误)"
};
// Act
var json = JsonSerializer.Serialize(result, _jsonOptions);
// Assert - 验证结构正确不验证具体中文内容因为JSON会转义Unicode
Assert.Contains("\"status\":0", json);
Assert.Contains("\"msg\":", json);
// data字段应该为null
Assert.Contains("\"data\":null", json);
// 验证反序列化后内容正确
var deserialized = JsonSerializer.Deserialize<WechatPayResult>(json, _jsonOptions);
Assert.NotNull(deserialized);
Assert.Equal(0, deserialized.Status);
Assert.Equal("网络故障,请稍后重试(支付参数错误)", deserialized.Msg);
}
/// <summary>
/// 验证支付回调XML响应格式
/// Requirements: 10.1
/// </summary>
[Fact]
public void NotifyXmlResponse_ShouldMatchWechatFormat()
{
// Arrange - 微信要求的XML响应格式
var successXml = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
var failXml = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[签名失败]]></return_msg></xml>";
// Assert - 验证XML格式正确
Assert.Contains("<return_code><![CDATA[SUCCESS]]></return_code>", successXml);
Assert.Contains("<return_msg><![CDATA[OK]]></return_msg>", successXml);
Assert.Contains("<return_code><![CDATA[FAIL]]></return_code>", failXml);
}
#endregion
#region Lottery System Response Format Tests - Requirements 10.1-10.4
/// <summary>
/// 验证PrizeOrderLogRequest使用snake_case
/// Requirements: 10.2
/// </summary>
[Fact]
public void LotteryPrizeOrderLogRequest_ShouldUseSnakeCase()
{
// Arrange
var request = new HoneyBox.Model.Models.Lottery.PrizeOrderLogRequest
{
OrderNum = "TEST001"
};
// Act
var json = JsonSerializer.Serialize(request, _jsonOptions);
// Assert
Assert.Contains("\"order_num\":", json);
Assert.DoesNotContain("\"OrderNum\":", json);
}
/// <summary>
/// 验证InfiniteShangLogRequest使用snake_case
/// Requirements: 10.2
/// </summary>
[Fact]
public void InfiniteShangLogRequest_ShouldUseSnakeCase()
{
// Arrange
var request = new InfiniteShangLogRequest
{
GoodsId = 1,
ShangId = 10,
IsMibao = 0,
Page = 1,
PageSize = 10
};
// Act
var json = JsonSerializer.Serialize(request, _jsonOptions);
// Assert
Assert.Contains("\"goods_id\":", json);
Assert.Contains("\"shang_id\":", json);
Assert.Contains("\"is_mibao\":", json);
Assert.Contains("\"page\":", json);
Assert.Contains("\"page_size\":", json);
}
/// <summary>
/// 验证DailyPrizeRecordsRequest使用snake_case
/// Requirements: 10.2
/// </summary>
[Fact]
public void DailyPrizeRecordsRequest_ShouldUseSnakeCase()
{
// Arrange
var request = new DailyPrizeRecordsRequest
{
GoodsId = 1,
Page = 1,
PageSize = 10
};
// Act
var json = JsonSerializer.Serialize(request, _jsonOptions);
// Assert
Assert.Contains("\"goods_id\":", json);
Assert.Contains("\"page\":", json);
Assert.Contains("\"page_size\":", json);
}
/// <summary>
/// 验证ItemCardDrawRequest使用snake_case
/// Requirements: 10.2
/// </summary>
[Fact]
public void ItemCardDrawRequest_ShouldUseSnakeCase()
{
// Arrange
var request = new ItemCardDrawRequest
{
GoodsId = 1,
OrderListIds = "1,2,3"
};
// Act
var json = JsonSerializer.Serialize(request, _jsonOptions);
// Assert
Assert.Contains("\"goods_id\":", json);
Assert.Contains("\"order_list_ids\":", json);
}
/// <summary>
/// 验证InfiniteShangLogResponseDto使用snake_case
/// Requirements: 10.2
/// </summary>
[Fact]
public void InfiniteShangLogResponseDto_ShouldUseSnakeCase()
{
// Arrange
var dto = new InfiniteShangLogResponseDto
{
Category = new List<ShangCategoryDto>
{
new() { ShangId = 0, ShangTitle = "全部" },
new() { ShangId = 10, ShangTitle = "A赏" }
},
Data = new List<InfiniteShangLogItemDto>(),
LastPage = 1,
Total = 10
};
// Act
var json = JsonSerializer.Serialize(dto, _jsonOptions);
// Assert
Assert.Contains("\"category\":", json);
Assert.Contains("\"data\":", json);
Assert.Contains("\"last_page\":", json);
Assert.Contains("\"total\":", json);
Assert.Contains("\"shang_id\":", json);
Assert.Contains("\"shang_title\":", json);
}
/// <summary>
/// 验证InfiniteShangLogItemDto使用snake_case
/// Requirements: 10.2
/// </summary>
[Fact]
public void InfiniteShangLogItemDto_ShouldUseSnakeCase()
{
// Arrange
var dto = new InfiniteShangLogItemDto
{
Id = 1,
UserId = 1,
GoodslistTitle = "测试奖品",
GoodslistImgurl = "http://test.com/img.jpg",
GoodslistPrice = "100.00",
GoodslistMoney = "50.00",
ShangId = 10,
ShangTitle = "A赏",
ShangColor = "#FF0000",
PrizeNum = 1,
Addtime = "2024-01-01 12:00:00",
LuckNo = 1,
Doubling = 1,
IsLingzhu = 0,
UserInfo = new LotteryUserInfoDto
{
Nickname = "测***户",
HeadImg = "avatar.jpg"
}
};
// Act
var json = JsonSerializer.Serialize(dto, _jsonOptions);
// Assert
Assert.Contains("\"id\":", json);
Assert.Contains("\"user_id\":", json);
Assert.Contains("\"goodslist_title\":", json);
Assert.Contains("\"goodslist_imgurl\":", json);
Assert.Contains("\"goodslist_price\":", json);
Assert.Contains("\"goodslist_money\":", json);
Assert.Contains("\"shang_id\":", json);
Assert.Contains("\"shang_title\":", json);
Assert.Contains("\"shang_color\":", json);
Assert.Contains("\"prize_num\":", json);
Assert.Contains("\"addtime\":", json);
Assert.Contains("\"luck_no\":", json);
Assert.Contains("\"doubling\":", json);
Assert.Contains("\"is_lingzhu\":", json);
Assert.Contains("\"user_info\":", json);
Assert.Contains("\"nickname\":", json);
Assert.Contains("\"headimg\":", json);
}
/// <summary>
/// 验证DailyPrizeRecordItemDto使用snake_case
/// Requirements: 10.2
/// </summary>
[Fact]
public void DailyPrizeRecordItemDto_ShouldUseSnakeCase()
{
// Arrange
var dto = new DailyPrizeRecordItemDto
{
Id = 1,
UserId = 1,
GoodslistTitle = "测试奖品",
GoodslistImgurl = "http://test.com/img.jpg",
ShangId = 10,
ShangTitle = "A赏",
Addtime = "2024-01-01 12:00:00"
};
// Act
var json = JsonSerializer.Serialize(dto, _jsonOptions);
// Assert
Assert.Contains("\"id\":", json);
Assert.Contains("\"user_id\":", json);
Assert.Contains("\"goodslist_title\":", json);
Assert.Contains("\"goodslist_imgurl\":", json);
Assert.Contains("\"shang_id\":", json);
Assert.Contains("\"shang_title\":", json);
Assert.Contains("\"addtime\":", json);
}
/// <summary>
/// 验证InfinitePrizeRecordsResponseDto使用snake_case
/// Requirements: 10.2
/// </summary>
[Fact]
public void InfinitePrizeRecordsResponseDto_ShouldUseSnakeCase()
{
// Arrange
var dto = new InfinitePrizeRecordsResponseDto
{
Data = new List<InfinitePrizeRecordItemDto>(),
CurrentPage = 1,
LastPage = 10,
PerPage = 10,
Total = 100
};
// Act
var json = JsonSerializer.Serialize(dto, _jsonOptions);
// Assert
Assert.Contains("\"data\":", json);
Assert.Contains("\"current_page\":", json);
Assert.Contains("\"last_page\":", json);
Assert.Contains("\"per_page\":", json);
Assert.Contains("\"total\":", json);
}
/// <summary>
/// 验证InfinitePrizeRecordItemDto使用snake_case
/// Requirements: 10.2
/// </summary>
[Fact]
public void InfinitePrizeRecordItemDto_ShouldUseSnakeCase()
{
// Arrange
var dto = new InfinitePrizeRecordItemDto
{
UserId = 1,
GoodslistTitle = "测试奖品",
GoodslistImgurl = "http://test.com/img.jpg",
Addtime = "2024-01-01 12:00:00"
};
// Act
var json = JsonSerializer.Serialize(dto, _jsonOptions);
// Assert
Assert.Contains("\"user_id\":", json);
Assert.Contains("\"goodslist_title\":", json);
Assert.Contains("\"goodslist_imgurl\":", json);
Assert.Contains("\"addtime\":", json);
}
/// <summary>
/// 验证ItemCardDrawResponseDto使用snake_case
/// Requirements: 10.2
/// </summary>
[Fact]
public void ItemCardDrawResponseDto_ShouldUseSnakeCase()
{
// Arrange
var dto = new ItemCardDrawResponseDto
{
Status = 0,
OrderNum = "TEST001",
Prizes = new List<ItemCardDrawPrizeDto>
{
new()
{
Id = 1,
Title = "测试奖品",
ImgUrl = "http://test.com/img.jpg",
ShangId = 10,
ShangTitle = "A赏",
ShangColor = "#FF0000",
Price = "100.00",
ScMoney = "50.00",
PrizeCode = "PRIZE001",
LuckNo = 1
}
},
RemainingCards = 5
};
// Act
var json = JsonSerializer.Serialize(dto, _jsonOptions);
// Assert
Assert.Contains("\"status\":", json);
Assert.Contains("\"order_num\":", json);
Assert.Contains("\"prizes\":", json);
Assert.Contains("\"remaining_cards\":", json);
Assert.Contains("\"id\":", json);
Assert.Contains("\"title\":", json);
Assert.Contains("\"img_url\":", json);
Assert.Contains("\"shang_id\":", json);
Assert.Contains("\"shang_title\":", json);
Assert.Contains("\"shang_color\":", json);
Assert.Contains("\"price\":", json);
Assert.Contains("\"sc_money\":", json);
Assert.Contains("\"prize_code\":", json);
Assert.Contains("\"luck_no\":", json);
}
/// <summary>
/// 验证PrizeOrderLogResponseDto使用snake_case
/// Requirements: 10.2
/// </summary>
[Fact]
public void PrizeOrderLogResponseDto_ShouldUseSnakeCase()
{
// Arrange
var dto = new PrizeOrderLogResponseDto
{
Data = new List<PrizeOrderLogDto>
{
new()
{
Id = 1,
UserId = 1,
GoodsListTitle = "测试奖品",
GoodsListImgUrl = "http://test.com/img.jpg",
GoodsListPrice = "100.00",
GoodsListMoney = "50.00",
Status = 0,
LuckNo = 1,
ShangId = 10,
ShangTitle = "A赏",
Doubling = 1,
IsLingzhu = 0,
GoodsListType = 1,
GoodsListId = 1,
ParentGoodsListId = 0,
PrizeNum = 1,
AddTime = 1234567890
}
},
ItemCardCount = 5,
PrizeNum = 1
};
// Act
var json = JsonSerializer.Serialize(dto, _jsonOptions);
// Assert
Assert.Contains("\"data\":", json);
Assert.Contains("\"item_card_count\":", json);
Assert.Contains("\"prize_num\":", json);
Assert.Contains("\"id\":", json);
Assert.Contains("\"user_id\":", json);
Assert.Contains("\"goodslist_title\":", json);
Assert.Contains("\"goodslist_imgurl\":", json);
Assert.Contains("\"goodslist_price\":", json);
Assert.Contains("\"goodslist_money\":", json);
Assert.Contains("\"status\":", json);
Assert.Contains("\"luck_no\":", json);
Assert.Contains("\"shang_id\":", json);
Assert.Contains("\"shang_title\":", json);
Assert.Contains("\"doubling\":", json);
Assert.Contains("\"is_lingzhu\":", json);
Assert.Contains("\"goodslist_type\":", json);
Assert.Contains("\"goodslist_id\":", json);
Assert.Contains("\"parent_goods_list_id\":", json);
Assert.Contains("\"addtime\":", json);
}
/// <summary>
/// 验证InfinitePrizeOrderLogResponseDto使用snake_case
/// Requirements: 10.2
/// </summary>
[Fact]
public void InfinitePrizeOrderLogResponseDto_ShouldUseSnakeCase()
{
// Arrange
var dto = new InfinitePrizeOrderLogResponseDto
{
UserInfo = new UserBasicInfoDto
{
Nickname = "测试用户",
HeadImg = "avatar.jpg"
},
Data = new List<PrizeOrderLogDto>(),
ItemCardCount = 5,
PrizeNum = 1
};
// Act
var json = JsonSerializer.Serialize(dto, _jsonOptions);
// Assert
Assert.Contains("\"user_info\":", json);
Assert.Contains("\"data\":", json);
Assert.Contains("\"item_card_count\":", json);
Assert.Contains("\"prize_num\":", json);
Assert.Contains("\"nickname\":", json);
Assert.Contains("\"headimg\":", json);
}
/// <summary>
/// 验证完整的无限赏中奖记录响应格式与PHP API一致
/// Requirements: 10.1-10.4
/// </summary>
[Fact]
public void CompleteInfiniteShangLogResponse_ShouldMatchPhpFormat()
{
// Arrange - 模拟PHP API返回的格式
var data = new InfiniteShangLogResponseDto
{
Category = new List<ShangCategoryDto>
{
new() { ShangId = 0, ShangTitle = "全部" },
new() { ShangId = 10, ShangTitle = "A赏" }
},
Data = new List<InfiniteShangLogItemDto>
{
new()
{
Id = 1,
UserId = 1,
GoodslistTitle = "测试奖品",
GoodslistImgurl = "http://test.com/img.jpg",
GoodslistPrice = "100.00",
GoodslistMoney = "50.00",
ShangId = 10,
ShangTitle = "A赏",
ShangColor = "#FF0000",
PrizeNum = 1,
Addtime = "2024-01-01 12:00:00",
LuckNo = 1,
Doubling = 1,
IsLingzhu = 0,
UserInfo = new LotteryUserInfoDto
{
Nickname = "测***户",
HeadImg = "avatar.jpg"
}
}
},
LastPage = 1,
Total = 1
};
var response = ApiResponse<InfiniteShangLogResponseDto>.Success(data);
// Act
var json = JsonSerializer.Serialize(response, _jsonOptions);
// Assert - 验证顶层结构
Assert.Contains("\"status\":1", json);
Assert.Contains("\"msg\":\"success\"", json);
Assert.Contains("\"data\":", json);
// Assert - 验证数据字段使用snake_case
Assert.Contains("\"category\":", json);
Assert.Contains("\"last_page\":", json);
Assert.Contains("\"total\":", json);
Assert.Contains("\"shang_id\":", json);
Assert.Contains("\"shang_title\":", json);
Assert.Contains("\"user_id\":", json);
Assert.Contains("\"goodslist_title\":", json);
Assert.Contains("\"goodslist_imgurl\":", json);
Assert.Contains("\"goodslist_price\":", json);
Assert.Contains("\"goodslist_money\":", json);
Assert.Contains("\"shang_color\":", json);
Assert.Contains("\"prize_num\":", json);
Assert.Contains("\"addtime\":", json);
Assert.Contains("\"luck_no\":", json);
Assert.Contains("\"doubling\":", json);
Assert.Contains("\"is_lingzhu\":", json);
Assert.Contains("\"user_info\":", json);
Assert.Contains("\"nickname\":", json);
Assert.Contains("\"headimg\":", json);
}
/// <summary>
/// 验证完整的道具卡抽奖响应格式与PHP API一致
/// Requirements: 10.1-10.4
/// </summary>
[Fact]
public void CompleteItemCardDrawResponse_ShouldMatchPhpFormat()
{
// Arrange
var data = new ItemCardDrawResponseDto
{
Status = 0,
OrderNum = "MH_20240101120000001",
Prizes = new List<ItemCardDrawPrizeDto>
{
new()
{
Id = 1,
Title = "A赏奖品",
ImgUrl = "http://test.com/prize.jpg",
ShangId = 10,
ShangTitle = "A赏",
ShangColor = "#FF0000",
Price = "100.00",
ScMoney = "50.00",
PrizeCode = "PRIZE20240101001",
LuckNo = 88
}
},
RemainingCards = 4
};
var response = ApiResponse<ItemCardDrawResponseDto>.Success(data, "重抽成功");
// Act
var json = JsonSerializer.Serialize(response, _jsonOptions);
// Assert - 验证顶层结构
Assert.Contains("\"status\":1", json);
Assert.Contains("\"data\":", json);
// Assert - 验证数据字段使用snake_case
Assert.Contains("\"order_num\":", json);
Assert.Contains("\"prizes\":", json);
Assert.Contains("\"remaining_cards\":", json);
Assert.Contains("\"img_url\":", json);
Assert.Contains("\"shang_id\":", json);
Assert.Contains("\"shang_title\":", json);
Assert.Contains("\"shang_color\":", json);
Assert.Contains("\"sc_money\":", json);
Assert.Contains("\"prize_code\":", json);
Assert.Contains("\"luck_no\":", json);
}
/// <summary>
/// 验证抽奖系统失败响应格式
/// Requirements: 10.1, 10.3
/// </summary>
[Fact]
public void LotteryFailResponse_ShouldMatchPhpFormat()
{
// Arrange - 模拟PHP API返回的失败格式
var response = ApiResponse<InfiniteShangLogResponseDto>.Fail("盒子不存在");
// Act
var json = JsonSerializer.Serialize(response, _jsonOptions);
// Assert
Assert.Contains("\"status\":0", json);
Assert.Contains("\"msg\":", json);
// 验证反序列化后内容正确
var deserialized = JsonSerializer.Deserialize<ApiResponse<InfiniteShangLogResponseDto>>(json, _jsonOptions);
Assert.NotNull(deserialized);
Assert.Equal(0, deserialized.Status);
Assert.Equal("盒子不存在", deserialized.Msg);
}
/// <summary>
/// 验证ShangInfoDto使用snake_case用于赏品等级信息
/// Requirements: 10.2
/// </summary>
[Fact]
public void ShangInfoDto_ShouldUseSnakeCase()
{
// Arrange
var dto = new ShangInfoDto
{
Id = 10,
Title = "A赏",
ImgUrl = "http://test.com/shang.jpg",
Color = "#FF0000",
SpecialImgUrl = "http://test.com/special.jpg"
};
// Act
var json = JsonSerializer.Serialize(dto, _jsonOptions);
// Assert
Assert.Contains("\"id\":", json);
Assert.Contains("\"title\":", json);
Assert.Contains("\"img_url\":", json);
Assert.Contains("\"color\":", json);
Assert.Contains("\"special_img_url\":", json);
}
/// <summary>
/// 验证LotteryUserInfoDto使用snake_case
/// Requirements: 10.2
/// </summary>
[Fact]
public void LotteryUserInfoDto_ShouldUseSnakeCase()
{
// Arrange
var dto = new LotteryUserInfoDto
{
Nickname = "测***户",
HeadImg = "avatar.jpg"
};
// Act
var json = JsonSerializer.Serialize(dto, _jsonOptions);
// Assert
Assert.Contains("\"nickname\":", json);
Assert.Contains("\"headimg\":", json);
}
#endregion
}