193 lines
6.6 KiB
C#
193 lines
6.6 KiB
C#
namespace Yh.SchClass.OpenApi.Tests;
|
|
|
|
/// <summary>
|
|
/// 响应解析与状态码归一(教务 OA 兼容端点约定):
|
|
/// 成功返回裸数据;404 / 204 表示"无数据"归一为 null / 空列表;400 及其它非 2xx 抛异常。
|
|
/// </summary>
|
|
public class SchClassResponseParsingTests
|
|
{
|
|
private static readonly SchClassOpenApiOptions Options = new() { BaseHost = "http://base.yunhaixingtong.com" };
|
|
|
|
private static SchClassVClassClient VClass(FakeHandler handler) => new(new HttpClient(handler), Options);
|
|
|
|
private static SchClassTeaInfoClient Tea(FakeHandler handler) => new(new HttpClient(handler), Options);
|
|
|
|
private static SchClassSchoolingClient Schooling(FakeHandler handler) => new(new HttpClient(handler), Options);
|
|
|
|
[Fact]
|
|
public async Task 裸数组_解析为兼容行列表()
|
|
{
|
|
var handler = FakeHandler.Json(HttpStatusCode.OK, """
|
|
[{"Id":1,"Name":"高一(1)班","ClassID":1001,"SchSerID":"S001"},
|
|
{"Id":2,"Name":"高一(2)班","ClassID":1002,"SchSerID":"S001"}]
|
|
""");
|
|
|
|
var rows = await VClass(handler).GetVClassListAsync("S001");
|
|
|
|
Assert.Equal(2, rows.Count);
|
|
Assert.Equal("高一(1)班", rows[0]["Name"].GetString());
|
|
Assert.Equal(1001, rows[0]["ClassID"].GetInt32());
|
|
Assert.Equal("S001", rows[1]["SchSerID"].GetString());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task 单对象_归一为单项列表()
|
|
{
|
|
var handler = FakeHandler.Json(HttpStatusCode.OK, """{"Id":9,"Name":"高三(7)班"}""");
|
|
|
|
var rows = await VClass(handler).GetVClassListAsync("S001");
|
|
|
|
Assert.Single(rows);
|
|
Assert.Equal(9, rows[0]["Id"].GetInt32());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task 裸字符串数组_解析为List_string()
|
|
{
|
|
var handler = FakeHandler.Json(HttpStatusCode.OK, """["语文","数学"]""");
|
|
|
|
var subs = await new SchClassSubInfoClient(new HttpClient(handler), Options)
|
|
.GetSchTeachSubListAsync("S001");
|
|
|
|
Assert.Equal(new[] { "语文", "数学" }, subs);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task HTTP404_列表归一为空列表()
|
|
{
|
|
var rows = await VClass(FakeHandler.Empty(HttpStatusCode.NotFound)).GetVClassListAsync("S001");
|
|
|
|
Assert.Empty(rows);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task HTTP404_单对象归一为null()
|
|
{
|
|
var row = await VClass(FakeHandler.Empty(HttpStatusCode.NotFound)).GetVClassFirstAsync("1001");
|
|
|
|
Assert.Null(row);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task HTTP204_归一为空列表()
|
|
{
|
|
var handler = FakeHandler.Json(HttpStatusCode.NoContent, "");
|
|
|
|
var list = await Tea(handler).GetArrTeaInfoAsync("1,2,3");
|
|
|
|
Assert.Empty(list);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task HTTP400_抛异常并携带状态码与响应体()
|
|
{
|
|
var handler = FakeHandler.Text(HttpStatusCode.BadRequest, "Required String parameter 'userIds' is not present");
|
|
|
|
var ex = await Assert.ThrowsAsync<SchClassOpenApiException>(() => Tea(handler).GetArrTeaInfoAsync(null!));
|
|
|
|
Assert.Equal(400, ex.StatusCode);
|
|
Assert.Contains("400", ex.Message);
|
|
Assert.Contains("userIds", ex.ResponseBody!);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task HTTP500_抛异常()
|
|
{
|
|
var handler = FakeHandler.Text(HttpStatusCode.InternalServerError, "boom");
|
|
|
|
var ex = await Assert.ThrowsAsync<SchClassOpenApiException>(() => VClass(handler).GetVClassListAsync("S001"));
|
|
|
|
Assert.Equal(500, ex.StatusCode);
|
|
Assert.Contains("500", ex.Message);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task 空响应体_返回空列表不抛()
|
|
{
|
|
var handler = new FakeHandler((_, _) => new HttpResponseMessage(HttpStatusCode.OK)
|
|
{
|
|
Content = new StringContent("", Encoding.UTF8, "application/json"),
|
|
});
|
|
|
|
Assert.Empty(await VClass(handler).GetVClassListAsync("S001"));
|
|
Assert.Null(await VClass(handler).GetVClassFirstAsync("1"));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task JSON字面量null_返回空列表()
|
|
{
|
|
var handler = FakeHandler.Json(HttpStatusCode.OK, "null");
|
|
|
|
Assert.Empty(await VClass(handler).GetVClassListAsync("S001"));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task 信封code0_提取data节点()
|
|
{
|
|
var handler = FakeHandler.Json(HttpStatusCode.OK,
|
|
"""{"code":0,"msg":"","data":[{"Id":1,"Name":"高一(1)班"}]}""");
|
|
|
|
var rows = await VClass(handler).GetVClassListAsync("S001");
|
|
|
|
Assert.Single(rows);
|
|
Assert.Equal("高一(1)班", rows[0]["Name"].GetString());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task 信封code0且data为null_归一为空()
|
|
{
|
|
var handler = FakeHandler.Json(HttpStatusCode.OK, """{"code":0,"msg":"ok","data":null}""");
|
|
|
|
Assert.Empty(await VClass(handler).GetVClassListAsync("S001"));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task 信封业务失败_抛异常并携带msg()
|
|
{
|
|
var handler = FakeHandler.Json(HttpStatusCode.OK,
|
|
"""{"code":3,"msg":"学校不存在","data":null}""");
|
|
|
|
var ex = await Assert.ThrowsAsync<SchClassOpenApiException>(() => VClass(handler).GetVClassListAsync("S001"));
|
|
|
|
Assert.Equal("学校不存在", ex.Message);
|
|
Assert.Equal(0, ex.StatusCode);
|
|
Assert.Contains("学校不存在", ex.ResponseBody!);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task 业务行含code字段_不被误判为信封()
|
|
{
|
|
// 学制等级行自身带 code=gradeId 字段,只有 code 而没有 data/msg,必须原样返回
|
|
var handler = FakeHandler.Json(HttpStatusCode.OK,
|
|
"""[{"Id":1,"Name":"一年级","OwnerId":2,"Levell":1,"code":11,"sx":1}]""");
|
|
|
|
var rows = await Schooling(handler).GetSchLenLevelListAsync("S001");
|
|
|
|
Assert.Single(rows);
|
|
Assert.Equal(11, rows[0]["code"].GetInt32());
|
|
Assert.Equal("一年级", rows[0]["Name"].GetString());
|
|
}
|
|
|
|
[Fact]
|
|
public async Task 非法JSON_抛解析异常()
|
|
{
|
|
var handler = FakeHandler.Json(HttpStatusCode.OK, "<html>502 Bad Gateway</html>");
|
|
|
|
var ex = await Assert.ThrowsAsync<SchClassOpenApiException>(() => VClass(handler).GetVClassListAsync("S001"));
|
|
|
|
Assert.Contains("解析失败", ex.Message);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task 兼容行键名_大小写与Java侧put顺序一致()
|
|
{
|
|
var handler = FakeHandler.Json(HttpStatusCode.OK,
|
|
"""[{"SchSerID":"S001","TeaTel":"13800000000","TeaName":"王老师","UserTeaSerID":77}]""");
|
|
|
|
var rows = await new SchClassDbClassClient(new HttpClient(handler), Options).GetSchteaListAsync(1);
|
|
|
|
Assert.Equal(new[] { "SchSerID", "TeaTel", "TeaName", "UserTeaSerID" }, rows[0].Keys);
|
|
Assert.Equal("王老师", rows[0]["TeaName"].GetString());
|
|
}
|
|
}
|