bag
This commit is contained in:
@@ -0,0 +1,185 @@
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using Yh.Platform.Abstractions;
|
||||
using Yh.Platform.OpenApi;
|
||||
using Xunit;
|
||||
|
||||
namespace Yh.Platform.OpenApi.Tests;
|
||||
|
||||
/// <summary>伪造 HttpMessageHandler:捕获最近一次请求并回放预设响应。</summary>
|
||||
public sealed class FakeHandler : HttpMessageHandler
|
||||
{
|
||||
private readonly Func<HttpRequestMessage, string?, HttpResponseMessage> _responder;
|
||||
|
||||
public FakeHandler(Func<HttpRequestMessage, string?, HttpResponseMessage> responder)
|
||||
{
|
||||
_responder = responder;
|
||||
}
|
||||
|
||||
public HttpRequestMessage? LastRequest { get; private set; }
|
||||
|
||||
public string? LastRequestBody { get; private set; }
|
||||
|
||||
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
LastRequest = request;
|
||||
LastRequestBody = request.Content == null
|
||||
? null
|
||||
: await request.Content.ReadAsStringAsync(cancellationToken);
|
||||
return _responder(request, LastRequestBody);
|
||||
}
|
||||
|
||||
public static FakeHandler Json(HttpStatusCode statusCode, string json)
|
||||
=> new((_, _) => new HttpResponseMessage(statusCode)
|
||||
{
|
||||
Content = new StringContent(json, Encoding.UTF8, "application/json"),
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 信封感知解析对拍(对齐 contract/fixtures/envelope-*.json 约定):
|
||||
/// 成功为裸数据;业务失败为 HTTP 200 + {code≠0,msg,data},客户端必须抛 DException。
|
||||
/// </summary>
|
||||
public class EnvelopeParsingTests
|
||||
{
|
||||
private static OpenApiOptions Options() => new() { BaseHost = "http://base.bestsch.com", BasePath = "/userbase" };
|
||||
|
||||
private static OpenApiUserClient Client(FakeHandler handler) => new(new HttpClient(handler), Options());
|
||||
|
||||
[Fact]
|
||||
public async Task 裸数据成功响应_解析为BoaUser()
|
||||
{
|
||||
var handler = FakeHandler.Json(HttpStatusCode.OK,
|
||||
"""{"id":1001,"account":"zhangsan","name":"张三","currentRole":{"roleId":3,"roleName":"教师","schId":2}}""");
|
||||
var user = await Client(handler).GetUserByIdAsync("1001");
|
||||
|
||||
Assert.NotNull(user);
|
||||
Assert.Equal(1001, user!.Id);
|
||||
Assert.Equal("zhangsan", user.Account);
|
||||
Assert.Equal("张三", user.Name);
|
||||
Assert.NotNull(user.CurrentRole);
|
||||
Assert.Equal(3, user.CurrentRole!.RoleId);
|
||||
Assert.Equal("教师", user.CurrentRole.RoleName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task 包裹信封code0_提取data节点()
|
||||
{
|
||||
var handler = FakeHandler.Json(HttpStatusCode.OK,
|
||||
"""{"code":0,"msg":"","data":{"id":1001,"account":"zhangsan","name":"张三"}}""");
|
||||
var user = await Client(handler).GetUserByIdAsync("1001");
|
||||
|
||||
Assert.NotNull(user);
|
||||
Assert.Equal("zhangsan", user!.Account);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task 业务失败信封_抛DException并携带code与data()
|
||||
{
|
||||
var handler = FakeHandler.Json(HttpStatusCode.OK,
|
||||
"""{"code":5,"msg":"组下存在子组或用户,禁止删除","data":{"groupId":9,"users":35}}""");
|
||||
|
||||
var ex = await Assert.ThrowsAsync<DException>(() => new OpenApiGroupClient(new HttpClient(handler), Options()).GetGroupByIdAsync(9));
|
||||
|
||||
var ec = Assert.IsType<OpenApiErrorCode>(ex.Code);
|
||||
Assert.Equal(5, ec.Code);
|
||||
Assert.Equal("组下存在子组或用户,禁止删除", ec.Msg);
|
||||
Assert.Contains("禁止删除", ex.Message);
|
||||
Assert.NotNull(ex.Data);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task 业务失败信封data为null_EnvelopeData为null()
|
||||
{
|
||||
var handler = FakeHandler.Json(HttpStatusCode.OK,
|
||||
"""{"code":4,"msg":"登录已经过期,请重新登录","data":null}""");
|
||||
|
||||
var ex = await Assert.ThrowsAsync<DException>(() => Client(handler).GetUserByIdAsync("1"));
|
||||
|
||||
Assert.Equal(4, Assert.IsType<OpenApiErrorCode>(ex.Code).Code);
|
||||
Assert.Null(ex.EnvelopeData);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task 非成功HTTP状态_抛OpenApiException()
|
||||
{
|
||||
var handler = FakeHandler.Json(HttpStatusCode.InternalServerError, "boom");
|
||||
|
||||
var ex = await Assert.ThrowsAsync<OpenApiException>(() => Client(handler).GetUserByIdAsync("1"));
|
||||
|
||||
Assert.Contains("500", ex.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task 列表解析_裸数组与单对象两种形态()
|
||||
{
|
||||
var arrayHandler = FakeHandler.Json(HttpStatusCode.OK,
|
||||
"""[{"roleId":3,"roleName":"教师","schId":2},{"roleId":5,"roleName":"班主任","schId":2}]""");
|
||||
var roles = await Client(arrayHandler).GetUserRoleListAsync(1001);
|
||||
Assert.Equal(2, roles.Count);
|
||||
Assert.Equal("教师", roles[0].RoleName);
|
||||
Assert.Equal(5, roles[1].RoleId);
|
||||
|
||||
var singleHandler = FakeHandler.Json(HttpStatusCode.OK, """{"roleId":3,"roleName":"教师","schId":2}""");
|
||||
var single = await Client(singleHandler).GetUserRoleListAsync(1001);
|
||||
Assert.Single(single);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GET请求_URL与query构造正确()
|
||||
{
|
||||
var handler = FakeHandler.Json(HttpStatusCode.OK, "null");
|
||||
await Client(handler).GetUserModuleAsync(1001, 2);
|
||||
|
||||
var url = handler.LastRequest!.RequestUri!;
|
||||
Assert.Equal("http://base.bestsch.com/userbase/oapi/user/modules", $"{url.Scheme}://{url.Host}{url.AbsolutePath}");
|
||||
Assert.Equal("?userId=1001&schId=2", url.Query);
|
||||
Assert.Equal(HttpMethod.Get, handler.LastRequest.Method);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task query中的null值参数被丢弃()
|
||||
{
|
||||
var handler = FakeHandler.Json(HttpStatusCode.OK, "[]");
|
||||
await Client(handler).GetUserModuleAsync(1001, null);
|
||||
|
||||
Assert.DoesNotContain("schId", handler.LastRequest!.RequestUri!.Query);
|
||||
Assert.Equal("?userId=1001", handler.LastRequest.RequestUri.Query);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task POST请求_编辑用户时路径id回写请求体()
|
||||
{
|
||||
var handler = FakeHandler.Json(HttpStatusCode.OK, """{"id":7,"account":"lisi","name":"李四"}""");
|
||||
var request = new SysUserRequest { Account = "lisi", RealName = "李四" };
|
||||
|
||||
await Client(handler).EditUserAsync(7, request);
|
||||
|
||||
Assert.Equal(HttpMethod.Post, handler.LastRequest!.Method);
|
||||
Assert.EndsWith("/userbase/oapi/user/edit/7", handler.LastRequest.RequestUri!.AbsolutePath);
|
||||
Assert.NotNull(handler.LastRequestBody);
|
||||
Assert.Contains("\"id\":7", handler.LastRequestBody);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task void解析_业务失败仍抛异常_成功不抛()
|
||||
{
|
||||
var ok = FakeHandler.Json(HttpStatusCode.OK, """{"code":0,"msg":"","data":null}""");
|
||||
await Client(ok).DeleteUserAsync(1, 2, 3);
|
||||
|
||||
var fail = FakeHandler.Json(HttpStatusCode.OK, """{"code":5,"msg":"无权限","data":null}""");
|
||||
await Assert.ThrowsAsync<DException>(() => Client(fail).DeleteUserAsync(1, 2, 3));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task 空响应体_返回default不抛()
|
||||
{
|
||||
var handler = new FakeHandler((_, _) => new HttpResponseMessage(HttpStatusCode.OK)
|
||||
{
|
||||
Content = new StringContent("", Encoding.UTF8, "application/json"),
|
||||
});
|
||||
|
||||
var user = await Client(handler).GetUserByIdAsync("1");
|
||||
Assert.Null(user);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user