From ba4d653fe41937939962aea93cfadf609d60338d Mon Sep 17 00:00:00 2001 From: Joshua Harms Date: Fri, 17 Jan 2025 17:13:04 -0600 Subject: [PATCH] fix: add unit test to verify correct content disposal behavior See #1080, #1129 --- .../Services/ShopifyServiceTests.cs | 73 ++++++++++++++++++- ...rviceWithExposedExecuteRequestCoreAsync.cs | 20 +++++ 2 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 ShopifySharp.Tests/TestClasses/TestShopifyServiceWithExposedExecuteRequestCoreAsync.cs diff --git a/ShopifySharp.Tests/Services/ShopifyServiceTests.cs b/ShopifySharp.Tests/Services/ShopifyServiceTests.cs index 8dc525861..b334f7e1d 100644 --- a/ShopifySharp.Tests/Services/ShopifyServiceTests.cs +++ b/ShopifySharp.Tests/Services/ShopifyServiceTests.cs @@ -1,15 +1,32 @@ +#nullable enable using System; +using System.Net; +using System.Net.Http; +using System.Threading.Tasks; +using FakeItEasy; using FluentAssertions; using JetBrains.Annotations; +using ShopifySharp.Infrastructure; +using ShopifySharp.Infrastructure.Policies.ExponentialRetry; using ShopifySharp.Tests.TestClasses; using Xunit; namespace ShopifySharp.Tests.Services; -[Trait("Category", "ShopifyService")] +[Trait("Category", "ShopifyService"), Trait("Category", "DotNetFramework")] [TestSubject(typeof(ShopifyService))] public class ShopifyServiceTests { + public static HttpMethod[] MakeHttpMethodList() => [HttpMethod.Get, HttpMethod.Post, HttpMethod.Put, HttpMethod.Delete, HttpMethod.Head]; + + public static Func[] MakeFakedExecutionPoliciesList() => + [ + () => A.Fake(x => x.Wrapping(new DefaultRequestExecutionPolicy())), + () => A.Fake(x => x.Wrapping(new RetryExecutionPolicy())), + () => A.Fake(x => x.Wrapping(new LeakyBucketExecutionPolicy())), + () => A.Fake(x => x.Wrapping(new ExponentialRetryPolicy(ExponentialRetryPolicyOptions.Default()))) + ]; + #region PrepareRequest(string path) and BuildRequestUri [Fact] @@ -27,4 +44,58 @@ public void BuildRequestUri_ShouldReturnOverridenUri() } #endregion + + #region internal ExecuteRequestCoreAsync + + [Theory] + [CombinatorialData] + public async Task Internal_ExecuteRequestCoreAsync_WhenRunningTheExecutionPolicy_ShouldNotDisposeTheBaseRequestMessageContent( + [CombinatorialMemberData(nameof(MakeHttpMethodList))] HttpMethod httpMethod, + [CombinatorialMemberData(nameof(MakeFakedExecutionPoliciesList))] Func makePolicy + ) + { + // Setup + var sut = new TestShopifyServiceWithExposedExecuteRequestCoreAsync(); + var handler = A.Fake(); + var content = A.Fake(x => x.Wrapping(new StringContent("""{"foo":"bar"}"""))); + var policy = makePolicy(); + + sut.SetExecutionPolicy(policy); + sut.SetHttpClient(new HttpClient(handler)); + + var callToHandler = A.CallTo(handler) + .WithReturnType>() + .Where(call => call.Method.Name == "SendAsync"); + var callToPolicy = A.CallTo(policy) + .WithReturnType>>() + .Where(call => call.Method.Name == "Run"); + var callToDisposeContent = A.CallTo(content) + .WithVoidReturnType() + .Where(call => call.Method.Name == "Dispose"); + + callToHandler.ReturnsLazily(() => new HttpResponseMessage + { + Content = new JsonContent(new { products = true }), + StatusCode = HttpStatusCode.OK + }); + + // Act + var act = async () => await sut.ExecuteRequestCoreAsync( + new RequestUri(new Uri("https://example.com")), + httpMethod, + content, + [], + null); + + // Assert + var result = await act.Should().NotThrowAsync(); + + result.Subject.Result.Should().Be("""{"products":true}"""); + + callToPolicy.MustHaveHappenedOnceExactly(); + callToHandler.MustHaveHappenedOnceExactly(); + callToDisposeContent.MustHaveHappenedOnceExactly(); + } + + #endregion } diff --git a/ShopifySharp.Tests/TestClasses/TestShopifyServiceWithExposedExecuteRequestCoreAsync.cs b/ShopifySharp.Tests/TestClasses/TestShopifyServiceWithExposedExecuteRequestCoreAsync.cs new file mode 100644 index 000000000..2c0f317bf --- /dev/null +++ b/ShopifySharp.Tests/TestClasses/TestShopifyServiceWithExposedExecuteRequestCoreAsync.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using ShopifySharp.Infrastructure; + +namespace ShopifySharp.Tests.TestClasses; + +public class TestShopifyServiceWithExposedExecuteRequestCoreAsync() + : ShopifyService("some-shop-domain", "some-access-token") +{ + public new Task> ExecuteRequestCoreAsync( + RequestUri uri, + HttpMethod method, + HttpContent content, + Dictionary headers, + int? graphqlQueryCost, + CancellationToken cancellationToken = default + ) => base.ExecuteRequestCoreAsync(uri, method, content, headers, graphqlQueryCost, cancellationToken); +}