Skip to content

Commit

Permalink
refactor: test refactoring (#469)
Browse files Browse the repository at this point in the history
* Individual tests for VonageHttpClient

* Add individual tests for SendAsync and SendWithResponseAsync

* Finalize tests for VonageHttpClients

* Remove use case tests for VerifyV2

* Remove use case test for CreateSubAccount

* Simplify tests in SubAccounts

* Update E2E Tests for SubAccounts
  • Loading branch information
Tr00d authored Jul 29, 2023
1 parent edcd78c commit f2e13a2
Show file tree
Hide file tree
Showing 31 changed files with 515 additions and 971 deletions.
271 changes: 271 additions & 0 deletions Vonage.Common.Test/Client/VonageHttpClientTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,271 @@
using System.Net;
using System.Net.Http.Headers;
using AutoFixture;
using FsCheck;
using FsCheck.Xunit;
using Vonage.Common.Client;
using Vonage.Common.Failures;
using Vonage.Common.Monads;
using Vonage.Common.Test.Extensions;
using Vonage.Common.Test.TestHelpers;

namespace Vonage.Common.Test.Client;

public class VonageHttpClientTest
{
private readonly Fixture fixture;

private readonly JsonSerializer serializer;
private readonly Result<FakeRequest> request;

public VonageHttpClientTest()
{
this.serializer = new JsonSerializer();
this.fixture = new Fixture();
this.fixture.Customize(new SupportMutableValueTypesCustomization());
this.request = BuildRequest();
}

[Property]
public Property SendAsync_VerifyReturnsFailureGivenApiResponseIsError() =>
this.VerifyReturnsFailureGivenApiResponseIsError(BuildExpectedRequest(),
configuration => new VonageHttpClient(configuration, this.serializer).SendAsync(this.request));

[Property]
public Property SendAsync_VerifyReturnsFailureGivenErrorCannotBeParsed() =>
this.VerifyReturnsFailureGivenErrorCannotBeParsed(BuildExpectedRequest(),
configuration => new VonageHttpClient(configuration, this.serializer).SendAsync(this.request));

[Fact]
public async Task SendAsync_VerifyReturnsFailureGivenRequestIsFailure() =>
await this.VerifyReturnsFailureGivenRequestIsFailure((configuration, failureRequest) =>
new VonageHttpClient(configuration, this.serializer).SendAsync(failureRequest));

[Fact]
public async Task SendAsync_VerifyReturnsFailureGivenTokenGenerationFails() =>
await this.VerifyReturnsFailureGivenTokenGenerationFails(configuration =>
new VonageHttpClient(configuration, this.serializer).SendAsync(this.request));

[Fact]
public async Task SendAsync_VerifyReturnsUnitGivenApiResponseIsSuccess() =>
await this.VerifyReturnsExpectedValueGivenApiResponseIsSuccess(BuildExpectedRequest(),
configuration => new VonageHttpClient(configuration, this.serializer).SendAsync(this.request));

[Property]
public Property SendWithoutHeaderAsync_VerifyReturnsFailureGivenApiResponseIsError() =>
this.VerifyReturnsFailureGivenApiResponseIsError(BuildExpectedRequest(),
configuration => new VonageHttpClient(configuration, this.serializer).SendAsync(this.request));

[Property]
public Property SendWithoutHeaderAsync_VerifyReturnsFailureGivenErrorCannotBeParsed() =>
this.VerifyReturnsFailureGivenErrorCannotBeParsed(BuildExpectedRequest(),
configuration =>
new VonageHttpClient(configuration, this.serializer).SendWithoutHeadersAsync(this.request));

[Fact]
public async Task SendWithoutHeaderAsync_VerifyReturnsFailureGivenRequestIsFailure() =>
await this.VerifyReturnsFailureGivenRequestIsFailure((configuration, failureRequest) =>
new VonageHttpClient(configuration, this.serializer).SendWithoutHeadersAsync(failureRequest));

[Fact]
public async Task SendWithoutHeaderAsync_VerifyReturnsUnitGivenApiResponseIsSuccess() =>
await this.VerifyReturnsExpectedValueGivenApiResponseIsSuccess(BuildExpectedRequest(),
configuration =>
new VonageHttpClient(configuration, this.serializer).SendWithoutHeadersAsync(this.request));

[Fact]
public async Task SendWithRawResponseAsync_VerifyReturnsExpectedValueGivenApiResponseIsSuccess() =>
await this.VerifyReturnsRawContentGivenApiResponseIsSuccess(BuildExpectedRequest(),
configuration =>
new VonageHttpClient(configuration, this.serializer).SendWithRawResponseAsync(this.request));

[Property]
public Property SendWithRawResponseAsync_VerifyReturnsFailureGivenApiResponseIsError() =>
this.VerifyReturnsFailureGivenApiResponseIsError(BuildExpectedRequest(),
configuration =>
new VonageHttpClient(configuration, this.serializer).SendWithRawResponseAsync(this.request));

[Property]
public Property SendWithRawResponseAsync_VerifyReturnsFailureGivenErrorCannotBeParsed() =>
this.VerifyReturnsFailureGivenErrorCannotBeParsed(BuildExpectedRequest(),
configuration =>
new VonageHttpClient(configuration, this.serializer).SendWithRawResponseAsync(this.request));

[Fact]
public async Task SendWithRawResponseAsync_VerifyReturnsFailureGivenRequestIsFailure() =>
await this.VerifyReturnsFailureGivenRequestIsFailure((configuration, failureRequest) =>
new VonageHttpClient(configuration, this.serializer).SendWithRawResponseAsync(failureRequest));

[Fact]
public async Task SendWithRawResponseAsync_VerifyReturnsFailureGivenTokenGenerationFails() =>
await this.VerifyReturnsFailureGivenTokenGenerationFails(configuration =>
new VonageHttpClient(configuration, this.serializer).SendWithRawResponseAsync(
this.request));

[Fact]
public async Task SendWithResponseAsync_VerifyReturnsExpectedValueGivenApiResponseIsSuccess() =>
await this.VerifyReturnsExpectedValueGivenApiResponseIsSuccess(BuildExpectedRequest(),
configuration =>
new VonageHttpClient(configuration, this.serializer).SendWithResponseAsync<FakeRequest, FakeResponse>(
this.request));

[Fact]
public async Task SendWithResponseAsync_VerifyReturnsFailureGivenApiResponseCannotBeParsed() =>
await this.VerifyReturnsFailureGivenApiResponseCannotBeParsed(BuildExpectedRequest(),
configuration =>
new VonageHttpClient(configuration, this.serializer).SendWithResponseAsync<FakeRequest, FakeResponse>(
this.request));

[Property]
public Property SendWithResponseAsync_VerifyReturnsFailureGivenApiResponseIsError() =>
this.VerifyReturnsFailureGivenApiResponseIsError(BuildExpectedRequest(),
configuration =>
new VonageHttpClient(configuration, this.serializer).SendWithResponseAsync<FakeRequest, FakeResponse>(
this.request));

[Property]
public Property SendWithResponseAsync_VerifyReturnsFailureGivenErrorCannotBeParsed() =>
this.VerifyReturnsFailureGivenErrorCannotBeParsed(BuildExpectedRequest(),
configuration =>
new VonageHttpClient(configuration, this.serializer).SendWithResponseAsync<FakeRequest, FakeResponse>(
this.request));

[Fact]
public async Task SendWithResponseAsync_VerifyReturnsFailureGivenRequestIsFailure() =>
await this.VerifyReturnsFailureGivenRequestIsFailure((configuration, failureRequest) =>
new VonageHttpClient(configuration, this.serializer).SendWithResponseAsync<FakeRequest, FakeResponse>(
failureRequest));

[Fact]
public async Task SendWithResponseAsync_VerifyReturnsFailureGivenTokenGenerationFails() =>
await this.VerifyReturnsFailureGivenTokenGenerationFails(configuration =>
new VonageHttpClient(configuration, this.serializer).SendWithResponseAsync<FakeRequest, FakeResponse>(
this.request));

private static ExpectedRequest BuildExpectedRequest() =>
new()
{
Method = HttpMethod.Post,
RequestUri = new Uri("/my-fake-api/yolo", UriKind.Relative),
Content = "{\"id\":\"foo bar\",\"name\":\"My fake request\"}",
};

private static Result<FakeRequest> BuildRequest() =>
new FakeRequest {Id = Guid.Parse("ceb2b201-2143-48f5-8890-c58369394eba"), Name = "My fake request"};

private VonageHttpClientConfiguration CreateConfiguration(FakeHttpRequestHandler handler) =>
new(handler.ToHttpClient(), new AuthenticationHeaderValue("Anonymous"), this.fixture.Create<string>());

private async Task VerifyReturnsExpectedValueGivenApiResponseIsSuccess<TResponse>(ExpectedRequest expected,
Func<VonageHttpClientConfiguration, Task<Result<TResponse>>> operation)
{
var expectedResponse = this.fixture.Create<TResponse>();
var messageHandler = FakeHttpRequestHandler
.Build(HttpStatusCode.OK)
.WithExpectedRequest(expected)
.WithResponseContent(this.serializer.SerializeObject(expectedResponse));
var result = await operation(this.CreateConfiguration(messageHandler));
result.Should().BeSuccess(expectedResponse);
}

private async Task VerifyReturnsFailureGivenApiResponseCannotBeParsed<TResponse>(ExpectedRequest expected,
Func<VonageHttpClientConfiguration, Task<Result<TResponse>>> operation)
{
var body = this.fixture.Create<string>();
var messageHandler = FakeHttpRequestHandler
.Build(HttpStatusCode.OK)
.WithExpectedRequest(expected)
.WithResponseContent(body);
var result = await operation(this.CreateConfiguration(messageHandler));
result.Should().BeFailure(DeserializationFailure.From(typeof(TResponse), body));
}

private Property VerifyReturnsFailureGivenApiResponseIsError<TResponse>(
ExpectedRequest expected,
Func<VonageHttpClientConfiguration, Task<Result<TResponse>>> operation) =>
Prop.ForAll(
FsCheckExtensions.GetErrorResponses(),
error =>
{
var expectedContent = string.Empty;
var messageHandler = FakeHttpRequestHandler
.Build(error.Code)
.WithExpectedRequest(expected);
if (error.Message != null)
{
expectedContent = this.serializer.SerializeObject(error);
messageHandler = messageHandler.WithResponseContent(expectedContent);
}
operation(this.CreateConfiguration(messageHandler)).Result.Should()
.BeFailure(HttpFailure.From(error.Code, error.Message ?? string.Empty, expectedContent));
});

private Property VerifyReturnsFailureGivenErrorCannotBeParsed<TResponse>(
ExpectedRequest expected,
Func<VonageHttpClientConfiguration, Task<Result<TResponse>>> operation) =>
Prop.ForAll(
FsCheckExtensions.GetInvalidStatusCodes(),
FsCheckExtensions.GetNonDeserializableStrings(),
(statusCode, jsonError) =>
{
var messageHandler = FakeHttpRequestHandler.Build(statusCode)
.WithExpectedRequest(expected)
.WithResponseContent(jsonError);
operation(this.CreateConfiguration(messageHandler))
.Result
.Should()
.BeFailure(HttpFailure.From(statusCode,
DeserializationFailure.From(typeof(ErrorResponse), jsonError).GetFailureMessage(),
jsonError));
});

private async Task VerifyReturnsFailureGivenRequestIsFailure<TRes>(
Func<VonageHttpClientConfiguration, Result<FakeRequest>, Task<Result<TRes>>> operation)
{
var messageHandler = FakeHttpRequestHandler.Build(HttpStatusCode.OK);
var expectedFailure = ResultFailure.FromErrorMessage(this.fixture.Create<string>());
var result = await operation(this.CreateConfiguration(messageHandler),
Result<FakeRequest>.FromFailure(expectedFailure));
result.Should().BeFailure(expectedFailure);
}

private async Task VerifyReturnsFailureGivenTokenGenerationFails<TResponse>(
Func<VonageHttpClientConfiguration, Task<Result<TResponse>>> operation)
{
var configuration = new VonageHttpClientConfiguration(
FakeHttpRequestHandler.Build(HttpStatusCode.OK).ToHttpClient(),
new AuthenticationFailure().ToResult<AuthenticationHeaderValue>(),
this.fixture.Create<string>());
var result = await operation(configuration);
result.Should().BeFailure(new AuthenticationFailure());
}

private async Task VerifyReturnsRawContentGivenApiResponseIsSuccess(ExpectedRequest expected,
Func<VonageHttpClientConfiguration, Task<Result<string>>> operation)
{
var expectedResponse = this.fixture.Create<string>();
var messageHandler = FakeHttpRequestHandler
.Build(HttpStatusCode.OK)
.WithExpectedRequest(expected)
.WithResponseContent(expectedResponse);
var result = await operation(this.CreateConfiguration(messageHandler));
result.Should().BeSuccess(expectedResponse);
}

private struct FakeRequest : IVonageRequest
{
public Guid Id { get; set; }

public string Name { get; set; }

public HttpRequestMessage BuildRequestMessage() => VonageRequestBuilder
.Initialize(HttpMethod.Post, this.GetEndpointPath())
.WithContent(new StringContent("{\"id\":\"foo bar\",\"name\":\"My fake request\"}"))
.Build();

public string GetEndpointPath() => "/my-fake-api/yolo";
}

private record FakeResponse;
}
34 changes: 27 additions & 7 deletions Vonage.Test.Unit/SubAccounts/CreateSubAccount/E2ETest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,33 @@ public async Task CreateSubAccount()
.UsingPost())
.RespondWith(Response.Create().WithStatusCode(HttpStatusCode.OK)
.WithBody(this.Serialization.GetResponseJson(nameof(SerializationTest.ShouldDeserialize200))));
var result = await this.Helper.VonageClient.SubAccountsClient.CreateSubAccountAsync(CreateSubAccountRequest
.Build()
.WithName("My SubAccount")
.WithSecret("123456789AbcDef")
.DisableSharedAccountBalance()
.Create());
result.Should().BeSuccess();
await this.Helper.VonageClient.SubAccountsClient.CreateSubAccountAsync(CreateSubAccountRequest
.Build()
.WithName("My SubAccount")
.WithSecret("123456789AbcDef")
.DisableSharedAccountBalance()
.Create())
.Should()
.BeSuccessAsync(SerializationTest.GetExpectedAccount());
}

[Fact]
public async Task CreateSubAccountWithDefaultValues()
{
this.Helper.Server.Given(WireMock.RequestBuilders.Request.Create()
.WithPath("/accounts/790fc5e5/subaccounts")
.WithHeader("Authorization", "Basic NzkwZmM1ZTU6QWEzNDU2Nzg5")
.WithBody(this.Serialization.GetRequestJson(nameof(SerializationTest
.ShouldSerializeWithDefaultValues)))
.UsingPost())
.RespondWith(Response.Create().WithStatusCode(HttpStatusCode.OK)
.WithBody(this.Serialization.GetResponseJson(nameof(SerializationTest.ShouldDeserialize200))));
await this.Helper.VonageClient.SubAccountsClient.CreateSubAccountAsync(CreateSubAccountRequest
.Build()
.WithName("My SubAccount")
.Create())
.Should()
.BeSuccessAsync(SerializationTest.GetExpectedAccount());
}
}
}
23 changes: 13 additions & 10 deletions Vonage.Test.Unit/SubAccounts/CreateSubAccount/SerializationTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,24 @@ public SerializationTest() =>
typeof(SerializationTest).Namespace,
JsonSerializer.BuildWithSnakeCase());

public static Account GetExpectedAccount() =>
new Account(
"aze1243v",
"SubAccount department A",
"bbe6222f",
false,
DateTimeOffset.Parse("2018-03-02T17:34:49Z"),
true,
(decimal) 1.25,
15
);

[Fact]
public void ShouldDeserialize200() =>
this.helper.Serializer
.DeserializeObject<Account>(this.helper.GetResponseJson())
.Should()
.BeSuccess(new Account(
"aze1243v",
"SubAccount department A",
"bbe6222f",
false,
DateTimeOffset.Parse("2018-03-02T17:34:49Z"),
true,
(decimal) 1.25,
15
));
.BeSuccess(GetExpectedAccount());

[Fact]
public void ShouldSerialize() =>
Expand Down
Loading

0 comments on commit f2e13a2

Please sign in to comment.