diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml
index 3fad790..245c5a6 100644
--- a/.github/workflows/dotnet.yml
+++ b/.github/workflows/dotnet.yml
@@ -15,53 +15,22 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
- dotnet-version: '9.0.x'
-
- # Add NuGet Sources
+ dotnet-version: '10.0.x'
- name: Create Local NuGet Directory
run: mkdir ~/nuget
- - name: Add Local Nuget Source
- run: dotnet nuget add source ~/nuget
-
- - name: Add Garage Group NuGet Source
- run: >
- dotnet nuget add source ${{ vars.GG_NUGET_SOURCE_URL }}
- -n garage
- -u ${{ secrets.GG_NUGET_SOURCE_USER_NAME }}
- -p ${{ secrets.GG_NUGET_SOURCE_USER_PASSWORD }}
- --store-password-in-clear-text
-
- # Api.Contract
-
- - name: Restore Api.Contract
- run: dotnet restore ./src/*/Api.Contract.csproj
-
- - name: Build Api.Contract
- run: dotnet build ./src/*/Api.Contract.csproj --no-restore -c Release
-
- - name: Pack Api.Contract
- run: dotnet pack ./src/*/Api.Contract.csproj --no-restore -o ~/nuget -c Release
-
- # Api
-
- - name: Restore Api
- run: dotnet restore ./src/*/Api.csproj
-
- - name: Build Api
- run: dotnet build ./src/*/Api.csproj --no-restore -c Release
-
- - name: Pack Api
- run: dotnet pack ./src/*/Api.csproj --no-restore -o ~/nuget -c Release
+ - name: Restore Infra.Http.Api
+ run: dotnet restore ./*/Infra.Http.Api.slnx
- # Api.Test
+ - name: Build Infra.Http.Api
+ run: dotnet build ./*/Infra.Http.Api.slnx --no-restore -c Release
- - name: Restore Api.Test
- run: dotnet restore ./src/*/Api.Test.csproj
+ - name: Test Infra.Http.Api
+ run: dotnet test ./*/Infra.Http.Api.slnx --no-restore -c Release
- - name: Test Api.Test
- run: dotnet test ./src/*/Api.Test.csproj --no-restore -c Release
+ - name: Pack Infra.Http.Api
+ run: dotnet pack ./*/Infra.Http.Api.slnx --no-build -o ~/nuget -c Release
# Push
@@ -69,6 +38,6 @@ jobs:
if: ${{ github.ref == 'refs/heads/main' }}
run: >
dotnet nuget push "../../../nuget/*.nupkg"
- -s ${{ vars.GG_NUGET_SOURCE_URL }}
- -k ${{ secrets.GG_NUGET_SOURCE_USER_PASSWORD }}
+ -s https://api.nuget.org/v3/index.json
+ -k ${{ secrets.NUGET_SECRET }}
--skip-duplicate
diff --git a/src/Api.Contract/Api.Contract.csproj b/src/Api.Contract/Api.Contract.csproj
index 454b11d..299b65c 100644
--- a/src/Api.Contract/Api.Contract.csproj
+++ b/src/Api.Contract/Api.Contract.csproj
@@ -1,7 +1,7 @@
- net8.0;net9.0
+ net10.0
disable
enable
true
@@ -9,15 +9,15 @@
$(NoWarn);IDE0130;CA1859
GarageGroup.Infra
GarageGroup.Infra.Http.Api.Contract
- 0.3.0
+ 1.0.0
-
+
-
-
+
+
\ No newline at end of file
diff --git a/src/Api.Contract/Input/HttpSendIn.cs b/src/Api.Contract/Input/HttpSendIn.cs
index d0837ff..84bbf63 100644
--- a/src/Api.Contract/Input/HttpSendIn.cs
+++ b/src/Api.Contract/Input/HttpSendIn.cs
@@ -109,13 +109,25 @@ public override string ToString()
return builder.Append('\n').Append('\n').Append(body).ToString();
}
- public static bool operator ==(HttpSendIn left, HttpSendIn right)
- =>
- left.Equals(right);
+ public static bool operator ==(HttpSendIn? left, HttpSendIn? right)
+ {
+ if (ReferenceEquals(left, right))
+ {
+ return true;
+ }
- public static bool operator !=(HttpSendIn left, HttpSendIn right)
- =>
- left.Equals(right) is not true;
+ return left?.Equals(right) is true;
+ }
+
+ public static bool operator !=(HttpSendIn? left, HttpSendIn? right)
+ {
+ if (ReferenceEquals(left, right))
+ {
+ return false;
+ }
+
+ return left?.Equals(right) is not true;
+ }
private static FlatArray> GetOrderedHeaders(FlatArray> source)
{
@@ -163,4 +175,4 @@ static string GetKey(KeyValuePair> kv)
=>
kv.Key;
}
-}
\ No newline at end of file
+}
diff --git a/src/Api.Contract/Input/HttpVerb.cs b/src/Api.Contract/Input/HttpVerb.cs
index efd7ece..a2e5191 100644
--- a/src/Api.Contract/Input/HttpVerb.cs
+++ b/src/Api.Contract/Input/HttpVerb.cs
@@ -80,11 +80,17 @@ public bool Equals(HttpVerb? other)
return StringComparer.InvariantCultureIgnoreCase.Equals(Name, other.Name);
}
- public static bool operator ==(HttpVerb left, HttpVerb right)
- =>
- left.Equals(right);
-
- public static bool operator !=(HttpVerb left, HttpVerb right)
- =>
- left.Equals(right) is not true;
-}
\ No newline at end of file
+ public static bool operator ==(HttpVerb? left, HttpVerb? right)
+ {
+ if (ReferenceEquals(left, right))
+ {
+ return true;
+ }
+
+ return left?.Equals(right) is true;
+ }
+
+ public static bool operator !=(HttpVerb? left, HttpVerb? right)
+ =>
+ (left == right) is false;
+}
diff --git a/src/Api.Test/Api.Test.csproj b/src/Api.Test/Api.Test.csproj
index 1a10d5f..af40a5f 100644
--- a/src/Api.Test/Api.Test.csproj
+++ b/src/Api.Test/Api.Test.csproj
@@ -1,7 +1,7 @@
- net8.0;net9.0
+ net10.0
disable
enable
true
@@ -17,13 +17,13 @@
-
-
-
+
+
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
-
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
diff --git a/src/Api.Test/Test.HttpApi/HttpApiTest.cs b/src/Api.Test/Test.HttpApi/HttpApiTest.cs
new file mode 100644
index 0000000..bfff8c2
--- /dev/null
+++ b/src/Api.Test/Test.HttpApi/HttpApiTest.cs
@@ -0,0 +1,87 @@
+using System;
+using System.Collections.Generic;
+using System.Net.Http;
+using System.Threading;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace GarageGroup.Infra.Http.Api.Test;
+
+public static partial class HttpApiTest
+{
+ public static TheoryData SuccessCaseTestData
+ =>
+ new()
+ {
+ { HttpSuccessType.Default, 201, "Created", true, true },
+ { HttpSuccessType.OnlyHeaders, 200, "OK", true, false },
+ { HttpSuccessType.OnlyStatusCode, 204, "No Content", false, false }
+ };
+
+ public static TheoryData FailureCaseTestData
+ =>
+ new()
+ {
+ { 404, "Not Found", "{\"error\":\"not found\"}", "application/json", "utf-8" },
+ { 503, "Service Unavailable", "temporary outage", "text/plain", "us-ascii" }
+ };
+
+ private static HttpApi CreateApi(
+ Func> sendAsync,
+ HttpApiOption option = default)
+ {
+ if (option.BaseAddress is null)
+ {
+ option = option with
+ {
+ BaseAddress = new("https://example.com/")
+ };
+ }
+
+ return new(new DelegateHttpMessageHandler(sendAsync), option);
+ }
+
+ private sealed class DelegateHttpMessageHandler(
+ Func> sendAsync) : HttpMessageHandler
+ {
+ protected override Task SendAsync(
+ HttpRequestMessage request, CancellationToken cancellationToken)
+ =>
+ sendAsync(request, cancellationToken);
+ }
+
+ private static HttpResponseMessage CreateResponse(
+ int statusCode,
+ string? reasonPhrase,
+ IEnumerable> headers,
+ string? body,
+ string? mediaType = null,
+ string? charSet = null)
+ {
+ var response = new HttpResponseMessage((System.Net.HttpStatusCode)statusCode)
+ {
+ ReasonPhrase = reasonPhrase
+ };
+
+ foreach (var header in headers)
+ {
+ response.Headers.Add(header.Key, header.Value);
+ }
+
+ if (body is null)
+ {
+ return response;
+ }
+
+ response.Content = new StringContent(body);
+ if (string.IsNullOrEmpty(mediaType) is false)
+ {
+ response.Content.Headers.ContentType = new(mediaType)
+ {
+ CharSet = charSet
+ };
+ }
+
+ return response;
+ }
+}
diff --git a/src/Api.Test/Test.HttpApi/Test.Constructor.cs b/src/Api.Test/Test.HttpApi/Test.Constructor.cs
new file mode 100644
index 0000000..c137e8f
--- /dev/null
+++ b/src/Api.Test/Test.HttpApi/Test.Constructor.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Net;
+using System.Net.Http;
+using System.Threading;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace GarageGroup.Infra.Http.Api.Test;
+
+partial class HttpApiTest
+{
+ [Fact]
+ public static async Task Constructor_BaseAddressProvided_ExpectRequestUriCombined()
+ {
+ Uri? actualUri = null;
+
+ var source = CreateApi(
+ sendAsync: (request, _) =>
+ {
+ actualUri = request.RequestUri;
+ return Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK));
+ },
+ option: new()
+ {
+ BaseAddress = new("https://example.com/root/")
+ });
+
+ _ = await source.SendAsync(
+ new(HttpVerb.Get, "products"),
+ TestContext.Current.CancellationToken);
+
+ Assert.Equal(new("https://example.com/root/products"), actualUri);
+ }
+}
diff --git a/src/Api.Test/Test.HttpApi/Test.Send.cs b/src/Api.Test/Test.HttpApi/Test.Send.cs
new file mode 100644
index 0000000..9f5aec8
--- /dev/null
+++ b/src/Api.Test/Test.HttpApi/Test.Send.cs
@@ -0,0 +1,209 @@
+using System;
+using System.Linq;
+using System.Net;
+using System.Net.Http;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace GarageGroup.Infra.Http.Api.Test;
+
+partial class HttpApiTest
+{
+ [Theory]
+ [MemberData(nameof(SuccessCaseTestData))]
+ public static async Task SendAsync_ResponseIsSuccess_ExpectMappedSuccess(
+ HttpSuccessType successType,
+ int statusCode,
+ string? reasonPhrase,
+ bool hasHeaders,
+ bool hasBody)
+ {
+ var source = CreateApi(
+ sendAsync: (_, _) =>
+ Task.FromResult(
+ CreateResponse(
+ statusCode: statusCode,
+ reasonPhrase: reasonPhrase,
+ headers: [new("x-request-id", "abc")],
+ body: "{\"id\":1}",
+ mediaType: "application/json",
+ charSet: "utf-8")));
+
+ var actual = await source.SendAsync(
+ new(HttpVerb.Post, "/users")
+ {
+ SuccessType = successType
+ },
+ TestContext.Current.CancellationToken);
+
+ Assert.True(actual.IsSuccess);
+ var success = actual.SuccessOrThrow();
+
+ Assert.Equal((HttpSuccessCode)(statusCode - 200), success.StatusCode);
+ Assert.Equal(reasonPhrase, success.ReasonPhrase);
+
+ if (hasHeaders)
+ {
+ Assert.Equal(1, success.Headers.Length);
+ Assert.True(string.Equals(success.Headers[0].Key, "x-request-id", StringComparison.InvariantCultureIgnoreCase));
+ Assert.Equal("abc", success.Headers[0].Value);
+ }
+ else
+ {
+ Assert.Equal(default, success.Headers);
+ }
+
+ if (hasBody)
+ {
+ Assert.Equal("application/json", success.Body.Type.MediaType);
+ Assert.Equal("utf-8", success.Body.Type.CharSet);
+ Assert.Equal("{\"id\":1}", success.Body.Content?.ToString());
+ }
+ else
+ {
+ Assert.Equal(default, success.Body);
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(FailureCaseTestData))]
+ public static async Task SendAsync_ResponseIsFailure_ExpectMappedFailure(
+ int statusCode,
+ string? reasonPhrase,
+ string? body,
+ string? mediaType,
+ string? charSet)
+ {
+ var source = CreateApi(
+ sendAsync: (_, _) =>
+ Task.FromResult(
+ CreateResponse(
+ statusCode: statusCode,
+ reasonPhrase: reasonPhrase,
+ headers: [new("x-request-id", "abc")],
+ body: body,
+ mediaType: mediaType,
+ charSet: charSet)));
+
+ var actual = await source.SendAsync(
+ new(HttpVerb.Get, "/missing")
+ {
+ SuccessType = HttpSuccessType.OnlyStatusCode
+ },
+ TestContext.Current.CancellationToken);
+
+ Assert.True(actual.IsFailure);
+ var failure = actual.FailureOrThrow();
+
+ Assert.Equal((HttpFailureCode)statusCode, failure.StatusCode);
+ Assert.Equal(reasonPhrase, failure.ReasonPhrase);
+ Assert.Equal(1, failure.Headers.Length);
+ Assert.True(string.Equals(failure.Headers[0].Key, "x-request-id", StringComparison.InvariantCultureIgnoreCase));
+ Assert.Equal("abc", failure.Headers[0].Value);
+
+ if (body is null)
+ {
+ Assert.Equal(default, failure.Body);
+ return;
+ }
+
+ Assert.Equal(mediaType, failure.Body.Type.MediaType);
+ Assert.Equal(charSet, failure.Body.Type.CharSet);
+ Assert.Equal(body, failure.Body.Content?.ToString());
+ }
+
+ [Fact]
+ public static async Task SendAsync_InputIsNull_ExpectArgumentNullException()
+ {
+ var source = CreateApi(
+ sendAsync: (_, _) => Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK)));
+
+ await Assert.ThrowsAsync(
+ () => source.SendAsync(null!, TestContext.Current.CancellationToken).AsTask());
+ }
+
+ [Fact]
+ public static async Task SendAsync_InputHasHeadersAndBody_ExpectRequestMapped()
+ {
+ HttpMethod? actualMethod = null;
+ string? actualHeaderValue = null;
+ string? actualContentType = null;
+ string? actualBody = null;
+
+ var source = CreateApi(
+ sendAsync: async (request, cancellationToken) =>
+ {
+ actualMethod = request.Method;
+ actualHeaderValue = request.Headers.GetValues("x-request-id").SingleOrDefault();
+ actualContentType = request.Content?.Headers.ContentType?.ToString();
+ actualBody = request.Content is null
+ ? null
+ : await request.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
+
+ return new HttpResponseMessage(HttpStatusCode.OK);
+ });
+
+ _ = await source.SendAsync(
+ new(HttpVerb.Patch, "/users/1")
+ {
+ Headers = [new("x-request-id", "abc")],
+ Body = new()
+ {
+ Type = new("application/json", "utf-8"),
+ Content = new("{\"firstName\":\"John\"}")
+ }
+ },
+ TestContext.Current.CancellationToken);
+
+ Assert.Equal(HttpMethod.Patch, actualMethod);
+ Assert.Equal("abc", actualHeaderValue);
+ Assert.Equal("application/json; charset=utf-8", actualContentType);
+ Assert.Equal("{\"firstName\":\"John\"}", actualBody);
+ }
+
+ [Fact]
+ public static async Task SendAsync_InputHasDuplicateHeaders_ExpectAllValuesMapped()
+ {
+ string[] actualHeaderValues = [];
+ var source = CreateApi(
+ sendAsync: (request, _) =>
+ {
+ actualHeaderValues = request.Headers.GetValues("x-request-id").ToArray();
+ return Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK));
+ });
+
+ _ = await source.SendAsync(
+ new(HttpVerb.Get, "/users")
+ {
+ Headers =
+ [
+ new("x-request-id", "abc"),
+ new("X-Request-ID", "def")
+ ]
+ },
+ TestContext.Current.CancellationToken);
+
+ Assert.Equal(["abc", "def"], actualHeaderValues);
+ }
+
+ [Fact]
+ public static async Task SendAsync_InputBodyIsEmpty_ExpectRequestWithoutContent()
+ {
+ var hasContent = true;
+ var source = CreateApi(
+ sendAsync: (request, _) =>
+ {
+ hasContent = request.Content is not null;
+ return Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK));
+ });
+
+ _ = await source.SendAsync(
+ new(HttpVerb.Get, "/users")
+ {
+ Body = default
+ },
+ TestContext.Current.CancellationToken);
+
+ Assert.False(hasContent);
+ }
+}
diff --git a/src/Api.Test/Test.HttpBody/HttpBodyTest.cs b/src/Api.Test/Test.HttpBody/HttpBodyTest.cs
new file mode 100644
index 0000000..c298c60
--- /dev/null
+++ b/src/Api.Test/Test.HttpBody/HttpBodyTest.cs
@@ -0,0 +1,75 @@
+using Xunit;
+
+namespace GarageGroup.Infra.Http.Api.Test;
+
+public static partial class HttpBodyTest
+{
+ public static TheoryData EqualTestData
+ =>
+ new()
+ {
+ {
+ new()
+ {
+ Type = new("application/json")
+ },
+ new()
+ {
+ Type = new("text/plain")
+ }
+ },
+ {
+ new()
+ {
+ Type = new("application/json", "utf-8"),
+ Content = new("{\"value\":1}")
+ },
+ new()
+ {
+ Type = new("application/json", "utf-8"),
+ Content = new("{\"value\":1}")
+ }
+ }
+ };
+
+ public static TheoryData UnequalTestData
+ =>
+ new()
+ {
+ {
+ new()
+ {
+ Type = new("application/json")
+ },
+ new()
+ {
+ Type = new("application/json"),
+ Content = new("{}")
+ }
+ },
+ {
+ new()
+ {
+ Type = new("application/json", "utf-8"),
+ Content = new("{\"value\":1}")
+ },
+ new()
+ {
+ Type = new("application/json", "utf-16"),
+ Content = new("{\"value\":1}")
+ }
+ },
+ {
+ new()
+ {
+ Type = new("application/json", "utf-8"),
+ Content = new("{\"value\":1}")
+ },
+ new()
+ {
+ Type = new("application/json", "utf-8"),
+ Content = new("{\"value\":2}")
+ }
+ }
+ };
+}
diff --git a/src/Api.Test/Test.HttpBody/Test.DeserializeFromJson.cs b/src/Api.Test/Test.HttpBody/Test.DeserializeFromJson.cs
new file mode 100644
index 0000000..97f9410
--- /dev/null
+++ b/src/Api.Test/Test.HttpBody/Test.DeserializeFromJson.cs
@@ -0,0 +1,65 @@
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using Xunit;
+
+namespace GarageGroup.Infra.Http.Api.Test;
+
+partial class HttpBodyTest
+{
+ [Fact]
+ public static void DeserializeFromJson_ContentIsNull_ExpectDefault()
+ {
+ var source = new HttpBody
+ {
+ Type = new("application/json", "utf-8")
+ };
+
+ var result = source.DeserializeFromJson();
+ Assert.Null(result);
+ }
+
+ [Fact]
+ public static void DeserializeFromJson_ExpectModel()
+ {
+ var source = new HttpBody
+ {
+ Type = new("application/json", "utf-8"),
+ Content = new("{\"firstName\":\"John\",\"age\":18}")
+ };
+
+ var result = source.DeserializeFromJson();
+
+ Assert.NotNull(result);
+ Assert.Equal("John", result?.FirstName);
+ Assert.Equal(18, result?.Age);
+ }
+
+ [Fact]
+ public static void DeserializeFromJson_CustomSerializerOptions_ExpectAppliedOptions()
+ {
+ var source = new HttpBody
+ {
+ Type = new("application/json", "utf-8"),
+ Content = new("{\"firstName\":\"John\",\"age\":\"18\"}")
+ };
+
+ var serializerOptions = new JsonSerializerOptions
+ {
+ NumberHandling = JsonNumberHandling.AllowReadingFromString,
+ PropertyNameCaseInsensitive = true
+ };
+
+ var result = source.DeserializeFromJson(serializerOptions);
+
+ Assert.NotNull(result);
+ Assert.Equal("John", result?.FirstName);
+ Assert.Equal(18, result?.Age);
+ }
+
+ private sealed record class DeserializeModel
+ {
+ public string? FirstName { get; init; }
+
+ public int Age { get; init; }
+ }
+}
diff --git a/src/Api.Test/Test.HttpBody/Test.Equality.Equals.cs b/src/Api.Test/Test.HttpBody/Test.Equality.Equals.cs
new file mode 100644
index 0000000..ea2b576
--- /dev/null
+++ b/src/Api.Test/Test.HttpBody/Test.Equality.Equals.cs
@@ -0,0 +1,24 @@
+using Xunit;
+
+namespace GarageGroup.Infra.Http.Api.Test;
+
+partial class HttpBodyTest
+{
+ [Theory]
+ [MemberData(nameof(EqualTestData))]
+ public static void Equals_SourceIsEqualToOther_ExpectTrue(
+ HttpBody source, HttpBody other)
+ {
+ var result = source.Equals(other);
+ Assert.True(result);
+ }
+
+ [Theory]
+ [MemberData(nameof(UnequalTestData))]
+ public static void Equals_SourceIsNotEqualToOther_ExpectFalse(
+ HttpBody source, HttpBody other)
+ {
+ var result = source.Equals(other);
+ Assert.False(result);
+ }
+}
diff --git a/src/Api.Test/Test.HttpBody/Test.Equality.EqualsWithObject.cs b/src/Api.Test/Test.HttpBody/Test.Equality.EqualsWithObject.cs
new file mode 100644
index 0000000..fa90d74
--- /dev/null
+++ b/src/Api.Test/Test.HttpBody/Test.Equality.EqualsWithObject.cs
@@ -0,0 +1,40 @@
+using Xunit;
+
+namespace GarageGroup.Infra.Http.Api.Test;
+
+partial class HttpBodyTest
+{
+ [Theory]
+ [MemberData(nameof(EqualTestData))]
+ public static void EqualsWithObject_SourceIsEqualToOther_ExpectTrue(
+ HttpBody source, HttpBody other)
+ {
+ var result = source.Equals((object)other);
+ Assert.True(result);
+ }
+
+ [Theory]
+ [MemberData(nameof(UnequalTestData))]
+ public static void EqualsWithObject_SourceIsNotEqualToOther_ExpectFalse(
+ HttpBody source, HttpBody other)
+ {
+ var result = source.Equals((object)other);
+ Assert.False(result);
+ }
+
+ [Fact]
+ public static void EqualsWithObject_OtherIsNull_ExpectFalse()
+ {
+ object? other = null;
+
+ var result = default(HttpBody).Equals(other);
+ Assert.False(result);
+ }
+
+ [Fact]
+ public static void EqualsWithObject_OtherHasAnotherType_ExpectFalse()
+ {
+ var result = default(HttpBody).Equals(new object());
+ Assert.False(result);
+ }
+}
diff --git a/src/Api.Test/Test.HttpBody/Test.Equality.GetHashCode.cs b/src/Api.Test/Test.HttpBody/Test.Equality.GetHashCode.cs
new file mode 100644
index 0000000..0eb065a
--- /dev/null
+++ b/src/Api.Test/Test.HttpBody/Test.Equality.GetHashCode.cs
@@ -0,0 +1,28 @@
+using Xunit;
+
+namespace GarageGroup.Infra.Http.Api.Test;
+
+partial class HttpBodyTest
+{
+ [Theory]
+ [MemberData(nameof(EqualTestData))]
+ public static void GetHashCode_SourceIsEqualToOther_ExpectEqualHashCodes(
+ HttpBody source, HttpBody other)
+ {
+ var sourceHashCode = source.GetHashCode();
+ var otherHashCode = other.GetHashCode();
+
+ Assert.Equal(sourceHashCode, otherHashCode);
+ }
+
+ [Theory]
+ [MemberData(nameof(UnequalTestData))]
+ public static void GetHashCode_SourceIsNotEqualToOther_ExpectUnequalHashCodes(
+ HttpBody source, HttpBody other)
+ {
+ var sourceHashCode = source.GetHashCode();
+ var otherHashCode = other.GetHashCode();
+
+ Assert.NotEqual(sourceHashCode, otherHashCode);
+ }
+}
diff --git a/src/Api.Test/Test.HttpBody/Test.Equality.Operator.Equality.cs b/src/Api.Test/Test.HttpBody/Test.Equality.Operator.Equality.cs
new file mode 100644
index 0000000..2e6d69f
--- /dev/null
+++ b/src/Api.Test/Test.HttpBody/Test.Equality.Operator.Equality.cs
@@ -0,0 +1,24 @@
+using Xunit;
+
+namespace GarageGroup.Infra.Http.Api.Test;
+
+partial class HttpBodyTest
+{
+ [Theory]
+ [MemberData(nameof(EqualTestData))]
+ public static void EqualityOperator_SourceIsEqualToOther_ExpectTrue(
+ HttpBody source, HttpBody other)
+ {
+ var result = source == other;
+ Assert.True(result);
+ }
+
+ [Theory]
+ [MemberData(nameof(UnequalTestData))]
+ public static void EqualityOperator_SourceIsNotEqualToOther_ExpectFalse(
+ HttpBody source, HttpBody other)
+ {
+ var result = source == other;
+ Assert.False(result);
+ }
+}
diff --git a/src/Api.Test/Test.HttpBody/Test.Equality.Operator.Inequality.cs b/src/Api.Test/Test.HttpBody/Test.Equality.Operator.Inequality.cs
new file mode 100644
index 0000000..6a03e2f
--- /dev/null
+++ b/src/Api.Test/Test.HttpBody/Test.Equality.Operator.Inequality.cs
@@ -0,0 +1,24 @@
+using Xunit;
+
+namespace GarageGroup.Infra.Http.Api.Test;
+
+partial class HttpBodyTest
+{
+ [Theory]
+ [MemberData(nameof(EqualTestData))]
+ public static void InequalityOperator_SourceIsEqualToOther_ExpectFalse(
+ HttpBody source, HttpBody other)
+ {
+ var result = source != other;
+ Assert.False(result);
+ }
+
+ [Theory]
+ [MemberData(nameof(UnequalTestData))]
+ public static void InequalityOperator_SourceIsNotEqualToOther_ExpectTrue(
+ HttpBody source, HttpBody other)
+ {
+ var result = source != other;
+ Assert.True(result);
+ }
+}
diff --git a/src/Api.Test/Test.HttpBody/Test.SerializeAsJson.cs b/src/Api.Test/Test.HttpBody/Test.SerializeAsJson.cs
new file mode 100644
index 0000000..643cb13
--- /dev/null
+++ b/src/Api.Test/Test.HttpBody/Test.SerializeAsJson.cs
@@ -0,0 +1,52 @@
+using System.Text.Json;
+using Xunit;
+
+namespace GarageGroup.Infra.Http.Api.Test;
+
+partial class HttpBodyTest
+{
+ [Fact]
+ public static void SerializeAsJson_ExpectJsonTypeAndContent()
+ {
+ var source = SerializeModel.Create();
+
+ var result = HttpBody.SerializeAsJson(source);
+
+ Assert.Equal("application/json", result.Type.MediaType);
+ Assert.Equal("utf-8", result.Type.CharSet);
+ Assert.NotNull(result.Content);
+
+ var json = result.Content?.ToString();
+ Assert.Contains("\"firstName\":\"John\"", json);
+ }
+
+ [Fact]
+ public static void SerializeAsJson_CustomSerializerOptions_ExpectAppliedOptions()
+ {
+ var source = SerializeModel.Create();
+ var serializerOptions = new JsonSerializerOptions
+ {
+ PropertyNamingPolicy = null
+ };
+
+ var result = HttpBody.SerializeAsJson(source, serializerOptions);
+
+ var json = result.Content?.ToString();
+ Assert.Contains("\"FirstName\":\"John\"", json);
+ }
+
+ private sealed record class SerializeModel
+ {
+ public string? FirstName { get; init; }
+
+ public int Age { get; init; }
+
+ public static SerializeModel Create()
+ =>
+ new()
+ {
+ FirstName = "John",
+ Age = 18
+ };
+ }
+}
diff --git a/src/Api.Test/Test.HttpBody/Test.ToString.cs b/src/Api.Test/Test.HttpBody/Test.ToString.cs
new file mode 100644
index 0000000..447a7ba
--- /dev/null
+++ b/src/Api.Test/Test.HttpBody/Test.ToString.cs
@@ -0,0 +1,62 @@
+using System;
+using Xunit;
+
+namespace GarageGroup.Infra.Http.Api.Test;
+
+partial class HttpBodyTest
+{
+ [Fact]
+ public static void ToString_ContentIsNull_ExpectNull()
+ {
+ var source = new HttpBody
+ {
+ Type = new("application/json", "utf-8")
+ };
+
+ var result = source.ToString();
+ Assert.Null(result);
+ }
+
+ [Fact]
+ public static void ToString_ContentTypeIsEmpty_ExpectBodyOnly()
+ {
+ var source = new HttpBody
+ {
+ Type = default,
+ Content = new("{\"value\":1}")
+ };
+
+ var result = source.ToString();
+ Assert.Equal("{\"value\":1}", result);
+ }
+
+ [Fact]
+ public static void ToString_ContentTypeHasMediaType_ExpectHeaderAndBody()
+ {
+ var source = new HttpBody
+ {
+ Type = new("application/json"),
+ Content = new("{\"value\":1}")
+ };
+
+ var result = source.ToString();
+ var expected = $"Content-Type: application/json{Environment.NewLine}{{\"value\":1}}";
+
+ Assert.Equal(expected, result);
+ }
+
+ [Fact]
+ public static void ToString_ContentTypeHasMediaTypeAndCharSet_ExpectHeaderAndBody()
+ {
+ var source = new HttpBody
+ {
+ Type = new("application/json", "utf-8"),
+ Content = new("{\"value\":1}")
+ };
+
+ var result = source.ToString();
+ var expected = $"Content-Type: application/json; charset=utf-8{Environment.NewLine}{{\"value\":1}}";
+
+ Assert.Equal(expected, result);
+ }
+}
diff --git a/src/Api.Test/Test.HttpBodyType/HttpBodyTypeTest.cs b/src/Api.Test/Test.HttpBodyType/HttpBodyTypeTest.cs
new file mode 100644
index 0000000..925260a
--- /dev/null
+++ b/src/Api.Test/Test.HttpBodyType/HttpBodyTypeTest.cs
@@ -0,0 +1,38 @@
+using Xunit;
+
+namespace GarageGroup.Infra.Http.Api.Test;
+
+public static partial class HttpBodyTypeTest
+{
+ public static TheoryData EqualTestData
+ =>
+ new()
+ {
+ {
+ new(mediaType: null!, charSet: null!),
+ new(mediaType: "\n\r", charSet: " ")
+ },
+ {
+ new(mediaType: "application/json", charSet: "utf-8"),
+ new(mediaType: "application/json", charSet: "utf-8")
+ }
+ };
+
+ public static TheoryData UnequalTestData
+ =>
+ new()
+ {
+ {
+ new(mediaType: "application/json", charSet: "utf-8"),
+ new(mediaType: "text/plain", charSet: "utf-8")
+ },
+ {
+ new(mediaType: "application/json", charSet: "utf-8"),
+ new(mediaType: "application/json", charSet: "utf-16")
+ },
+ {
+ new(mediaType: "application/json", charSet: null!),
+ new(mediaType: "application/json", charSet: "utf-8")
+ }
+ };
+}
diff --git a/src/Api.Test/Test.HttpBodyType/Test.Constructor.cs b/src/Api.Test/Test.HttpBodyType/Test.Constructor.cs
new file mode 100644
index 0000000..fd32dc1
--- /dev/null
+++ b/src/Api.Test/Test.HttpBodyType/Test.Constructor.cs
@@ -0,0 +1,24 @@
+using Xunit;
+
+namespace GarageGroup.Infra.Http.Api.Test;
+
+partial class HttpBodyTypeTest
+{
+ [Fact]
+ public static void Constructor_MediaTypeIsWhiteSpace_ExpectMediaTypeNull()
+ {
+ var result = new HttpBodyType(mediaType: " \n\r ", charSet: "utf-8");
+
+ Assert.Null(result.MediaType);
+ Assert.Equal("utf-8", result.CharSet);
+ }
+
+ [Fact]
+ public static void Constructor_CharSetIsWhiteSpace_ExpectCharSetNull()
+ {
+ var result = new HttpBodyType(mediaType: "application/json", charSet: " ");
+
+ Assert.Equal("application/json", result.MediaType);
+ Assert.Null(result.CharSet);
+ }
+}
diff --git a/src/Api.Test/Test.HttpBodyType/Test.Equality.Equals.cs b/src/Api.Test/Test.HttpBodyType/Test.Equality.Equals.cs
new file mode 100644
index 0000000..16443a0
--- /dev/null
+++ b/src/Api.Test/Test.HttpBodyType/Test.Equality.Equals.cs
@@ -0,0 +1,24 @@
+using Xunit;
+
+namespace GarageGroup.Infra.Http.Api.Test;
+
+partial class HttpBodyTypeTest
+{
+ [Theory]
+ [MemberData(nameof(EqualTestData))]
+ public static void Equals_SourceIsEqualToOther_ExpectTrue(
+ HttpBodyType source, HttpBodyType other)
+ {
+ var result = source.Equals(other);
+ Assert.True(result);
+ }
+
+ [Theory]
+ [MemberData(nameof(UnequalTestData))]
+ public static void Equals_SourceIsNotEqualToOther_ExpectFalse(
+ HttpBodyType source, HttpBodyType other)
+ {
+ var result = source.Equals(other);
+ Assert.False(result);
+ }
+}
diff --git a/src/Api.Test/Test.HttpBodyType/Test.Equality.EqualsWithObject.cs b/src/Api.Test/Test.HttpBodyType/Test.Equality.EqualsWithObject.cs
new file mode 100644
index 0000000..646b9de
--- /dev/null
+++ b/src/Api.Test/Test.HttpBodyType/Test.Equality.EqualsWithObject.cs
@@ -0,0 +1,40 @@
+using Xunit;
+
+namespace GarageGroup.Infra.Http.Api.Test;
+
+partial class HttpBodyTypeTest
+{
+ [Theory]
+ [MemberData(nameof(EqualTestData))]
+ public static void EqualsWithObject_SourceIsEqualToOther_ExpectTrue(
+ HttpBodyType source, HttpBodyType other)
+ {
+ var result = source.Equals((object)other);
+ Assert.True(result);
+ }
+
+ [Theory]
+ [MemberData(nameof(UnequalTestData))]
+ public static void EqualsWithObject_SourceIsNotEqualToOther_ExpectFalse(
+ HttpBodyType source, HttpBodyType other)
+ {
+ var result = source.Equals((object)other);
+ Assert.False(result);
+ }
+
+ [Fact]
+ public static void EqualsWithObject_ObjectIsNull_ExpectFalse()
+ {
+ object? other = null;
+
+ var result = default(HttpBodyType).Equals(other);
+ Assert.False(result);
+ }
+
+ [Fact]
+ public static void EqualsWithObject_ObjectHasAnotherType_ExpectFalse()
+ {
+ var result = default(HttpBodyType).Equals(new object());
+ Assert.False(result);
+ }
+}
diff --git a/src/Api.Test/Test.HttpBodyType/Test.Equality.GetHashCode.cs b/src/Api.Test/Test.HttpBodyType/Test.Equality.GetHashCode.cs
new file mode 100644
index 0000000..08092f9
--- /dev/null
+++ b/src/Api.Test/Test.HttpBodyType/Test.Equality.GetHashCode.cs
@@ -0,0 +1,28 @@
+using Xunit;
+
+namespace GarageGroup.Infra.Http.Api.Test;
+
+partial class HttpBodyTypeTest
+{
+ [Theory]
+ [MemberData(nameof(EqualTestData))]
+ public static void GetHashCode_SourceIsEqualToOther_ExpectEqualHashCodes(
+ HttpBodyType source, HttpBodyType other)
+ {
+ var sourceHashCode = source.GetHashCode();
+ var otherHashCode = other.GetHashCode();
+
+ Assert.Equal(sourceHashCode, otherHashCode);
+ }
+
+ [Theory]
+ [MemberData(nameof(UnequalTestData))]
+ public static void GetHashCode_SourceIsNotEqualToOther_ExpectUnequalHashCodes(
+ HttpBodyType source, HttpBodyType other)
+ {
+ var sourceHashCode = source.GetHashCode();
+ var otherHashCode = other.GetHashCode();
+
+ Assert.NotEqual(sourceHashCode, otherHashCode);
+ }
+}
diff --git a/src/Api.Test/Test.HttpBodyType/Test.Equality.Operator.Equality.cs b/src/Api.Test/Test.HttpBodyType/Test.Equality.Operator.Equality.cs
new file mode 100644
index 0000000..b132346
--- /dev/null
+++ b/src/Api.Test/Test.HttpBodyType/Test.Equality.Operator.Equality.cs
@@ -0,0 +1,24 @@
+using Xunit;
+
+namespace GarageGroup.Infra.Http.Api.Test;
+
+partial class HttpBodyTypeTest
+{
+ [Theory]
+ [MemberData(nameof(EqualTestData))]
+ public static void EqualityOperator_SourceIsEqualToOther_ExpectTrue(
+ HttpBodyType source, HttpBodyType other)
+ {
+ var result = source == other;
+ Assert.True(result);
+ }
+
+ [Theory]
+ [MemberData(nameof(UnequalTestData))]
+ public static void EqualityOperator_SourceIsNotEqualToOther_ExpectFalse(
+ HttpBodyType source, HttpBodyType other)
+ {
+ var result = source == other;
+ Assert.False(result);
+ }
+}
diff --git a/src/Api.Test/Test.HttpBodyType/Test.Equality.Operator.Inequality.cs b/src/Api.Test/Test.HttpBodyType/Test.Equality.Operator.Inequality.cs
new file mode 100644
index 0000000..6b2c545
--- /dev/null
+++ b/src/Api.Test/Test.HttpBodyType/Test.Equality.Operator.Inequality.cs
@@ -0,0 +1,24 @@
+using Xunit;
+
+namespace GarageGroup.Infra.Http.Api.Test;
+
+partial class HttpBodyTypeTest
+{
+ [Theory]
+ [MemberData(nameof(EqualTestData))]
+ public static void InequalityOperator_SourceIsEqualToOther_ExpectFalse(
+ HttpBodyType source, HttpBodyType other)
+ {
+ var result = source != other;
+ Assert.False(result);
+ }
+
+ [Theory]
+ [MemberData(nameof(UnequalTestData))]
+ public static void InequalityOperator_SourceIsNotEqualToOther_ExpectTrue(
+ HttpBodyType source, HttpBodyType other)
+ {
+ var result = source != other;
+ Assert.True(result);
+ }
+}
diff --git a/src/Api.Test/Test.HttpBodyType/Test.IsJsonMediaType.cs b/src/Api.Test/Test.HttpBodyType/Test.IsJsonMediaType.cs
new file mode 100644
index 0000000..eee77be
--- /dev/null
+++ b/src/Api.Test/Test.HttpBodyType/Test.IsJsonMediaType.cs
@@ -0,0 +1,42 @@
+using Xunit;
+
+namespace GarageGroup.Infra.Http.Api.Test;
+
+partial class HttpBodyTypeTest
+{
+ [Fact]
+ public static void IsJsonMediaType_MediaTypeIsNull_ExpectFalse()
+ {
+ var source = new HttpBodyType(mediaType: null!, charSet: null!);
+
+ var result = source.IsJsonMediaType(isApplicationJsonStrict: true);
+ Assert.False(result);
+ }
+
+ [Fact]
+ public static void IsJsonMediaType_MediaTypeIsApplicationJson_ExpectTrue()
+ {
+ var source = new HttpBodyType(mediaType: "APPLICATION/JSON", charSet: "utf-8");
+
+ var result = source.IsJsonMediaType(isApplicationJsonStrict: true);
+ Assert.True(result);
+ }
+
+ [Fact]
+ public static void IsJsonMediaType_MediaTypeContainsJsonAndStrictIsFalse_ExpectTrue()
+ {
+ var source = new HttpBodyType(mediaType: "application/problem+json", charSet: "utf-8");
+
+ var result = source.IsJsonMediaType(isApplicationJsonStrict: false);
+ Assert.True(result);
+ }
+
+ [Fact]
+ public static void IsJsonMediaType_MediaTypeContainsJsonAndStrictIsTrue_ExpectFalse()
+ {
+ var source = new HttpBodyType(mediaType: "application/problem+json", charSet: "utf-8");
+
+ var result = source.IsJsonMediaType(isApplicationJsonStrict: true);
+ Assert.False(result);
+ }
+}
diff --git a/src/Api.Test/Test.HttpSendFailure/HttpSendFailureTest.cs b/src/Api.Test/Test.HttpSendFailure/HttpSendFailureTest.cs
new file mode 100644
index 0000000..16ddaf5
--- /dev/null
+++ b/src/Api.Test/Test.HttpSendFailure/HttpSendFailureTest.cs
@@ -0,0 +1,115 @@
+using System.Collections.Generic;
+using Xunit;
+
+namespace GarageGroup.Infra.Http.Api.Test;
+
+public static partial class HttpSendFailureTest
+{
+ public static TheoryData EqualTestData
+ =>
+ new()
+ {
+ {
+ default,
+ default
+ },
+ {
+ new()
+ {
+ StatusCode = HttpFailureCode.BadGateway,
+ ReasonPhrase = "Bad Gateway",
+ Headers =
+ [
+ new("x-api-version", "2"),
+ new("retry-after", "5")
+ ],
+ Body = new()
+ {
+ Type = new("application/json", "utf-8"),
+ Content = new("{\"error\":\"proxy\"}")
+ }
+ },
+ new()
+ {
+ StatusCode = HttpFailureCode.BadGateway,
+ ReasonPhrase = "Bad Gateway",
+ Headers =
+ [
+ new("x-api-version", "2"),
+ new("retry-after", "5")
+ ],
+ Body = new()
+ {
+ Type = new("application/json", "utf-8"),
+ Content = new("{\"error\":\"proxy\"}")
+ }
+ }
+ }
+ };
+
+ public static TheoryData UnequalTestData
+ =>
+ new()
+ {
+ {
+ new()
+ {
+ StatusCode = HttpFailureCode.BadRequest
+ },
+ new()
+ {
+ StatusCode = HttpFailureCode.NotFound
+ }
+ },
+ {
+ new()
+ {
+ StatusCode = HttpFailureCode.BadRequest,
+ ReasonPhrase = "Bad Request"
+ },
+ new()
+ {
+ StatusCode = HttpFailureCode.BadRequest,
+ ReasonPhrase = "Request Failed"
+ }
+ },
+ {
+ new()
+ {
+ StatusCode = HttpFailureCode.BadGateway,
+ Headers =
+ [
+ new("x-api-version", "2")
+ ]
+ },
+ new()
+ {
+ StatusCode = HttpFailureCode.BadGateway,
+ Headers =
+ [
+ new("x-api-version", "3")
+ ]
+ }
+ },
+ {
+ new()
+ {
+ StatusCode = HttpFailureCode.BadGateway,
+ Body = new()
+ {
+ Type = new("application/json", "utf-8"),
+ Content = new("{\"error\":\"proxy\"}")
+ }
+ },
+ new()
+ {
+ StatusCode = HttpFailureCode.BadGateway,
+ Body = new()
+ {
+ Type = new("application/json", "utf-8"),
+ Content = new("{\"error\":\"timeout\"}")
+ }
+ }
+ }
+ };
+}
diff --git a/src/Api.Test/Test.HttpSendFailure/Test.Equality.Equals.cs b/src/Api.Test/Test.HttpSendFailure/Test.Equality.Equals.cs
new file mode 100644
index 0000000..cd5798c
--- /dev/null
+++ b/src/Api.Test/Test.HttpSendFailure/Test.Equality.Equals.cs
@@ -0,0 +1,24 @@
+using Xunit;
+
+namespace GarageGroup.Infra.Http.Api.Test;
+
+partial class HttpSendFailureTest
+{
+ [Theory]
+ [MemberData(nameof(EqualTestData))]
+ public static void Equals_SourceIsEqualToOther_ExpectTrue(
+ HttpSendFailure source, HttpSendFailure other)
+ {
+ var result = source.Equals(other);
+ Assert.True(result);
+ }
+
+ [Theory]
+ [MemberData(nameof(UnequalTestData))]
+ public static void Equals_SourceIsNotEqualToOther_ExpectFalse(
+ HttpSendFailure source, HttpSendFailure other)
+ {
+ var result = source.Equals(other);
+ Assert.False(result);
+ }
+}
diff --git a/src/Api.Test/Test.HttpSendFailure/Test.Equality.EqualsWithObject.cs b/src/Api.Test/Test.HttpSendFailure/Test.Equality.EqualsWithObject.cs
new file mode 100644
index 0000000..adfd616
--- /dev/null
+++ b/src/Api.Test/Test.HttpSendFailure/Test.Equality.EqualsWithObject.cs
@@ -0,0 +1,40 @@
+using Xunit;
+
+namespace GarageGroup.Infra.Http.Api.Test;
+
+partial class HttpSendFailureTest
+{
+ [Theory]
+ [MemberData(nameof(EqualTestData))]
+ public static void EqualsWithObject_SourceIsEqualToOther_ExpectTrue(
+ HttpSendFailure source, HttpSendFailure other)
+ {
+ var result = source.Equals((object)other);
+ Assert.True(result);
+ }
+
+ [Theory]
+ [MemberData(nameof(UnequalTestData))]
+ public static void EqualsWithObject_SourceIsNotEqualToOther_ExpectFalse(
+ HttpSendFailure source, HttpSendFailure other)
+ {
+ var result = source.Equals((object)other);
+ Assert.False(result);
+ }
+
+ [Fact]
+ public static void EqualsWithObject_ObjectIsNull_ExpectFalse()
+ {
+ object? other = null;
+
+ var result = default(HttpSendFailure).Equals(other);
+ Assert.False(result);
+ }
+
+ [Fact]
+ public static void EqualsWithObject_ObjectHasAnotherType_ExpectFalse()
+ {
+ var result = default(HttpSendFailure).Equals(new object());
+ Assert.False(result);
+ }
+}
diff --git a/src/Api.Test/Test.HttpSendFailure/Test.Equality.GetHashCode.cs b/src/Api.Test/Test.HttpSendFailure/Test.Equality.GetHashCode.cs
new file mode 100644
index 0000000..0facfdf
--- /dev/null
+++ b/src/Api.Test/Test.HttpSendFailure/Test.Equality.GetHashCode.cs
@@ -0,0 +1,17 @@
+using Xunit;
+
+namespace GarageGroup.Infra.Http.Api.Test;
+
+partial class HttpSendFailureTest
+{
+ [Theory]
+ [MemberData(nameof(EqualTestData))]
+ public static void GetHashCode_SourceIsEqualToOther_ExpectEqualHashCodes(
+ HttpSendFailure source, HttpSendFailure other)
+ {
+ var sourceHashCode = source.GetHashCode();
+ var otherHashCode = other.GetHashCode();
+
+ Assert.Equal(sourceHashCode, otherHashCode);
+ }
+}
diff --git a/src/Api.Test/Test.HttpSendFailure/Test.Equality.Operator.Equality.cs b/src/Api.Test/Test.HttpSendFailure/Test.Equality.Operator.Equality.cs
new file mode 100644
index 0000000..e35b4ce
--- /dev/null
+++ b/src/Api.Test/Test.HttpSendFailure/Test.Equality.Operator.Equality.cs
@@ -0,0 +1,24 @@
+using Xunit;
+
+namespace GarageGroup.Infra.Http.Api.Test;
+
+partial class HttpSendFailureTest
+{
+ [Theory]
+ [MemberData(nameof(EqualTestData))]
+ public static void EqualityOperator_SourceIsEqualToOther_ExpectTrue(
+ HttpSendFailure source, HttpSendFailure other)
+ {
+ var result = source == other;
+ Assert.True(result);
+ }
+
+ [Theory]
+ [MemberData(nameof(UnequalTestData))]
+ public static void EqualityOperator_SourceIsNotEqualToOther_ExpectFalse(
+ HttpSendFailure source, HttpSendFailure other)
+ {
+ var result = source == other;
+ Assert.False(result);
+ }
+}
diff --git a/src/Api.Test/Test.HttpSendFailure/Test.Equality.Operator.Inequality.cs b/src/Api.Test/Test.HttpSendFailure/Test.Equality.Operator.Inequality.cs
new file mode 100644
index 0000000..a98e28c
--- /dev/null
+++ b/src/Api.Test/Test.HttpSendFailure/Test.Equality.Operator.Inequality.cs
@@ -0,0 +1,24 @@
+using Xunit;
+
+namespace GarageGroup.Infra.Http.Api.Test;
+
+partial class HttpSendFailureTest
+{
+ [Theory]
+ [MemberData(nameof(EqualTestData))]
+ public static void InequalityOperator_SourceIsEqualToOther_ExpectFalse(
+ HttpSendFailure source, HttpSendFailure other)
+ {
+ var result = source != other;
+ Assert.False(result);
+ }
+
+ [Theory]
+ [MemberData(nameof(UnequalTestData))]
+ public static void InequalityOperator_SourceIsNotEqualToOther_ExpectTrue(
+ HttpSendFailure source, HttpSendFailure other)
+ {
+ var result = source != other;
+ Assert.True(result);
+ }
+}
diff --git a/src/Api.Test/Test.HttpSendFailure/Test.ToStandardFailure.cs b/src/Api.Test/Test.HttpSendFailure/Test.ToStandardFailure.cs
new file mode 100644
index 0000000..181eabb
--- /dev/null
+++ b/src/Api.Test/Test.HttpSendFailure/Test.ToStandardFailure.cs
@@ -0,0 +1,75 @@
+using System;
+using Xunit;
+
+namespace GarageGroup.Infra.Http.Api.Test;
+
+partial class HttpSendFailureTest
+{
+ [Fact]
+ public static void ToStandardFailure_BaseMessageIsDefaultNoReasonNoBody_ExpectMessageWithStatusCode()
+ {
+ var source = new HttpSendFailure
+ {
+ StatusCode = HttpFailureCode.BadRequest
+ };
+
+ var actual = source.ToStandardFailure();
+ var expected = Failure.Create(HttpFailureCode.BadRequest, "An unexpected http failure occured: 400.");
+
+ Assert.Equal(expected, actual);
+ }
+
+ [Fact]
+ public static void ToStandardFailure_BaseMessageIsWhiteSpaceAndReasonPresentNoBody_ExpectMessageWithoutBaseMessage()
+ {
+ var source = new HttpSendFailure
+ {
+ StatusCode = HttpFailureCode.NotFound,
+ ReasonPhrase = "Not Found"
+ };
+
+ var actual = source.ToStandardFailure(" \n\r ");
+ var expected = Failure.Create(HttpFailureCode.NotFound, "404 Not Found.");
+
+ Assert.Equal(expected, actual);
+ }
+
+ [Fact]
+ public static void ToStandardFailure_BaseMessageAndReasonAndBodyPresent_ExpectMessageWithBody()
+ {
+ var source = new HttpSendFailure
+ {
+ StatusCode = HttpFailureCode.BadGateway,
+ ReasonPhrase = "Bad Gateway",
+ Body = new()
+ {
+ Type = new("application/json", "utf-8"),
+ Content = new("{\"error\":\"proxy\"}")
+ }
+ };
+
+ var actual = source.ToStandardFailure("Failed to execute request:");
+ var expected = Failure.Create(HttpFailureCode.BadGateway, "Failed to execute request: 502 Bad Gateway.\n{\"error\":\"proxy\"}");
+
+ Assert.Equal(expected, actual);
+ }
+
+ [Fact]
+ public static void ToStandardFailure_ReasonIsWhiteSpaceBodyPresent_ExpectMessageWithoutReason()
+ {
+ var source = new HttpSendFailure
+ {
+ StatusCode = HttpFailureCode.ServiceUnavailable,
+ ReasonPhrase = " ",
+ Body = new()
+ {
+ Content = new("Some error body")
+ }
+ };
+
+ var actual = source.ToStandardFailure("Request failed:");
+ var expected = Failure.Create(HttpFailureCode.ServiceUnavailable, "Request failed: 503.\nSome error body");
+
+ Assert.Equal(expected, actual);
+ }
+}
diff --git a/src/Api.Test/Test.HttpSendIn/HttpSendInTest.cs b/src/Api.Test/Test.HttpSendIn/HttpSendInTest.cs
index f4851d1..3d5bb0c 100644
--- a/src/Api.Test/Test.HttpSendIn/HttpSendInTest.cs
+++ b/src/Api.Test/Test.HttpSendIn/HttpSendInTest.cs
@@ -16,10 +16,10 @@ public static TheoryData EqualTestData
method: HttpVerb.Get,
requestUri: null!)
},
- {
- new(
- method: HttpVerb.Post,
- requestUri: "https://www.example.com/about")
+ {
+ new(
+ method: HttpVerb.Post,
+ requestUri: "https://www.example.com/about")
{
SuccessType = HttpSuccessType.OnlyHeaders
},
@@ -31,10 +31,10 @@ public static TheoryData EqualTestData
}
},
{
- new(
- method: HttpVerb.Head,
- requestUri: "/services/web-development")
- {
+ new(
+ method: HttpVerb.Head,
+ requestUri: "/services/web-development")
+ {
Headers =
[
new("x-ms-date", "Wed, 03 Jul 2024 14:41:12 GMT"),
@@ -52,12 +52,33 @@ public static TheoryData EqualTestData
new("x-ms-version", "2019-02-02"),
new("accept", "application/json;odata=nometadata")
]
- }
- },
- {
- new(
- method: HttpVerb.Delete,
- requestUri: "http://www.example.com/blog/post-title")
+ }
+ },
+ {
+ new(
+ method: HttpVerb.Get,
+ requestUri: "https://www.example.com/about")
+ {
+ Headers =
+ [
+ new(" ", "some value"),
+ new("x-ms-date", "Wed, 03 Jul 2024 14:41:12 GMT")
+ ]
+ },
+ new(
+ method: HttpVerb.Get,
+ requestUri: "https://www.example.com/about")
+ {
+ Headers =
+ [
+ new("x-ms-date", "Wed, 03 Jul 2024 14:41:12 GMT")
+ ]
+ }
+ },
+ {
+ new(
+ method: HttpVerb.Delete,
+ requestUri: "http://www.example.com/blog/post-title")
{
Headers =
[
@@ -170,12 +191,20 @@ public static TheoryData UnequalTestData
method: HttpVerb.Get,
requestUri: "https://www.example.com/About")
},
- {
- new(
- method: HttpVerb.Post,
- requestUri: "https://www.example.com/about"),
- new(
- method: HttpVerb.Post,
+ {
+ new(
+ method: HttpVerb.Get,
+ requestUri: "https://www.example.com/about"),
+ new(
+ method: HttpVerb.Post,
+ requestUri: "https://www.example.com/about")
+ },
+ {
+ new(
+ method: HttpVerb.Post,
+ requestUri: "https://www.example.com/about"),
+ new(
+ method: HttpVerb.Post,
requestUri: "https://www.example.com/about")
{
SuccessType = HttpSuccessType.OnlyHeaders
@@ -264,8 +293,29 @@ public static TheoryData UnequalTestData
{
Type = new("SomeType", "Some CharSet"),
Content = new("Some text")
- }
- }
- }
- };
-}
\ No newline at end of file
+ }
+ }
+ }
+ };
+
+ public static TheoryData NullableEqualTestData
+ =>
+ new()
+ {
+ { null, null }
+ };
+
+ public static TheoryData NullableUnequalTestData
+ =>
+ new()
+ {
+ {
+ null,
+ new(HttpVerb.Get, "https://www.example.com/about")
+ },
+ {
+ new(HttpVerb.Get, "https://www.example.com/about"),
+ null
+ }
+ };
+}
diff --git a/src/Api.Test/Test.HttpSendIn/Test.Constructor.cs b/src/Api.Test/Test.HttpSendIn/Test.Constructor.cs
new file mode 100644
index 0000000..a7748ad
--- /dev/null
+++ b/src/Api.Test/Test.HttpSendIn/Test.Constructor.cs
@@ -0,0 +1,30 @@
+using Xunit;
+
+namespace GarageGroup.Infra.Http.Api.Test;
+
+partial class HttpSendInTest
+{
+ [Fact]
+ public static void Constructor_ExpectMethodAssigned()
+ {
+ var source = new HttpSendIn(HttpVerb.Post, "https://www.example.com/about");
+
+ Assert.Equal(HttpVerb.Post, source.Method);
+ }
+
+ [Fact]
+ public static void Constructor_RequestUriIsNull_ExpectEmptyString()
+ {
+ var source = new HttpSendIn(HttpVerb.Get, null!);
+
+ Assert.Equal(string.Empty, source.RequestUri);
+ }
+
+ [Fact]
+ public static void Constructor_RequestUriHasSurroundingWhiteSpace_ExpectTrimmed()
+ {
+ var source = new HttpSendIn(HttpVerb.Get, " /api/items ");
+
+ Assert.Equal("/api/items", source.RequestUri);
+ }
+}
diff --git a/src/Api.Test/Test.HttpSendIn/Test.Equality.Equals.cs b/src/Api.Test/Test.HttpSendIn/Test.Equality.Equals.cs
index 0bba596..ebaa09f 100644
--- a/src/Api.Test/Test.HttpSendIn/Test.Equality.Equals.cs
+++ b/src/Api.Test/Test.HttpSendIn/Test.Equality.Equals.cs
@@ -2,13 +2,22 @@
namespace GarageGroup.Infra.Http.Api.Test;
-partial class HttpSendInTest
-{
- [Fact]
- public static void Equals_OtherIsNull_ExpectFalse()
- {
- var source = new HttpSendIn(HttpVerb.Get, "https://www.example.com/about");
- var result = source.Equals(null);
+partial class HttpSendInTest
+{
+ [Fact]
+ public static void Equals_OtherHasSameReference_ExpectTrue()
+ {
+ var source = new HttpSendIn(HttpVerb.Get, "https://www.example.com/about");
+
+ var result = source.Equals(source);
+ Assert.True(result);
+ }
+
+ [Fact]
+ public static void Equals_OtherIsNull_ExpectFalse()
+ {
+ var source = new HttpSendIn(HttpVerb.Get, "https://www.example.com/about");
+ var result = source.Equals(null);
Assert.False(result);
}
@@ -30,4 +39,4 @@ public static void Equals_SourceIsNotEqualToOther_ExpectFalse(
var result = source.Equals(other);
Assert.False(result);
}
-}
\ No newline at end of file
+}
diff --git a/src/Api.Test/Test.HttpSendIn/Test.Equality.Operator.Equality.cs b/src/Api.Test/Test.HttpSendIn/Test.Equality.Operator.Equality.cs
index bedf651..bfaa752 100644
--- a/src/Api.Test/Test.HttpSendIn/Test.Equality.Operator.Equality.cs
+++ b/src/Api.Test/Test.HttpSendIn/Test.Equality.Operator.Equality.cs
@@ -15,10 +15,28 @@ public static void EqualityOperator_SourceIsEqualToOther_ExpectTrue(
[Theory]
[MemberData(nameof(UnequalTestData))]
- public static void EqualityOperator_SourceIsNotEqualToOther_ExpectFalse(
- HttpSendIn source, HttpSendIn other)
- {
- var result = source == other;
- Assert.False(result);
- }
-}
\ No newline at end of file
+ public static void EqualityOperator_SourceIsNotEqualToOther_ExpectFalse(
+ HttpSendIn source, HttpSendIn other)
+ {
+ var result = source == other;
+ Assert.False(result);
+ }
+
+ [Theory]
+ [MemberData(nameof(NullableEqualTestData))]
+ public static void EqualityOperator_SourceIsNullEqualToOther_ExpectTrue(
+ HttpSendIn? source, HttpSendIn? other)
+ {
+ var result = source == other;
+ Assert.True(result);
+ }
+
+ [Theory]
+ [MemberData(nameof(NullableUnequalTestData))]
+ public static void EqualityOperator_SourceIsNullNotEqualToOther_ExpectFalse(
+ HttpSendIn? source, HttpSendIn? other)
+ {
+ var result = source == other;
+ Assert.False(result);
+ }
+}
diff --git a/src/Api.Test/Test.HttpSendIn/Test.Equality.Operator.Inequality.cs b/src/Api.Test/Test.HttpSendIn/Test.Equality.Operator.Inequality.cs
index a6fcf1f..5a19a03 100644
--- a/src/Api.Test/Test.HttpSendIn/Test.Equality.Operator.Inequality.cs
+++ b/src/Api.Test/Test.HttpSendIn/Test.Equality.Operator.Inequality.cs
@@ -15,10 +15,28 @@ public static void InequalityOperator_SourceIsEqualToOther_ExpectFalse(
[Theory]
[MemberData(nameof(UnequalTestData))]
- public static void InequalityOperator_SourceIsNotEqualToOther_ExpectTrue(
- HttpSendIn source, HttpSendIn other)
- {
- var result = source != other;
- Assert.True(result);
- }
-}
\ No newline at end of file
+ public static void InequalityOperator_SourceIsNotEqualToOther_ExpectTrue(
+ HttpSendIn source, HttpSendIn other)
+ {
+ var result = source != other;
+ Assert.True(result);
+ }
+
+ [Theory]
+ [MemberData(nameof(NullableEqualTestData))]
+ public static void InequalityOperator_SourceIsNullEqualToOther_ExpectFalse(
+ HttpSendIn? source, HttpSendIn? other)
+ {
+ var result = source != other;
+ Assert.False(result);
+ }
+
+ [Theory]
+ [MemberData(nameof(NullableUnequalTestData))]
+ public static void InequalityOperator_SourceIsNullNotEqualToOther_ExpectTrue(
+ HttpSendIn? source, HttpSendIn? other)
+ {
+ var result = source != other;
+ Assert.True(result);
+ }
+}
diff --git a/src/Api.Test/Test.HttpVerb/HttpVerbTest.cs b/src/Api.Test/Test.HttpVerb/HttpVerbTest.cs
new file mode 100644
index 0000000..549b67d
--- /dev/null
+++ b/src/Api.Test/Test.HttpVerb/HttpVerbTest.cs
@@ -0,0 +1,40 @@
+using Xunit;
+
+namespace GarageGroup.Infra.Http.Api.Test;
+
+public static partial class HttpVerbTest
+{
+ public static TheoryData EqualTestData
+ =>
+ new()
+ {
+ { HttpVerb.Get, HttpVerb.From("GET") },
+ { HttpVerb.Get, HttpVerb.From("get") },
+ { HttpVerb.Patch, HttpVerb.From("patch") },
+ { HttpVerb.Get, HttpVerb.From("\n\r") }
+ };
+
+ public static TheoryData UnequalTestData
+ =>
+ new()
+ {
+ { HttpVerb.Get, HttpVerb.Post },
+ { HttpVerb.Head, HttpVerb.Delete },
+ { HttpVerb.From("custom"), HttpVerb.Get }
+ };
+
+ public static TheoryData NullableEqualTestData
+ =>
+ new()
+ {
+ { null, null }
+ };
+
+ public static TheoryData NullableUnequalTestData
+ =>
+ new()
+ {
+ { null, HttpVerb.Get },
+ { HttpVerb.Post, null }
+ };
+}
diff --git a/src/Api.Test/Test.HttpVerb/Test.Equality.Equals.cs b/src/Api.Test/Test.HttpVerb/Test.Equality.Equals.cs
new file mode 100644
index 0000000..3b6fca0
--- /dev/null
+++ b/src/Api.Test/Test.HttpVerb/Test.Equality.Equals.cs
@@ -0,0 +1,33 @@
+using Xunit;
+
+namespace GarageGroup.Infra.Http.Api.Test;
+
+partial class HttpVerbTest
+{
+ [Theory]
+ [MemberData(nameof(EqualTestData))]
+ public static void Equals_SourceIsEqualToOther_ExpectTrue(
+ HttpVerb source, HttpVerb other)
+ {
+ var result = source.Equals(other);
+ Assert.True(result);
+ }
+
+ [Theory]
+ [MemberData(nameof(UnequalTestData))]
+ public static void Equals_SourceIsNotEqualToOther_ExpectFalse(
+ HttpVerb source, HttpVerb other)
+ {
+ var result = source.Equals(other);
+ Assert.False(result);
+ }
+
+ [Fact]
+ public static void Equals_OtherIsNull_ExpectFalse()
+ {
+ HttpVerb? other = null;
+
+ var result = HttpVerb.Get.Equals(other);
+ Assert.False(result);
+ }
+}
diff --git a/src/Api.Test/Test.HttpVerb/Test.Equality.EqualsWithObject.cs b/src/Api.Test/Test.HttpVerb/Test.Equality.EqualsWithObject.cs
new file mode 100644
index 0000000..c43c96e
--- /dev/null
+++ b/src/Api.Test/Test.HttpVerb/Test.Equality.EqualsWithObject.cs
@@ -0,0 +1,31 @@
+using Xunit;
+
+namespace GarageGroup.Infra.Http.Api.Test;
+
+partial class HttpVerbTest
+{
+ [Theory]
+ [MemberData(nameof(EqualTestData))]
+ public static void EqualsWithObject_SourceIsEqualToOther_ExpectTrue(
+ HttpVerb source, HttpVerb other)
+ {
+ var result = source.Equals((object)other);
+ Assert.True(result);
+ }
+
+ [Theory]
+ [MemberData(nameof(UnequalTestData))]
+ public static void EqualsWithObject_SourceIsNotEqualToOther_ExpectFalse(
+ HttpVerb source, HttpVerb other)
+ {
+ var result = source.Equals((object)other);
+ Assert.False(result);
+ }
+
+ [Fact]
+ public static void EqualsWithObject_OtherHasAnotherType_ExpectFalse()
+ {
+ var result = HttpVerb.Get.Equals(new object());
+ Assert.False(result);
+ }
+}
diff --git a/src/Api.Test/Test.HttpVerb/Test.Equality.GetHashCode.cs b/src/Api.Test/Test.HttpVerb/Test.Equality.GetHashCode.cs
new file mode 100644
index 0000000..9e0359b
--- /dev/null
+++ b/src/Api.Test/Test.HttpVerb/Test.Equality.GetHashCode.cs
@@ -0,0 +1,17 @@
+using Xunit;
+
+namespace GarageGroup.Infra.Http.Api.Test;
+
+partial class HttpVerbTest
+{
+ [Theory]
+ [MemberData(nameof(EqualTestData))]
+ public static void GetHashCode_SourceIsEqualToOther_ExpectEqualHashes(
+ HttpVerb source, HttpVerb other)
+ {
+ var sourceHash = source.GetHashCode();
+ var otherHash = other.GetHashCode();
+
+ Assert.Equal(sourceHash, otherHash);
+ }
+}
diff --git a/src/Api.Test/Test.HttpVerb/Test.Equality.Operator.Equality.cs b/src/Api.Test/Test.HttpVerb/Test.Equality.Operator.Equality.cs
new file mode 100644
index 0000000..7c879c9
--- /dev/null
+++ b/src/Api.Test/Test.HttpVerb/Test.Equality.Operator.Equality.cs
@@ -0,0 +1,42 @@
+using Xunit;
+
+namespace GarageGroup.Infra.Http.Api.Test;
+
+partial class HttpVerbTest
+{
+ [Theory]
+ [MemberData(nameof(EqualTestData))]
+ public static void EqualityOperator_SourceIsEqualToOther_ExpectTrue(
+ HttpVerb source, HttpVerb other)
+ {
+ var result = source == other;
+ Assert.True(result);
+ }
+
+ [Theory]
+ [MemberData(nameof(UnequalTestData))]
+ public static void EqualityOperator_SourceIsNotEqualToOther_ExpectFalse(
+ HttpVerb source, HttpVerb other)
+ {
+ var result = source == other;
+ Assert.False(result);
+ }
+
+ [Theory]
+ [MemberData(nameof(NullableEqualTestData))]
+ public static void EqualityOperator_SourceIsNullEqualToOther_ExpectTrue(
+ HttpVerb? source, HttpVerb? other)
+ {
+ var result = source == other;
+ Assert.True(result);
+ }
+
+ [Theory]
+ [MemberData(nameof(NullableUnequalTestData))]
+ public static void EqualityOperator_SourceIsNullNotEqualToOther_ExpectFalse(
+ HttpVerb? source, HttpVerb? other)
+ {
+ var result = source == other;
+ Assert.False(result);
+ }
+}
diff --git a/src/Api.Test/Test.HttpVerb/Test.Equality.Operator.Inequality.cs b/src/Api.Test/Test.HttpVerb/Test.Equality.Operator.Inequality.cs
new file mode 100644
index 0000000..18cae7a
--- /dev/null
+++ b/src/Api.Test/Test.HttpVerb/Test.Equality.Operator.Inequality.cs
@@ -0,0 +1,42 @@
+using Xunit;
+
+namespace GarageGroup.Infra.Http.Api.Test;
+
+partial class HttpVerbTest
+{
+ [Theory]
+ [MemberData(nameof(EqualTestData))]
+ public static void InequalityOperator_SourceIsEqualToOther_ExpectFalse(
+ HttpVerb source, HttpVerb other)
+ {
+ var result = source != other;
+ Assert.False(result);
+ }
+
+ [Theory]
+ [MemberData(nameof(UnequalTestData))]
+ public static void InequalityOperator_SourceIsNotEqualToOther_ExpectTrue(
+ HttpVerb source, HttpVerb other)
+ {
+ var result = source != other;
+ Assert.True(result);
+ }
+
+ [Theory]
+ [MemberData(nameof(NullableEqualTestData))]
+ public static void InequalityOperator_SourceIsNullEqualToOther_ExpectFalse(
+ HttpVerb? source, HttpVerb? other)
+ {
+ var result = source != other;
+ Assert.False(result);
+ }
+
+ [Theory]
+ [MemberData(nameof(NullableUnequalTestData))]
+ public static void InequalityOperator_SourceIsNullNotEqualToOther_ExpectTrue(
+ HttpVerb? source, HttpVerb? other)
+ {
+ var result = source != other;
+ Assert.True(result);
+ }
+}
diff --git a/src/Api.Test/Test.HttpVerb/Test.From.cs b/src/Api.Test/Test.HttpVerb/Test.From.cs
new file mode 100644
index 0000000..3b9e6b3
--- /dev/null
+++ b/src/Api.Test/Test.HttpVerb/Test.From.cs
@@ -0,0 +1,27 @@
+using Xunit;
+
+namespace GarageGroup.Infra.Http.Api.Test;
+
+partial class HttpVerbTest
+{
+ [Fact]
+ public static void From_NameIsNull_ExpectNameIsGet()
+ {
+ var result = HttpVerb.From(null!);
+ Assert.Equal(HttpVerb.Get.Name, result.Name);
+ }
+
+ [Fact]
+ public static void From_NameIsWhiteSpace_ExpectNameIsGet()
+ {
+ var result = HttpVerb.From(" \n\r ");
+ Assert.Equal(HttpVerb.Get.Name, result.Name);
+ }
+
+ [Fact]
+ public static void From_NameHasLowerCase_ExpectNameIsUpperCase()
+ {
+ var result = HttpVerb.From("test");
+ Assert.Equal("TEST", result.Name);
+ }
+}
diff --git a/src/Api.Test/Test.HttpVerb/Test.ToString.cs b/src/Api.Test/Test.HttpVerb/Test.ToString.cs
new file mode 100644
index 0000000..10efcc2
--- /dev/null
+++ b/src/Api.Test/Test.HttpVerb/Test.ToString.cs
@@ -0,0 +1,18 @@
+using Xunit;
+
+namespace GarageGroup.Infra.Http.Api.Test;
+
+partial class HttpVerbTest
+{
+ [Theory]
+ [InlineData("GET")]
+ [InlineData("POST")]
+ [InlineData("PATCH")]
+ public static void ToString_ExpectName(string verbName)
+ {
+ var source = HttpVerb.From(verbName.ToLowerInvariant());
+
+ var result = source.ToString();
+ Assert.Equal(verbName, result);
+ }
+}
diff --git a/src/Api/Api.csproj b/src/Api/Api.csproj
index b22cf66..fe3f522 100644
--- a/src/Api/Api.csproj
+++ b/src/Api/Api.csproj
@@ -1,7 +1,7 @@
- net8.0;net9.0
+ net10.0
disable
enable
true
@@ -9,12 +9,21 @@
$(NoWarn);IDE0130;CA1859
GarageGroup.Infra
GarageGroup.Infra.Http.Api
- 0.3.0
+ 1.0.0
-
-
+
+
+
+
+
+ <_Parameter1>GarageGroup.Infra.Http.Api.Test
+
+
+
+
+
diff --git a/src/Api/Api/Api.Send.cs b/src/Api/Api/Api.Send.cs
index 8c2a3e3..f6823bd 100644
--- a/src/Api/Api/Api.Send.cs
+++ b/src/Api/Api/Api.Send.cs
@@ -7,20 +7,10 @@ namespace GarageGroup.Infra;
partial class HttpApi
{
- public ValueTask> SendAsync(HttpSendIn input, CancellationToken cancellationToken)
+ public async ValueTask> SendAsync(HttpSendIn input, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(input);
- if (cancellationToken.IsCancellationRequested)
- {
- return ValueTask.FromCanceled>(cancellationToken);
- }
-
- return InnerSendAsync(input, cancellationToken);
- }
-
- private async ValueTask> InnerSendAsync(HttpSendIn input, CancellationToken cancellationToken)
- {
using var httpRequest = BuildHttpRequest(input);
using var httpResponse = await httpClient.SendAsync(httpRequest, cancellationToken).ConfigureAwait(false);
@@ -37,16 +27,14 @@ private async ValueTask> InnerSendAsync(Htt
Body = response.Body
};
}
- else
+
+ return new HttpSendFailure
{
- return new HttpSendFailure
- {
- StatusCode = (HttpFailureCode)response.StatusCode,
- ReasonPhrase = response.ReasonPhrase,
- Headers = response.Headers,
- Body = response.Body
- };
- }
+ StatusCode = (HttpFailureCode)response.StatusCode,
+ ReasonPhrase = response.ReasonPhrase,
+ Headers = response.Headers,
+ Body = response.Body
+ };
}
private static HttpRequestMessage BuildHttpRequest(HttpSendIn input)
diff --git a/src/Api/Api/HttpApi.cs b/src/Api/Api/HttpApi.cs
index 9d016f3..109c245 100644
--- a/src/Api/Api/HttpApi.cs
+++ b/src/Api/Api/HttpApi.cs
@@ -42,9 +42,10 @@ private static void SetHeaders(HttpRequestMessage httpRequest, FlatArray ReadBodyAsync(HttpContent httpContent, Cance
}
private sealed record class HttpOut(int StatusCode, string? ReasonPhrase, FlatArray> Headers, HttpBody Body);
-}
\ No newline at end of file
+}
diff --git a/src/Api/HttpApiDependency.cs b/src/Api/HttpApiDependency.cs
index db9ff61..9396efe 100644
--- a/src/Api/HttpApiDependency.cs
+++ b/src/Api/HttpApiDependency.cs
@@ -1,7 +1,8 @@
-using System;
-using System.Net.Http;
-using Microsoft.Extensions.Configuration;
-using PrimeFuncPack;
+using System;
+using System.Globalization;
+using System.Net.Http;
+using Microsoft.Extensions.Configuration;
+using PrimeFuncPack;
namespace GarageGroup.Infra;
@@ -86,11 +87,11 @@ HttpApi ResolveApi(IServiceProvider serviceProvider, HttpMessageHandler httpMess
return null;
}
- if (TimeSpan.TryParse(value, out var result))
- {
- return result;
- }
+ if (TimeSpan.TryParse(value, CultureInfo.InvariantCulture, out var result))
+ {
+ return result;
+ }
throw new InvalidOperationException($"Section '{section.Path}' key '{key}' value '{value}' must be a valid TimeSpan value.");
}
-}
\ No newline at end of file
+}
diff --git a/src/Infra.Http.Api.sln b/src/Infra.Http.Api.sln
deleted file mode 100644
index f286654..0000000
--- a/src/Infra.Http.Api.sln
+++ /dev/null
@@ -1,37 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 17
-VisualStudioVersion = 17.5.002.0
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Api.Test", "Api.Test\Api.Test.csproj", "{5CE0128F-B405-4F9C-874B-C8355A6CD6BA}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Api.Contract", "Api.Contract\Api.Contract.csproj", "{71E36871-9D43-4CC5-A191-3CF9B1DDAD0B}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Api", "Api\Api.csproj", "{DD25FFE8-2BFD-4358-A1A2-92D89D799628}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {5CE0128F-B405-4F9C-874B-C8355A6CD6BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {5CE0128F-B405-4F9C-874B-C8355A6CD6BA}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {5CE0128F-B405-4F9C-874B-C8355A6CD6BA}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {5CE0128F-B405-4F9C-874B-C8355A6CD6BA}.Release|Any CPU.Build.0 = Release|Any CPU
- {71E36871-9D43-4CC5-A191-3CF9B1DDAD0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {71E36871-9D43-4CC5-A191-3CF9B1DDAD0B}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {71E36871-9D43-4CC5-A191-3CF9B1DDAD0B}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {71E36871-9D43-4CC5-A191-3CF9B1DDAD0B}.Release|Any CPU.Build.0 = Release|Any CPU
- {DD25FFE8-2BFD-4358-A1A2-92D89D799628}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {DD25FFE8-2BFD-4358-A1A2-92D89D799628}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {DD25FFE8-2BFD-4358-A1A2-92D89D799628}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {DD25FFE8-2BFD-4358-A1A2-92D89D799628}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {6EC80F57-CE6F-4372-8624-BA75FB6F0732}
- EndGlobalSection
-EndGlobal
diff --git a/src/Infra.Http.Api.slnx b/src/Infra.Http.Api.slnx
new file mode 100644
index 0000000..cdf48ee
--- /dev/null
+++ b/src/Infra.Http.Api.slnx
@@ -0,0 +1,5 @@
+
+
+
+
+