Files
rudder-csharp-sdk/tests/Rudder.Core.Tests/HttpClientTransportTests.cs
T
2026-08-12 14:04:55 +03:00

144 lines
5.5 KiB
C#

using System.Net;
using System.Text;
using RudderSdk.Core;
using RudderSdk.Core.Models.Player;
using Xunit;
namespace RudderSdk.Core.Tests;
public sealed class HttpClientTransportTests
{
[Fact]
public async Task Maps_404_To_NotFoundException_With_Code_And_RequestId()
{
var transport = CreateTransport(_ => Error(HttpStatusCode.NotFound, """{"code":"store_not_found","error":"Store not found","requestId":"req-123"}"""));
var exception = await Assert.ThrowsAsync<RudderNotFoundException>(
() => transport.SendAsync<object, PlayerProfile>("GET", "/sdk/v1/stores/main", null, "token"));
Assert.Equal(404, exception.StatusCode);
Assert.Equal("store_not_found", exception.Code);
Assert.Equal("req-123", exception.RequestId);
Assert.Equal("Store not found", exception.Message);
}
[Fact]
public async Task Maps_429_To_RateLimitException()
{
var transport = CreateTransport(_ => Error(HttpStatusCode.TooManyRequests, """{"code":"rate_limited","error":"Slow down"}"""));
var exception = await Assert.ThrowsAsync<RudderRateLimitException>(
() => transport.SendAsync<object, PlayerProfile>("GET", "/sdk/v1/player/information", null, null));
Assert.Equal(429, exception.StatusCode);
Assert.Equal("rate_limited", exception.Code);
}
[Fact]
public async Task Maps_401_To_AuthException()
{
var transport = CreateTransport(_ => Error(HttpStatusCode.Unauthorized, """{"error":"Unauthorized"}"""));
var exception = await Assert.ThrowsAsync<RudderAuthException>(
() => transport.SendAsync<object, PlayerProfile>("GET", "/sdk/v1/player/information", null, null));
Assert.Equal(401, exception.StatusCode);
Assert.Null(exception.RequestId);
}
[Fact]
public async Task Maps_500_To_Base_ApiException()
{
var transport = CreateTransport(_ => Error(HttpStatusCode.InternalServerError, """{"code":"internal","error":"Boom"}"""));
var exception = await Assert.ThrowsAsync<RudderApiException>(
() => transport.SendAsync<object, PlayerProfile>("GET", "/sdk/v1/player/information", null, null));
Assert.Equal(typeof(RudderApiException), exception.GetType());
Assert.Equal(500, exception.StatusCode);
Assert.Equal("internal", exception.Code);
}
[Fact]
public async Task Non_Json_Error_Body_Falls_Back_To_Reason_Phrase()
{
var transport = CreateTransport(_ => new HttpResponseMessage(HttpStatusCode.BadGateway)
{
Content = new StringContent("<html>bad gateway</html>", Encoding.UTF8, "text/html"),
ReasonPhrase = "Bad Gateway"
});
var exception = await Assert.ThrowsAsync<RudderApiException>(
() => transport.SendAsync<object, PlayerProfile>("GET", "/sdk/v1/player/information", null, null));
Assert.Equal(502, exception.StatusCode);
Assert.Equal(string.Empty, exception.Code);
Assert.Equal("Bad Gateway", exception.Message);
}
[Fact]
public async Task Network_Failure_Maps_To_NetworkException()
{
var transport = CreateTransport(_ => throw new HttpRequestException("Connection refused"));
var exception = await Assert.ThrowsAsync<RudderNetworkException>(
() => transport.SendAsync<object, PlayerProfile>("GET", "/sdk/v1/player/information", null, null));
Assert.Equal(0, exception.StatusCode);
Assert.Equal("Connection refused", exception.Message);
}
[Fact]
public async Task Success_Deserializes_Response_And_Sends_Bearer_Token()
{
StubHandler? handler = null;
var transport = CreateTransport(request =>
{
return new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent("""{"player":{"id":"p1"},"wallets":[]}""", Encoding.UTF8, "application/json")
};
}, h => handler = h);
var profile = await transport.SendAsync<object, PlayerProfile>("GET", "/sdk/v1/player/information", null, "access-token");
Assert.Equal("p1", profile.Player.Id);
Assert.Equal("Bearer", handler!.LastRequest!.Headers.Authorization!.Scheme);
Assert.Equal("access-token", handler.LastRequest.Headers.Authorization.Parameter);
Assert.Equal("http://localhost:8082/sdk/v1/player/information", handler.LastRequest.RequestUri!.ToString());
}
private static HttpClientTransport CreateTransport(
Func<HttpRequestMessage, HttpResponseMessage> respond,
Action<StubHandler>? capture = null)
{
var handler = new StubHandler(respond);
capture?.Invoke(handler);
return new HttpClientTransport("http://localhost:8082/", new HttpClient(handler));
}
private static HttpResponseMessage Error(HttpStatusCode statusCode, string json)
{
return new HttpResponseMessage(statusCode)
{
Content = new StringContent(json, Encoding.UTF8, "application/json")
};
}
private sealed class StubHandler : HttpMessageHandler
{
private readonly Func<HttpRequestMessage, HttpResponseMessage> _respond;
public StubHandler(Func<HttpRequestMessage, HttpResponseMessage> respond) => _respond = respond;
public HttpRequestMessage? LastRequest { get; private set; }
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
LastRequest = request;
return Task.FromResult(_respond(request));
}
}
}