Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 30 additions & 11 deletions src/CheckoutSdk/ApiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -313,10 +313,18 @@ private async Task<TResult> DeserializeResponseAsync<TResult>(HttpResponseMessag

private async Task<dynamic> DeserializeResponseAsync(HttpResponseMessage httpResponse, Type resultType)
{
dynamic deserializedObject;
if (httpResponse.StatusCode == HttpStatusCode.NoContent || httpResponse.Content.Headers.ContentLength == 0)
dynamic deserializedObject = null;

if (httpResponse.StatusCode == HttpStatusCode.NoContent ||
httpResponse.Content == null ||
(httpResponse.Content.Headers.ContentLength.HasValue && httpResponse.Content.Headers.ContentLength.Value == 0))
{
deserializedObject = Activator.CreateInstance(resultType);

if (deserializedObject == null)
{
deserializedObject = new DefaultHttpMetadata();
}
}
else if (resultType == typeof(ContentsResponse))
{
Expand All @@ -327,23 +335,34 @@ private async Task<dynamic> DeserializeResponseAsync(HttpResponseMessage httpRes
var json = await httpResponse.Content.ReadAsStringAsync();
deserializedObject = _serializer.Deserialize(json, resultType);
}

await SetHttpMetadata(httpResponse, deserializedObject);

if (deserializedObject is HttpMetadata metadata)
{
metadata.Body = metadata.Body ?? string.Empty;
metadata.HttpStatusCode = metadata.HttpStatusCode == null ? 0 : (int)httpResponse.StatusCode;
metadata.ResponseHeaders = metadata.ResponseHeaders ?? new Dictionary<string, string>();
}

if (deserializedObject != null)
{
await SetHttpMetadata(httpResponse, deserializedObject);
}

return deserializedObject;
}


private static async Task SetHttpMetadata(HttpResponseMessage httpResponse, dynamic deserializedObject)
{
((HttpMetadata)deserializedObject).Body = await httpResponse.Content.ReadAsStringAsync();
((HttpMetadata)deserializedObject).HttpStatusCode = (int)httpResponse.StatusCode;
IDictionary<string, string> headers = new Dictionary<string, string>();
foreach (var header in httpResponse.Headers)
if (deserializedObject is HttpMetadata metadata)
{
headers.Add(header.Key, header.Value.First());
metadata.Body = await (httpResponse.Content?.ReadAsStringAsync() ?? Task.FromResult(string.Empty));
metadata.HttpStatusCode = (int)httpResponse.StatusCode;
metadata.ResponseHeaders = httpResponse.Headers?.ToDictionary(
h => h.Key,
h => h.Value.FirstOrDefault() ?? string.Empty
) ?? new Dictionary<string, string>();
}

((HttpMetadata)deserializedObject).ResponseHeaders = headers;
}
}
}
10 changes: 10 additions & 0 deletions src/CheckoutSdk/HttpMetadata.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,14 @@ public class HttpMetadata

public IDictionary<string, string> ResponseHeaders { get; set; }
}

public class DefaultHttpMetadata : HttpMetadata
{
public DefaultHttpMetadata()
{
Body = string.Empty;
HttpStatusCode = 0;
ResponseHeaders = new Dictionary<string, string>();
}
}
}
147 changes: 147 additions & 0 deletions test/CheckoutSdkTest/ApiClientTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
using System;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Moq;
using Moq.Protected;
using Xunit;

namespace Checkout
{
public class ApiClientTests : UnitTestFixture, IDisposable
{
private readonly ApiClient _apiClient;
private readonly Mock<HttpMessageHandler> _httpMessageHandlerMock;
private readonly HttpClient _httpClient;

public ApiClientTests()
{
_httpMessageHandlerMock = new Mock<HttpMessageHandler>();
_httpClient = new HttpClient(_httpMessageHandlerMock.Object);

var httpClientFactoryMock = new Mock<IHttpClientFactory>();
httpClientFactoryMock
.Setup(factory => factory.CreateClient())
.Returns(_httpClient);

_apiClient = new ApiClient(httpClientFactoryMock.Object, new Uri("https://api.example.com"), false);
}

[Fact]
public async Task ShouldGetReturnDeserializedObject()
{
// Arrange
var jsonResponse = "{\"message\":\"success\"}";
using var httpResponse = new HttpResponseMessage(HttpStatusCode.OK);
httpResponse.Content = new StringContent(jsonResponse, Encoding.UTF8, "application/json");

_httpMessageHandlerMock
.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(httpResponse);

var authorization = new SdkAuthorization(PlatformType.Default, ValidDefaultSk);

// Act
var result = await _apiClient.Get<FakeResponse>("/test", authorization);

// Assert
Assert.NotNull(result);
Assert.IsType<FakeResponse>(result);
Assert.Equal("success", result.Message);
}

[Fact]
public async Task ShouldPostReturnDeserializedObject()
{
// Arrange
var jsonResponse = "{\"message\":\"created\"}";
using HttpResponseMessage httpResponse = new HttpResponseMessage(HttpStatusCode.Created)
{
Content = new StringContent(jsonResponse, Encoding.UTF8, "application/json")
};

_httpMessageHandlerMock
.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(httpResponse);

var authorization = new SdkAuthorization(PlatformType.Default, ValidDefaultSk);
var requestData = new { name = "new item" };

// Act
var result = await _apiClient.Post<FakeResponse>("/test", authorization, requestData);

// Assert
Assert.NotNull(result);
Assert.IsType<FakeResponse>(result);
Assert.Equal("created", result.Message);
}

[Fact]
public async Task ShouldDeleteReturnDeserializedObject()
{
// Arrange
var jsonResponse = "{\"message\":\"deleted\"}";
using HttpResponseMessage httpResponse = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(jsonResponse, Encoding.UTF8, "application/json")
};

_httpMessageHandlerMock
.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(httpResponse);

var authorization = new SdkAuthorization(PlatformType.Default, ValidDefaultSk);

// Act
var result = await _apiClient.Delete<FakeResponse>("/test", authorization);

// Assert
Assert.NotNull(result);
Assert.IsType<FakeResponse>(result);
Assert.Equal("deleted", result.Message);
}

[Fact]
public async Task ShouldGetHandleNoContentResponse()
{
// Arrange
using var httpResponse = new HttpResponseMessage(HttpStatusCode.NoContent);
httpResponse.Content = null;

_httpMessageHandlerMock
.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(httpResponse);

var authorization = new SdkAuthorization(PlatformType.Default, ValidDefaultSk);

// Act
var result = await _apiClient.Get<HttpMetadata>("/test", authorization);

// Assert
Assert.NotNull(result);
Assert.IsType<HttpMetadata>(result);
Assert.Equal(204, result.HttpStatusCode);
Assert.NotNull(result.Body);
Assert.NotNull(result.ResponseHeaders);
}

public void Dispose()
{
_httpClient.Dispose();
}
}

public class FakeResponse : HttpMetadata
{
public string Message { get; set; }
}
}
12 changes: 6 additions & 6 deletions test/CheckoutSdkTest/UnitTestFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ namespace Checkout
public abstract class UnitTestFixture
{
// Previous
protected const string ValidPreviousSk = "sk_test_fde517a8-3f01-41ef-b4bd-4282384b0a64";
protected const string ValidPreviousPk = "pk_test_fe70ff27-7c32-4ce1-ae90-5691a188ee7b";
protected const string InvalidPreviousSk = "sk_test_asdsad3q4dq";
protected static readonly string ValidPreviousPk = System.Environment.GetEnvironmentVariable("CHECKOUT_PREVIOUS_PUBLIC_KEY");
protected static readonly string ValidPreviousSk = System.Environment.GetEnvironmentVariable("CHECKOUT_PREVIOUS_SECRET_KEY");
protected const string InvalidPreviousPk = "pk_test_q414dasds";
protected const string InvalidPreviousSk = "sk_test_asdsad3q4dq";

// Default
protected const string ValidDefaultSk = "sk_sbox_m73dzbpy7cf3gfd46xr4yj5xo4e";
protected const string ValidDefaultPk = "pk_sbox_sderftvmkgf7hdnpwnbhw7r2uic";
protected const string InvalidDefaultSk = "sk_sbox_m73dzbpy7c-f3gfd46xr4yj5xo4e";
protected static readonly string ValidDefaultPk = System.Environment.GetEnvironmentVariable("CHECKOUT_DEFAULT_PUBLIC_KEY");
protected static readonly string ValidDefaultSk = System.Environment.GetEnvironmentVariable("CHECKOUT_DEFAULT_SECRET_KEY");
protected const string InvalidDefaultPk = "pk_sbox_pkh";
protected const string InvalidDefaultSk = "sk_sbox_m73dzbpy7c-f3gfd46xr4yj5xo4e";
}
}
Loading