From 576bcb672ebc44a07ca295cd969279251d89f569 Mon Sep 17 00:00:00 2001 From: Paul Welter Date: Wed, 5 Jun 2024 12:56:50 -0500 Subject: [PATCH] fix flaky tests --- test/FluentRest.Tests/FluentEchoTests.cs | 45 +++++++------ test/FluentRest.Tests/FluentRest.Tests.csproj | 2 + .../GitHub/GitHubFactoryTests.cs | 29 ++------- test/FluentRest.Tests/GitHub/GitHubTests.cs | 29 ++++----- test/FluentRest.Tests/GitHub/GithubClient.cs | 1 - .../Google/Maps/GoogleTests.cs | 2 +- test/FluentRest.Tests/HostCollection.cs | 9 +++ test/FluentRest.Tests/HostFixture.cs | 65 +++++++++++++++++++ test/FluentRest.Tests/HostTestBase.cs | 15 +++++ test/FluentRest.Tests/HttpClientEchoTests.cs | 42 +++++++----- test/FluentRest.Tests/QueryBuilderTest.cs | 1 - test/FluentRest.Tests/UrlBuilderTests.cs | 4 -- 12 files changed, 159 insertions(+), 85 deletions(-) create mode 100644 test/FluentRest.Tests/HostCollection.cs create mode 100644 test/FluentRest.Tests/HostFixture.cs create mode 100644 test/FluentRest.Tests/HostTestBase.cs diff --git a/test/FluentRest.Tests/FluentEchoTests.cs b/test/FluentRest.Tests/FluentEchoTests.cs index ebe9e18..b8f586c 100644 --- a/test/FluentRest.Tests/FluentEchoTests.cs +++ b/test/FluentRest.Tests/FluentEchoTests.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.IO; using System.IO.Compression; using System.Net; @@ -7,16 +6,23 @@ using System.Net.Http.Headers; using System.Text; using System.Text.Json; -using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; + using Xunit; +using Xunit.Abstractions; namespace FluentRest.Tests; -public class FluentEchoTests +public class FluentEchoTests : HostTestBase { + public FluentEchoTests(ITestOutputHelper output, HostFixture fixture) + : base(output, fixture) + { + } + [Fact] public async Task EchoGet() { @@ -29,7 +35,7 @@ public async Task EchoGet() ); Assert.NotNull(result); - Assert.Equal("https://httpbin.org/get?page=1&size=10", result.Url); + Assert.Equal($"{Fixture.HttpBinUrl}/get?page=1&size=10", result.Url); Assert.Equal("1", result.QueryString["page"]); Assert.Equal("10", result.QueryString["size"]); } @@ -47,7 +53,7 @@ public async Task EchoGetBearer() ); Assert.NotNull(result); - Assert.Equal("https://httpbin.org/get?page=1&size=10", result.Url); + Assert.Equal($"{Fixture.HttpBinUrl}/get?page=1&size=10", result.Url); Assert.Equal("1", result.QueryString["page"]); Assert.Equal("10", result.QueryString["size"]); @@ -104,7 +110,7 @@ public async Task EchoGetAcceptMultiple() ); Assert.NotNull(result); - Assert.Equal("https://httpbin.org/get?page=10", result.Url); + Assert.Equal($"{Fixture.HttpBinUrl}/get?page=10", result.Url); Assert.Equal("text/xml, application/bson, application/json", result.Headers[HttpRequestHeaders.Accept]); Assert.Equal("testing header", result.Headers["X-Blah"]); } @@ -122,7 +128,7 @@ public async Task EchoPost() ); Assert.NotNull(result); - Assert.Equal("https://httpbin.org/post?page=10", result.Url); + Assert.Equal($"{Fixture.HttpBinUrl}/post?page=10", result.Url); Assert.Equal("Value", result.Form["Test"]); Assert.Equal("value", result.Form["key"]); } @@ -145,7 +151,7 @@ public async Task EchoPostResponse() var result = await response.DeserializeAsync(); Assert.NotNull(result); - Assert.Equal("https://httpbin.org/post?page=10", result.Url); + Assert.Equal($"{Fixture.HttpBinUrl}/post?page=10", result.Url); Assert.Equal("Value", result.Form["Test"]); Assert.Equal("value", result.Form["key"]); } @@ -163,7 +169,7 @@ public async Task EchoPatch() Assert.NotNull(response); - Assert.Equal("https://httpbin.org/patch?page=10", response.Url); + Assert.Equal($"{Fixture.HttpBinUrl}/patch?page=10", response.Url); Assert.Equal("Value", response.Form["Test"]); } @@ -184,7 +190,7 @@ public async Task EchoPatchResponse() var result = await response.DeserializeAsync(); Assert.NotNull(result); - Assert.Equal("https://httpbin.org/patch?page=10", result.Url); + Assert.Equal($"{Fixture.HttpBinUrl}/patch?page=10", result.Url); Assert.Equal("Value", result.Form["Test"]); } @@ -201,7 +207,7 @@ public async Task EchoPut() ); Assert.NotNull(result); - Assert.Equal("https://httpbin.org/put?page=10", result.Url); + Assert.Equal($"{Fixture.HttpBinUrl}/put?page=10", result.Url); Assert.Equal("Value", result.Form["Test"]); Assert.Equal("value", result.Form["key"]); } @@ -238,7 +244,7 @@ public async Task EchoPostData() Assert.True(result.Headers.ContainsKey("Content-Length")); int contentLength = Int32.Parse(result.Headers["Content-Length"]); Assert.True(contentLength > 0); - Assert.Equal("https://httpbin.org/post?page=10", result.Url); + Assert.Equal($"{Fixture.HttpBinUrl}/post?page=10", result.Url); Assert.Equal("application/json; charset=utf-8", result.Headers[HttpRequestHeaders.ContentType]); Assert.True(result.Headers.ContainsKey("Content-Type")); var contentType = result.Headers["Content-Type"]; @@ -359,7 +365,7 @@ public async Task EchoPostDataCustomCompressedContent() Assert.True(result.Headers.ContainsKey("Content-Length")); int contentLength = Int32.Parse(result.Headers["Content-Length"]); Assert.True(contentLength > 0); - Assert.Equal("https://httpbin.org/post?page=10", result.Url); + Assert.Equal($"{Fixture.HttpBinUrl}/post?page=10", result.Url); Assert.Equal("application/json; charset=utf-8", result.Headers[HttpRequestHeaders.ContentType]); Assert.Equal("gzip", result.Headers[HttpRequestHeaders.ContentEncoding]); } @@ -416,7 +422,7 @@ public async Task DefaultPost() ); Assert.NotNull(result); - Assert.Equal("https://httpbin.org/post?page=10", result.Url); + Assert.Equal($"{Fixture.HttpBinUrl}/post?page=10", result.Url); Assert.Equal("Value", result.Form["Test"]); Assert.Equal("value", result.Form["key"]); Assert.Equal("Token abc-def-123", result.Headers["Authorization"]); @@ -434,15 +440,16 @@ public async Task SendRequest() Assert.NotNull(result); Assert.True(result.Headers.ContainsKey("Content-Length")); - Assert.Equal("https://httpbin.org/post", result.Url); + Assert.Equal($"{Fixture.HttpBinUrl}/post", result.Url); } - private static IFluentClient CreateClient() + private IFluentClient CreateClient() { - var httpClient = new HttpClient(); - httpClient.BaseAddress = new Uri("https://httpbin.org/", UriKind.Absolute); + var httpClientFactory = Services.GetService(); + var httpClient = httpClientFactory.CreateClient("HttpBin"); - var fluentClient = new FluentClient(httpClient); + var serializer = Services.GetService(); + var fluentClient = new FluentClient(httpClient, serializer); return fluentClient; } diff --git a/test/FluentRest.Tests/FluentRest.Tests.csproj b/test/FluentRest.Tests/FluentRest.Tests.csproj index 79c7747..44bff00 100644 --- a/test/FluentRest.Tests/FluentRest.Tests.csproj +++ b/test/FluentRest.Tests/FluentRest.Tests.csproj @@ -14,6 +14,8 @@ + + all runtime; build; native; contentfiles; analyzers diff --git a/test/FluentRest.Tests/GitHub/GitHubFactoryTests.cs b/test/FluentRest.Tests/GitHub/GitHubFactoryTests.cs index 3e399d0..d980cc3 100644 --- a/test/FluentRest.Tests/GitHub/GitHubFactoryTests.cs +++ b/test/FluentRest.Tests/GitHub/GitHubFactoryTests.cs @@ -1,6 +1,4 @@ -using System; using System.Collections.Generic; -using System.Text.Json; using System.Threading.Tasks; using FluentRest.Tests.GitHub.Models; @@ -8,35 +6,20 @@ using Microsoft.Extensions.DependencyInjection; using Xunit; +using Xunit.Abstractions; namespace FluentRest.Tests.GitHub; -public class GitHubFactoryTests +public class GitHubFactoryTests : HostTestBase { - public IServiceProvider ServiceProvider { get; } - - public GitHubFactoryTests() + public GitHubFactoryTests(ITestOutputHelper output, HostFixture fixture) : base(output, fixture) { - var services = new ServiceCollection(); - - services.AddSingleton(sp => new JsonContentSerializer(new JsonSerializerOptions(JsonSerializerDefaults.Web))); - - services.AddHttpClient(c => - { - c.BaseAddress = new Uri("https://api.github.com/"); - - c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json"); - c.DefaultRequestHeaders.Add("User-Agent", "GitHubClient"); - }) - .AddHttpMessageHandler(() => new RetryHandler()); - - ServiceProvider = services.BuildServiceProvider(); } [Fact] public async Task GetRepo() { - var client = ServiceProvider.GetService(); + var client = Services.GetService(); var result = await client.GetAsync(b => b .AppendPath("repos") .AppendPath("loresoft") @@ -50,7 +33,7 @@ public async Task GetRepo() [Fact] public async Task GetRepoIssues() { - var client = ServiceProvider.GetService(); + var client = Services.GetService(); var result = await client.GetAsync>(b => b .AppendPath("repos") .AppendPath("loresoft") @@ -64,7 +47,7 @@ public async Task GetRepoIssues() [Fact] public async Task GetFirstIssue() { - var client = ServiceProvider.GetService(); + var client = Services.GetService(); var result = await client.GetAsync(b => b .AppendPath("repos") .AppendPath("loresoft") diff --git a/test/FluentRest.Tests/GitHub/GitHubTests.cs b/test/FluentRest.Tests/GitHub/GitHubTests.cs index 09b9b37..636d5f4 100644 --- a/test/FluentRest.Tests/GitHub/GitHubTests.cs +++ b/test/FluentRest.Tests/GitHub/GitHubTests.cs @@ -1,20 +1,25 @@ -using System; using System.Collections.Generic; -using System.Net.Http; using System.Threading.Tasks; using FluentRest.Tests.GitHub.Models; +using Microsoft.Extensions.DependencyInjection; + using Xunit; +using Xunit.Abstractions; namespace FluentRest.Tests.GitHub; -public class GitHubTests +public class GitHubTests : HostTestBase { + public GitHubTests(ITestOutputHelper output, HostFixture fixture) : base(output, fixture) + { + } + [Fact] public async Task GetRepo() { - var client = CreateClient(); + var client = Services.GetService(); var result = await client.GetAsync(b => b .AppendPath("repos") .AppendPath("loresoft") @@ -28,7 +33,7 @@ public async Task GetRepo() [Fact] public async Task GetRepoIssues() { - var client = CreateClient(); + var client = Services.GetService(); var result = await client.GetAsync>(b => b .AppendPath("repos") .AppendPath("loresoft") @@ -42,7 +47,7 @@ public async Task GetRepoIssues() [Fact] public async Task GetFirstIssue() { - var client = CreateClient(); + var client = Services.GetService(); var result = await client.GetAsync(b => b .AppendPath("repos") .AppendPath("loresoft") @@ -53,16 +58,4 @@ public async Task GetFirstIssue() Assert.NotNull(result); } - - private static FluentClient CreateClient() - { - var contentSerializer = new JsonContentSerializer(); - - var httpClient = new HttpClient(); - httpClient.BaseAddress = new Uri("https://api.github.com/", UriKind.Absolute); - - var fluentClient = new FluentClient(httpClient, contentSerializer); - return fluentClient; - } - } diff --git a/test/FluentRest.Tests/GitHub/GithubClient.cs b/test/FluentRest.Tests/GitHub/GithubClient.cs index 136d13b..b4a8ddb 100644 --- a/test/FluentRest.Tests/GitHub/GithubClient.cs +++ b/test/FluentRest.Tests/GitHub/GithubClient.cs @@ -1,4 +1,3 @@ -using System; using System.Net.Http; namespace FluentRest.Tests.GitHub; diff --git a/test/FluentRest.Tests/Google/Maps/GoogleTests.cs b/test/FluentRest.Tests/Google/Maps/GoogleTests.cs index 91f7bdf..f81d164 100644 --- a/test/FluentRest.Tests/Google/Maps/GoogleTests.cs +++ b/test/FluentRest.Tests/Google/Maps/GoogleTests.cs @@ -5,7 +5,7 @@ using Xunit; -namespace FluentRest.Tests; +namespace FluentRest.Tests.Google.Maps; public class GoogleMapsTests { diff --git a/test/FluentRest.Tests/HostCollection.cs b/test/FluentRest.Tests/HostCollection.cs new file mode 100644 index 0000000..d10177b --- /dev/null +++ b/test/FluentRest.Tests/HostCollection.cs @@ -0,0 +1,9 @@ +using Xunit; + +namespace FluentRest.Tests; + +[CollectionDefinition(CollectionName)] +public class HostCollection : ICollectionFixture +{ + public const string CollectionName = nameof(HostCollection); +} diff --git a/test/FluentRest.Tests/HostFixture.cs b/test/FluentRest.Tests/HostFixture.cs new file mode 100644 index 0000000..d5b1ed2 --- /dev/null +++ b/test/FluentRest.Tests/HostFixture.cs @@ -0,0 +1,65 @@ +using System; +using System.Threading.Tasks; + +using DotNet.Testcontainers.Builders; + +using FluentRest.Tests.GitHub; + +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Hosting; + +using Xunit; + +using XUnit.Hosting; + +using IContainer = DotNet.Testcontainers.Containers.IContainer; + +namespace FluentRest.Tests; + +public class HostFixture : TestApplicationFixture, IAsyncLifetime +{ + private readonly IContainer _container = new ContainerBuilder() + .WithImage("kennethreitz/httpbin:latest") + .WithPortBinding(80, true) + .Build(); + + public async Task InitializeAsync() + { + await _container.StartAsync(); + + // get container url + HttpBinUrl = $"http://{_container.Hostname}:{_container.GetMappedPublicPort(80)}"; + } + + public async Task DisposeAsync() + { + await _container.DisposeAsync(); + } + + public string HttpBinUrl { get; private set; } = "https://httpbin.org/"; + + protected override void ConfigureApplication(HostApplicationBuilder builder) + { + base.ConfigureApplication(builder); + + builder.Services + .TryAddSingleton(); + + builder.Services + .AddHttpClient(c => + { + c.BaseAddress = new Uri("https://api.github.com/"); + + c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json"); + c.DefaultRequestHeaders.Add("User-Agent", "GitHubClient"); + }) + .AddHttpMessageHandler(() => new RetryHandler()); + + builder.Services + .AddHttpClient("GoogleMaps", client => client.BaseAddress = new Uri("https://maps.googleapis.com/maps/api/", UriKind.Absolute)); + + builder.Services + .AddHttpClient("HttpBin", client => client.BaseAddress = new Uri(HttpBinUrl, UriKind.Absolute)); + } +} diff --git a/test/FluentRest.Tests/HostTestBase.cs b/test/FluentRest.Tests/HostTestBase.cs new file mode 100644 index 0000000..b8e0795 --- /dev/null +++ b/test/FluentRest.Tests/HostTestBase.cs @@ -0,0 +1,15 @@ +using Xunit; +using Xunit.Abstractions; + +using XUnit.Hosting; + +namespace FluentRest.Tests; + +[Collection(HostCollection.CollectionName)] +public abstract class HostTestBase : TestHostBase +{ + protected HostTestBase(ITestOutputHelper output, HostFixture fixture) + : base(output, fixture) + { + } +} diff --git a/test/FluentRest.Tests/HttpClientEchoTests.cs b/test/FluentRest.Tests/HttpClientEchoTests.cs index fff9ae3..0240956 100644 --- a/test/FluentRest.Tests/HttpClientEchoTests.cs +++ b/test/FluentRest.Tests/HttpClientEchoTests.cs @@ -9,12 +9,20 @@ using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; + using Xunit; +using Xunit.Abstractions; namespace FluentRest.Tests; -public class HttpClientEchoTests +public class HttpClientEchoTests : HostTestBase { + public HttpClientEchoTests(ITestOutputHelper output, HostFixture fixture) + : base(output, fixture) + { + } + [Fact] public async Task EchoGet() { @@ -27,7 +35,7 @@ public async Task EchoGet() ); Assert.NotNull(result); - Assert.Equal("https://httpbin.org/get?page=1&size=10", result.Url); + Assert.Equal($"{Fixture.HttpBinUrl}/get?page=1&size=10", result.Url); Assert.Equal("1", result.QueryString["page"]); Assert.Equal("10", result.QueryString["size"]); } @@ -45,7 +53,7 @@ public async Task EchoGetBearer() ); Assert.NotNull(result); - Assert.Equal("https://httpbin.org/get?page=1&size=10", result.Url); + Assert.Equal($"{Fixture.HttpBinUrl}/get?page=1&size=10", result.Url); Assert.Equal("1", result.QueryString["page"]); Assert.Equal("10", result.QueryString["size"]); @@ -102,7 +110,7 @@ public async Task EchoGetAcceptMultiple() ); Assert.NotNull(result); - Assert.Equal("https://httpbin.org/get?page=10", result.Url); + Assert.Equal($"{Fixture.HttpBinUrl}/get?page=10", result.Url); Assert.Equal("text/xml, application/bson, application/json", result.Headers[HttpRequestHeaders.Accept]); Assert.Equal("testing header", result.Headers["X-Blah"]); } @@ -120,7 +128,7 @@ public async Task EchoPost() ); Assert.NotNull(result); - Assert.Equal("https://httpbin.org/post?page=10", result.Url); + Assert.Equal($"{Fixture.HttpBinUrl}/post?page=10", result.Url); Assert.Equal("Value", result.Form["Test"]); Assert.Equal("value", result.Form["key"]); } @@ -143,7 +151,7 @@ public async Task EchoPostResponse() var result = await response.DeserializeAsync(); Assert.NotNull(result); - Assert.Equal("https://httpbin.org/post?page=10", result.Url); + Assert.Equal($"{Fixture.HttpBinUrl}/post?page=10", result.Url); Assert.Equal("Value", result.Form["Test"]); Assert.Equal("value", result.Form["key"]); } @@ -161,7 +169,7 @@ public async Task EchoPatch() Assert.NotNull(response); - Assert.Equal("https://httpbin.org/patch?page=10", response.Url); + Assert.Equal($"{Fixture.HttpBinUrl}/patch?page=10", response.Url); Assert.Equal("Value", response.Form["Test"]); } @@ -182,7 +190,7 @@ public async Task EchoPatchResponse() var result = await response.DeserializeAsync(); Assert.NotNull(result); - Assert.Equal("https://httpbin.org/patch?page=10", result.Url); + Assert.Equal($"{Fixture.HttpBinUrl}/patch?page=10", result.Url); Assert.Equal("Value", result.Form["Test"]); } @@ -199,7 +207,7 @@ public async Task EchoPut() ); Assert.NotNull(result); - Assert.Equal("https://httpbin.org/put?page=10", result.Url); + Assert.Equal($"{Fixture.HttpBinUrl}/put?page=10", result.Url); Assert.Equal("Value", result.Form["Test"]); Assert.Equal("value", result.Form["key"]); } @@ -236,7 +244,7 @@ public async Task EchoPostData() Assert.True(result.Headers.ContainsKey("Content-Length")); int contentLength = Int32.Parse(result.Headers["Content-Length"]); Assert.True(contentLength > 0); - Assert.Equal("https://httpbin.org/post?page=10", result.Url); + Assert.Equal($"{Fixture.HttpBinUrl}/post?page=10", result.Url); Assert.Equal("application/json; charset=utf-8", result.Headers[HttpRequestHeaders.ContentType]); Assert.True(result.Headers.ContainsKey("Content-Type")); var contentType = result.Headers["Content-Type"]; @@ -337,7 +345,7 @@ public async Task EchoPostDataCustomCompressedContent() Assert.True(result.Headers.ContainsKey("Content-Length")); int contentLength = Int32.Parse(result.Headers["Content-Length"]); Assert.True(contentLength > 0); - Assert.Equal("https://httpbin.org/post?page=10", result.Url); + Assert.Equal($"{Fixture.HttpBinUrl}/post?page=10", result.Url); Assert.Equal("application/json; charset=utf-8", result.Headers[HttpRequestHeaders.ContentType]); Assert.Equal("gzip", result.Headers[HttpRequestHeaders.ContentEncoding]); } @@ -394,7 +402,7 @@ public async Task DefaultPost() ); Assert.NotNull(result); - Assert.Equal("https://httpbin.org/post?page=10", result.Url); + Assert.Equal($"{Fixture.HttpBinUrl}/post?page=10", result.Url); Assert.Equal("Value", result.Form["Test"]); Assert.Equal("value", result.Form["key"]); Assert.Equal("Token abc-def-123", result.Headers["Authorization"]); @@ -412,15 +420,13 @@ public async Task SendRequest() Assert.NotNull(result); Assert.True(result.Headers.ContainsKey("Content-Length")); - Assert.Equal("https://httpbin.org/post", result.Url); + Assert.Equal($"{Fixture.HttpBinUrl}/post", result.Url); } - private static HttpClient CreateClient() + private HttpClient CreateClient() { - var httpClient = new HttpClient - { - BaseAddress = new Uri("https://httpbin.org/", UriKind.Absolute) - }; + var httpClientFactory = Services.GetService(); + var httpClient = httpClientFactory.CreateClient("HttpBin"); return httpClient; } diff --git a/test/FluentRest.Tests/QueryBuilderTest.cs b/test/FluentRest.Tests/QueryBuilderTest.cs index 35591f6..7549ffa 100644 --- a/test/FluentRest.Tests/QueryBuilderTest.cs +++ b/test/FluentRest.Tests/QueryBuilderTest.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; diff --git a/test/FluentRest.Tests/UrlBuilderTests.cs b/test/FluentRest.Tests/UrlBuilderTests.cs index 6e767c1..e83a814 100644 --- a/test/FluentRest.Tests/UrlBuilderTests.cs +++ b/test/FluentRest.Tests/UrlBuilderTests.cs @@ -1,7 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Text; - using FluentAssertions; using Xunit;