Skip to content

Commit

Permalink
chore: add httpclient wrapper and extend tests
Browse files Browse the repository at this point in the history
  • Loading branch information
DariuszGarbarz committed Aug 12, 2024
1 parent c7090ea commit 5d68c8d
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 66 deletions.
47 changes: 47 additions & 0 deletions BleBoxNetSdk.Tests/ResponseRequestTestCases.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using BleBoxNetSdk.AirSensor.Endpoints;
using BleBoxNetSdk.AirSensor.Enums;
using BleBoxNetSdk.Common.Endpoints;
using BleBoxNetSdk.Common.Enums;

namespace BleBoxNetSdk.Tests;

public class ResponseRequestTestCases
{
internal static IEnumerable<TestCaseData> GetResponses()
{
//Common
yield return new TestCaseData(new Info.ResponseResult(), Samples.InfoResponse);
yield return new TestCaseData(new DeviceUptime.ResponseResult(), Samples.UptimeResponse);
yield return new TestCaseData(new PerformUpdate.ResponseResult(), Samples.PerformUpdateResponse);
yield return new TestCaseData(new NetworkInformation.ResponseResult(), Samples.NetworkInformationResponse);
yield return new TestCaseData(new SetNetwork.ResponseResult(), Samples.SetAPResponse);
yield return new TestCaseData(new WiFiScan.ResponseResult(), Samples.PerformWiFiScanResponse);
yield return new TestCaseData(new ConnectWiFi.ResponseResult(), Samples.PerformWiFiConnectResponse);
yield return new TestCaseData(new DisconnectWiFi.ResponseResult(), Samples.PerformWiFiDisconnectResponse);

//AirSensor
yield return new TestCaseData(new DeviceState.ResponseResult(), Samples.AirSensorDeviceStateResponse);
yield return new TestCaseData(new ExtendedDeviceState.ResponseResult(), Samples.AirSensorExtendedResponse);
yield return new TestCaseData(new SensorRuntime.ResponseResult(), Samples.AirSensorRuntimeResponse);
yield return new TestCaseData(new ForceMeasurement.ResponseResult(), Samples.AirSensorMeasurementResponse);
yield return new TestCaseData(new SettingsState.ResponseResult(), Samples.AirSensorSettingsResponse);
yield return new TestCaseData(new SettingsSet.ResponseResult(), Samples.AirSensorSetSettingsResponse);
}

internal static IEnumerable<TestCaseData> GetRequests()
{
//Common
yield return new TestCaseData(new SetNetwork.Request(true, "shutterBox-g650e32d2217", "my_secret_password"), Samples.SetAPRequest);
yield return new TestCaseData(new ConnectWiFi.Request("WiFi_Name", "my_secret_password"), Samples.PerformWiFiConnectRequest);

//AirSensor
yield return new TestCaseData(new SettingsSet.Request(
"My BleBox device name",
Toggle.Enabled,
Toggle.Enabled,
Toggle.Enabled,
Geolocation.Accurate,
Mounting.Outside,
Toggle.Enabled), Samples.AirSensorSetSettingsRequest);
}
}
52 changes: 52 additions & 0 deletions BleBoxNetSdk.Tests/Services/ApiHttpClientTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using BleBoxNetSdk.Common.Endpoints;
using BleBoxNetSdk.Services;
using BleBoxNetSdk.Wrappers;
using FluentAssertions;
using Microsoft.Extensions.Logging;
using Moq;

namespace BleBoxNetSdk.Tests.Services;

internal class ApiHttpClientTests : ResponseRequestTestCases
{
[TestCaseSource(nameof(GetResponses))]
public async Task should_send_request<TResponse>(TResponse _, string json)
{
var uri = new Uri("http://127.0.0.1/");
var request = new PerformUpdate.Request();
var httpClientMock = new Mock<IHttpClient>();
var apiHttpClient = PrepareApiHttpClient(httpClientMock.Object);
var responseMessage = new HttpResponseMessage(System.Net.HttpStatusCode.OK) { Content = new StringContent(json) };
httpClientMock.Setup(h => h.SendAsync(It.IsAny<HttpRequestMessage>(), CancellationToken.None)).Returns(Task.FromResult(responseMessage));

var result = await apiHttpClient.Send<TResponse>(uri, request, CancellationToken.None);

result.Should().NotBeNull();
}

[Test]
public void should_throw_after_failed_trysends()
{
var uri = new Uri("http://127.0.0.1/");
var request = new PerformUpdate.Request();
var httpClientMock = new Mock<IHttpClient>();
var apiHttpClient = PrepareApiHttpClient(httpClientMock.Object);
var responseMessage = new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);
var totalCalls = 3;
httpClientMock.Setup(h => h.SendAsync(It.IsAny<HttpRequestMessage>(), CancellationToken.None)).Returns(Task.FromResult(responseMessage));

Assert.ThrowsAsync<Exception>( async () => await apiHttpClient.Send<PerformUpdate.ResponseResult>(uri, request, CancellationToken.None));

httpClientMock.Verify(m => m.SendAsync(It.IsAny<HttpRequestMessage>(), CancellationToken.None), Times.Exactly(totalCalls));
}

private ApiHttpClient PrepareApiHttpClient(IHttpClient? httpClient = null)
{
var apiHttpClient = new ApiHttpClient(
httpClient ?? Mock.Of<IHttpClient>(),
Mock.Of<ILogger<ApiHttpClient>>(),
new Serializer());

return apiHttpClient;
}
}
46 changes: 2 additions & 44 deletions BleBoxNetSdk.Tests/Services/SerializerTests.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
using BleBoxNetSdk.AirSensor.Endpoints;
using BleBoxNetSdk.AirSensor.Enums;
using BleBoxNetSdk.Common.Endpoints;
using BleBoxNetSdk.Common.Enums;
using BleBoxNetSdk.Services;
using BleBoxNetSdk.Services;
using FluentAssertions;

namespace BleBoxNetSdk.Tests.Services;

internal class SerializerTests
internal class SerializerTests : ResponseRequestTestCases
{
[TestCaseSource(nameof(GetResponses))]
public void should_deserialize_and_serialize_info<TResponse>(TResponse _, string json)
Expand All @@ -30,42 +26,4 @@ public void should_serialize_requests(object request, string json)

serializedString.Should().Be(json);
}

private static IEnumerable<TestCaseData> GetResponses()
{
//Common
yield return new TestCaseData(new Info.ResponseResult(), Samples.InfoResponse);
yield return new TestCaseData(new DeviceUptime.ResponseResult(), Samples.UptimeResponse);
yield return new TestCaseData(new PerformUpdate.ResponseResult(), Samples.PerformUpdateResponse);
yield return new TestCaseData(new NetworkInformation.ResponseResult(), Samples.NetworkInformationResponse);
yield return new TestCaseData(new SetNetwork.ResponseResult(), Samples.SetAPResponse);
yield return new TestCaseData(new WiFiScan.ResponseResult(), Samples.PerformWiFiScanResponse);
yield return new TestCaseData(new ConnectWiFi.ResponseResult(), Samples.PerformWiFiConnectResponse);
yield return new TestCaseData(new DisconnectWiFi.ResponseResult(), Samples.PerformWiFiDisconnectResponse);

//AirSensor
yield return new TestCaseData(new DeviceState.ResponseResult(), Samples.AirSensorDeviceStateResponse);
yield return new TestCaseData(new ExtendedDeviceState.ResponseResult(), Samples.AirSensorExtendedResponse);
yield return new TestCaseData(new SensorRuntime.ResponseResult(), Samples.AirSensorRuntimeResponse);
yield return new TestCaseData(new ForceMeasurement.ResponseResult(), Samples.AirSensorMeasurementResponse);
yield return new TestCaseData(new SettingsState.ResponseResult(), Samples.AirSensorSettingsResponse);
yield return new TestCaseData(new SettingsSet.ResponseResult(), Samples.AirSensorSetSettingsResponse);
}

private static IEnumerable<TestCaseData> GetRequests()
{
//Common
yield return new TestCaseData(new SetNetwork.Request(true, "shutterBox-g650e32d2217", "my_secret_password"), Samples.SetAPRequest);
yield return new TestCaseData(new ConnectWiFi.Request("WiFi_Name", "my_secret_password"), Samples.PerformWiFiConnectRequest);

//AirSensor
yield return new TestCaseData(new SettingsSet.Request(
"My BleBox device name",
Toggle.Enabled,
Toggle.Enabled,
Toggle.Enabled,
Geolocation.Accurate,
Mounting.Outside,
Toggle.Enabled), Samples.AirSensorSetSettingsRequest);
}
}
2 changes: 2 additions & 0 deletions BleBoxNetSdk/ServiceExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using BleBoxNetSdk.AirSensor;
using BleBoxNetSdk.Common;
using BleBoxNetSdk.Services;
using BleBoxNetSdk.Wrappers;
using Microsoft.Extensions.DependencyInjection;

namespace BleBoxNetSdk;
Expand All @@ -11,6 +12,7 @@ public static IServiceCollection RegisterSdkServices(this IServiceCollection ser
{
services
.AddSingleton<ISerializer, Serializer>()
.AddSingleton<IHttpClient, BleHttpClient>()
.AddSingleton<IApiHttpClient, ApiHttpClient>()
.AddSingleton<ICommonApiClient, CommonApiClient>()
.AddSingleton<IAirSensorApiClient, AirSensorApiClient>();
Expand Down
33 changes: 11 additions & 22 deletions BleBoxNetSdk/Services/ApiHttpClient.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using BleBoxNetSdk.Common;
using BleBoxNetSdk.Wrappers;
using Microsoft.Extensions.Logging;
using System.Net;
using System.Text;
Expand All @@ -11,25 +12,13 @@ public interface IApiHttpClient
Task<TResult> Send<TResult>(Uri baseUri, IRequest requestObject, CancellationToken cancellationToken);
}

public class ApiHttpClient : IApiHttpClient
public class ApiHttpClient(IHttpClient httpClient, ILogger<ApiHttpClient> logger, ISerializer serializer) : IApiHttpClient
{
private const int MaxResendCount = 3;
private const string JsonContentType = "application/json";

private readonly HttpClient _httpClient;
private readonly ILogger<ApiHttpClient> _logger;
private readonly ISerializer _serializer;
private readonly Encoding _encoding = Encoding.UTF8;

public ApiHttpClient(
ILogger<ApiHttpClient> logger,
ISerializer serializer)
{
_httpClient = new HttpClient();
_logger = logger;
_serializer = serializer;
}

public async Task<TResult> Send<TResult>(
Uri baseUri,
IRequest requestObject,
Expand All @@ -38,9 +27,9 @@ public async Task<TResult> Send<TResult>(
var response = await TrySend(baseUri, requestObject, cancellationToken);

var serializedResult = await response.Content.ReadAsStringAsync(cancellationToken);
_logger.LogDebug("<- Received response: {response}", serializedResult);
logger.LogDebug("<- Received response: {response}", serializedResult);

var deserializedResult = _serializer.DeserializeJson<TResult>(serializedResult)
var deserializedResult = serializer.DeserializeJson<TResult>(serializedResult)
?? throw new JsonException($"Could not deserialize {serializedResult} to {typeof(TResult)}");

return deserializedResult;
Expand All @@ -57,20 +46,20 @@ private async Task<HttpResponseMessage> TrySend(
{
var request = PrepareRequest(baseUri, requestObject);

var response = await _httpClient.SendAsync(request, cancellationToken);
var response = await httpClient.SendAsync(request, cancellationToken);

if (response.StatusCode == HttpStatusCode.Conflict)
_logger.LogWarning("Requested action is already in progress");
logger.LogWarning("Requested action is already in progress");

response.EnsureSuccessStatusCode();

return response;
}
catch (Exception ex) when (count < MaxResendCount)
catch (Exception ex) when (count <= MaxResendCount)
{
await Task.Delay(TimeSpan.FromSeconds(3), cancellationToken);

_logger.LogWarning("Request to API failed with exception: {ex}. Resending request {count}", ex, count);
logger.LogWarning("Request to API failed with exception: {ex}. Resending request {count}", ex, count);
}
}

Expand All @@ -82,13 +71,13 @@ private HttpRequestMessage PrepareRequest(
IRequest requestObject)
{
var request = new HttpRequestMessage(requestObject.HttpMethod, BuildUri(baseUri, requestObject.Uri));
_logger.LogDebug("-> Sending http request to: {uri}", request.RequestUri?.ToString());
logger.LogDebug("-> Sending http request to: {uri}", request.RequestUri?.ToString());

if (requestObject.HaveContent)
{
var serializedContent = _serializer.SerializeJson(requestObject);
var serializedContent = serializer.SerializeJson(requestObject);

_logger.LogDebug("-> With body: {body}", serializedContent);
logger.LogDebug("-> With body: {body}", serializedContent);
request.Content = new StringContent(serializedContent, _encoding, JsonContentType);
}

Expand Down
19 changes: 19 additions & 0 deletions BleBoxNetSdk/Wrappers/BleHttpClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
namespace BleBoxNetSdk.Wrappers;

public interface IHttpClient
{
Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken token = default);
}

public class BleHttpClient : IHttpClient
{
private readonly HttpClient _httpClient;

public BleHttpClient()
{
_httpClient = new HttpClient();
}

public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken token = default)
=> _httpClient.SendAsync(request, token);
}

0 comments on commit 5d68c8d

Please sign in to comment.