Files
KJD/test/ApiTransportSmoke.cs
T
2026-11-05 18:50:13 +08:00

113 lines
4.9 KiB
C#

using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using CsharpHttpHelper;
using Work.Store.Api;
// Compile against Work.dll, AllBase.dll and Newtonsoft.Json.dll; run beside these assemblies.
internal static class ApiTransportSmoke
{
private static void Check(bool passed)
{
if (!passed) throw new Exception("ApiTransport assertion failed.");
}
private static async Task<T> Respond<T>(int status, string json, Func<ApiTransport, Task<T>> request)
{
var listener = new TcpListener(IPAddress.Loopback, 0);
listener.Start();
var server = Task.Run(() =>
{
using (var client = listener.AcceptTcpClient())
using (var stream = client.GetStream())
using (var reader = new StreamReader(stream, Encoding.ASCII, false, 1024, true))
{
while (!string.IsNullOrEmpty(reader.ReadLine())) { }
var body = Encoding.UTF8.GetBytes(json);
var header = Encoding.ASCII.GetBytes("HTTP/1.1 " + status + " Response\r\nContent-Type: application/json\r\nContent-Length: " + body.Length + "\r\nConnection: close\r\n\r\n");
stream.Write(header, 0, header.Length);
stream.Write(body, 0, body.Length);
}
});
try
{
var port = ((IPEndPoint)listener.LocalEndpoint).Port;
return await request(new ApiTransport("http://127.0.0.1:" + port));
}
finally
{
listener.Stop();
await server;
}
}
private static Task<string> Send(ApiTransport transport)
{
return transport.SendAsync<string>("Test", MethodType.Get, authenticated: false, timeoutMilliseconds: 5000);
}
public static async Task Main()
{
Check(await Respond(200, "{\"code\":0,\"success\":true,\"data\":\"ok\"}", Send) == "ok");
Check(await Respond(200, "{\"code\":0,\"success\":true,\"data\":null}", Send) == null);
Check(await Respond(200, "{\"success\":true,\"data\":null}", transport =>
transport.SendAsync<int>("Test", MethodType.Get, authenticated: false)) == 0);
Check((await Respond(200, "{\"success\":true,\"data\":[1,2]}", transport =>
transport.SendAsync<int[]>("Test", MethodType.Get, authenticated: false)))[1] == 2);
foreach (var status in new[] { 200, 500 })
{
await Respond(status, "{\"data\":null,\"code\":5000,\"msg\":\"semaphore limit\",\"traceId\":\"trace-1\",\"success\":false}", async transport =>
{
try { await Send(transport); throw new Exception("Expected ApiException."); }
catch (ApiException ex)
{
Check(ex.Code == 5000 && ex.TraceId == "trace-1" && ex.Message.Contains("semaphore limit"));
}
return true;
});
}
await Respond(200, "{\"code\":200,\"success\":false,\"data\":{}}", async transport =>
{
try { await Send(transport); throw new Exception("Expected ApiException."); }
catch (ApiException ex) { Check(ex.Code == 200 && ex.ResponseData is Newtonsoft.Json.Linq.JObject); }
return true;
});
foreach (var json in new[] { "not json", "[]", "null", "{\"code\":\"invalid\",\"success\":true}",
"{\"code\":200,\"data\":\"legacy\"}", "{\"code\":200,\"success\":null}", "{}" })
{
await Respond(200, json, async transport =>
{
try { await Send(transport); throw new Exception("Expected response format error."); }
catch (HttpException ex) { Check(ex.Message.Contains("Result")); }
return true;
});
}
await Respond(401, "{\"code\":401,\"success\":false,\"msg\":\"expired\"}", async transport =>
{
try { await Send(transport); throw new Exception("Expected authentication error."); }
catch (HttpException ex) { Check(!ex.IsOnline && ex.Message == "expired"); }
return true;
});
Check(await Respond(200, "{\"success\":true,\"data\":\"ok\"}", transport =>
Task.Run(() => transport.GetRequired<string>("Test", MethodType.Get, authenticated: false))) == "ok");
var stalled = new TcpListener(IPAddress.Loopback, 0);
stalled.Start();
try
{
var transport = new ApiTransport("http://" + stalled.LocalEndpoint);
try
{
await transport.SendAsync<string>("Test", MethodType.Get, authenticated: false, timeoutMilliseconds: 250);
throw new Exception("Expected timeout.");
}
catch (HttpException ex) { Check(ex.Message.Contains("超时")); }
}
finally { stalled.Stop(); }
Console.WriteLine("ApiTransport smoke checks passed.");
}
}