-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
test(Profile): add integration tests for GetPersonalInformation endpo…
…int #38
- Loading branch information
1 parent
f761233
commit 7f11947
Showing
18 changed files
with
845 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
{ | ||
"sdk": { | ||
"version": "8.0.201", | ||
"version": "8.0.401", | ||
"rollForward": "major", | ||
"allowPrerelease": true | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
namespace Place.Api.Profile; | ||
|
||
public interface IAssemblyMarker { } |
75 changes: 75 additions & 0 deletions
75
tests/Place.Api.Integration.Tests/Apis/Extensions/AddressValidationExtensions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
using System; | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
using FluentAssertions; | ||
using FluentAssertions.Execution; | ||
using Place.Api.Profile.Apis.V1.Responses; | ||
using Place.Api.Profile.Infrastructure.Persistence.EF.Models; | ||
|
||
namespace Place.Api.Integration.Tests.Apis.Extensions; | ||
|
||
public static class AddressValidationExtensions | ||
{ | ||
public static Task ValidateAddress( | ||
this PersonalInformationResponse response, | ||
ProfileReadModel profile | ||
) | ||
{ | ||
using AssertionScope scope = new(); | ||
|
||
response.FormattedAddress.Should().NotBeNull("FormattedAddress should never be null"); | ||
|
||
if (IsCompleteAddress(profile)) | ||
{ | ||
ValidateCompleteAddress(response.FormattedAddress, profile); | ||
return Task.CompletedTask; | ||
} | ||
|
||
ValidatePartialAddress(response.FormattedAddress, profile); | ||
return Task.CompletedTask; | ||
} | ||
|
||
private static bool IsCompleteAddress(ProfileReadModel profile) => | ||
!string.IsNullOrEmpty(profile.Street) | ||
&& !string.IsNullOrEmpty(profile.City) | ||
&& !string.IsNullOrEmpty(profile.ZipCode) | ||
&& !string.IsNullOrEmpty(profile.Country); | ||
|
||
private static void ValidateCompleteAddress(string formattedAddress, ProfileReadModel profile) | ||
{ | ||
string expectedAddress = | ||
$"{profile.Street}, {profile.ZipCode} {profile.City}, {profile.Country}"; | ||
|
||
formattedAddress | ||
.Should() | ||
.Be(expectedAddress) | ||
.And.Contain(profile.Street!) | ||
.And.Contain(profile.ZipCode!) | ||
.And.Contain(profile.City!) | ||
.And.Contain(profile.Country!); | ||
|
||
ValidateAddressOrder(formattedAddress, profile); | ||
} | ||
|
||
private static void ValidateAddressOrder(string formattedAddress, ProfileReadModel profile) | ||
{ | ||
int streetIndex = formattedAddress.IndexOf(profile.Street!, StringComparison.Ordinal); | ||
int zipIndex = formattedAddress.IndexOf(profile.ZipCode!, StringComparison.Ordinal); | ||
int cityIndex = formattedAddress.IndexOf(profile.City!, StringComparison.Ordinal); | ||
int countryIndex = formattedAddress.IndexOf(profile.Country!, StringComparison.Ordinal); | ||
|
||
streetIndex.Should().BeLessThan(zipIndex, "Street should come before zip code"); | ||
zipIndex.Should().BeLessThan(countryIndex, "Zip code should come before country"); | ||
cityIndex.Should().BeLessThan(countryIndex, "City should come before country"); | ||
} | ||
|
||
private static void ValidatePartialAddress(string formattedAddress, ProfileReadModel profile) | ||
{ | ||
string?[] components = [profile.Street, profile.City, profile.ZipCode, profile.Country]; | ||
|
||
foreach (string? component in components.Where(c => !string.IsNullOrEmpty(c))) | ||
{ | ||
formattedAddress.Should().Contain(component!); | ||
} | ||
} | ||
} |
160 changes: 160 additions & 0 deletions
160
tests/Place.Api.Integration.Tests/Apis/Extensions/PersonalInformationAssertions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
using System.Net; | ||
using System.Net.Http; | ||
using System.Threading.Tasks; | ||
using FluentAssertions; | ||
using FluentAssertions.Execution; | ||
using Place.Api.Profile.Apis.V1.Responses; | ||
using Place.Api.Profile.Domain.Profile; | ||
using Place.Api.Profile.Infrastructure.Persistence.EF.Models; | ||
|
||
namespace Place.Api.Integration.Tests.Apis.Extensions; | ||
|
||
public static class PersonalInformationAssertions | ||
{ | ||
public static Task ShouldReturnValidProfileAsync( | ||
this (HttpResponseMessage Response, PersonalInformationResponse? Content) result, | ||
ProfileReadModel expectedProfile | ||
) | ||
{ | ||
using AssertionScope scope = new(); | ||
|
||
ValidateHttpResponse(result.Response); | ||
ValidateContent(result.Content, expectedProfile); | ||
ValidateContactInformation(result.Content!, expectedProfile); | ||
result.Content!.ValidateAddress(expectedProfile); | ||
|
||
return Task.CompletedTask; | ||
} | ||
|
||
public static Task ShouldReturnPartialAddressAsync( | ||
this (HttpResponseMessage Response, PersonalInformationResponse? Content) result, | ||
string? expectedCity, | ||
string? expectedCountry, | ||
string expectedFormattedAddress | ||
) | ||
{ | ||
using AssertionScope scope = new(); | ||
|
||
ValidateHttpResponse(result.Response); | ||
ValidatePartialAddress( | ||
result.Content!, | ||
expectedCity, | ||
expectedCountry, | ||
expectedFormattedAddress | ||
); | ||
|
||
return Task.CompletedTask; | ||
} | ||
|
||
private static void ValidateHttpResponse(HttpResponseMessage response) | ||
{ | ||
response.Should().NotBeNull(); | ||
response | ||
.StatusCode.Should() | ||
.Be(HttpStatusCode.OK, "a valid profile should return OK status"); | ||
response.Content.Headers.ContentType?.MediaType.Should().Be("application/json"); | ||
} | ||
|
||
private static void ValidateContent( | ||
PersonalInformationResponse? content, | ||
ProfileReadModel expected | ||
) | ||
{ | ||
content.Should().NotBeNull("response content should not be null for a valid profile"); | ||
|
||
content!.FirstName.Should().Be(expected.FirstName, "FirstName should match exactly"); | ||
content.LastName.Should().Be(expected.LastName, "LastName should match exactly"); | ||
content.Street.Should().Be(expected.Street, "Street should match exactly"); | ||
content.City.Should().Be(expected.City, "City should match exactly"); | ||
content.ZipCode.Should().Be(expected.ZipCode, "ZipCode should match exactly"); | ||
content.Country.Should().Be(expected.Country, "Country should match exactly"); | ||
|
||
ValidateNonEmptyFields(content, expected); | ||
} | ||
|
||
private static void ValidateContactInformation( | ||
PersonalInformationResponse content, | ||
ProfileReadModel expected | ||
) | ||
{ | ||
ValidateEmail(content.Email); | ||
ValidatePhoneNumber(content.PhoneNumber); | ||
ValidateGender(content.Gender, expected.Gender); | ||
} | ||
|
||
private static void ValidateNonEmptyFields( | ||
PersonalInformationResponse content, | ||
ProfileReadModel expected | ||
) | ||
{ | ||
if (expected.FirstName != null) | ||
content.FirstName.Should().NotBeEmpty("FirstName should not be empty when provided"); | ||
|
||
if (expected.LastName != null) | ||
content.LastName.Should().NotBeEmpty("LastName should not be empty when provided"); | ||
|
||
if (expected.Street != null) | ||
content.Street.Should().NotBeEmpty("Street should not be empty when provided"); | ||
} | ||
|
||
private static void ValidateEmail(string email) | ||
{ | ||
.Should() | ||
.NotBeNullOrWhiteSpace("Email is required") | ||
.And.Contain("@", "Email should be in valid format") | ||
.And.Contain(".", "Email should be in valid format"); | ||
} | ||
|
||
private static void ValidatePhoneNumber(string? phoneNumber) | ||
{ | ||
if (phoneNumber != null) | ||
{ | ||
phoneNumber | ||
.Should() | ||
.StartWith("+", "Phone number should start with country code") | ||
.And.HaveLength(12, "Phone number should be in international format"); | ||
} | ||
} | ||
|
||
private static void ValidateGender(string? actual, Gender? expected) | ||
{ | ||
if (expected.HasValue) | ||
{ | ||
actual.Should().NotBeNull().And.Be(expected.ToString(), "Gender should match exactly"); | ||
} | ||
else | ||
{ | ||
actual.Should().BeNull("Gender should be null when not provided"); | ||
} | ||
} | ||
|
||
private static void ValidatePartialAddress( | ||
PersonalInformationResponse content, | ||
string? expectedCity, | ||
string? expectedCountry, | ||
string expectedFormattedAddress | ||
) | ||
{ | ||
content.City.Should().Be(expectedCity); | ||
content.Country.Should().Be(expectedCountry); | ||
content.FormattedAddress.Should().Be(expectedFormattedAddress); | ||
|
||
if (expectedCity == null) | ||
content.City.Should().BeNull("City should be null when not provided"); | ||
|
||
if (expectedCountry == null) | ||
content.Country.Should().BeNull("Country should be null when not provided"); | ||
|
||
content.FormattedAddress.Should().NotBeNull("FormattedAddress should never be null"); | ||
|
||
if (string.IsNullOrWhiteSpace(expectedCity) && string.IsNullOrWhiteSpace(expectedCountry)) | ||
{ | ||
content | ||
.FormattedAddress.Should() | ||
.BeEmpty( | ||
"FormattedAddress should be empty when no address components are provided" | ||
); | ||
} | ||
} | ||
} |
Oops, something went wrong.