144 lines
5.5 KiB
C#
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));
|
|
}
|
|
}
|
|
}
|