diff --git a/DigitalData.Core.Client/BaseHttpClientService.cs b/DigitalData.Core.Client/BaseHttpClientService.cs index b48ab6c..6e909a5 100644 --- a/DigitalData.Core.Client/BaseHttpClientService.cs +++ b/DigitalData.Core.Client/BaseHttpClientService.cs @@ -44,7 +44,10 @@ namespace DigitalData.Core.Client if (sendWithCookie) { var cookieHeader = _cookies.GetCookieHeader(requestUri); - requestMessage.Headers.Add("Cookie", cookieHeader); + if (!string.IsNullOrWhiteSpace(cookieHeader)) + { + requestMessage.Headers.Add("Cookie", cookieHeader); + } } // Add body content if provided diff --git a/DigitalData.Core.Client/DigitalData.Core.Client.csproj b/DigitalData.Core.Client/DigitalData.Core.Client.csproj index 4827aae..5102691 100644 --- a/DigitalData.Core.Client/DigitalData.Core.Client.csproj +++ b/DigitalData.Core.Client/DigitalData.Core.Client.csproj @@ -18,6 +18,7 @@ + diff --git a/DigitalData.Core.Client/ServiceFactory.cs b/DigitalData.Core.Client/ServiceFactory.cs new file mode 100644 index 0000000..f689b16 --- /dev/null +++ b/DigitalData.Core.Client/ServiceFactory.cs @@ -0,0 +1,21 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace DigitalData.Core.Client +{ + public static class ServiceFactory + { + private static readonly IServiceCollection _services = new ServiceCollection(); + private static readonly Lazy _lazyProvider = new(Build); + + public static IServiceCollection Services => !_lazyProvider.IsValueCreated + ? _services + : throw new InvalidOperationException( + "Service provider has already been created. " + + "Further modifications to the service collection are not allowed. " + + "This is to ensure that the dependency injection container remains in a consistent state."); + + public static IServiceProvider Build() => _services.BuildServiceProvider(); + + public static T Provide() where T : notnull => _lazyProvider.Value.GetRequiredService(); + } +} \ No newline at end of file diff --git a/DigitalData.Core.Tests/Client/BaseHttpClientServiceTest.cs b/DigitalData.Core.Tests/Client/BaseHttpClientServiceTest.cs index aca2a5f..3859516 100644 --- a/DigitalData.Core.Tests/Client/BaseHttpClientServiceTest.cs +++ b/DigitalData.Core.Tests/Client/BaseHttpClientServiceTest.cs @@ -1,127 +1,35 @@ -using DigitalData.Core.Client; -using Microsoft.Extensions.Options; -using Moq; -using Moq.Protected; -using NUnit.Framework; -using System; -using System.Collections.Generic; -using System.Net; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; +using DigitalData.Core.Abstractions.Client; +using DigitalData.Core.Client; +using Microsoft.Extensions.DependencyInjection; namespace DigitalData.Core.Tests { [TestFixture] public class BaseHttpClientServiceTests { - private Mock _messageHandlerMock; - private HttpClient _httpClient; - private CookieContainer _cookieContainer; - private Mock> _optionsMock; - private BaseHttpClientService _service; + private IServiceProvider _serviceProvider; + private IBaseHttpClientService _service; [SetUp] public void SetUp() { - _messageHandlerMock = new Mock(MockBehavior.Strict); - _httpClient = new HttpClient(_messageHandlerMock.Object); - _cookieContainer = new CookieContainer(); - _optionsMock = new Mock>(); - _optionsMock.Setup(o => o.Value).Returns(new HttpClientOptions { Uri = "https://example.com" }); + _serviceProvider = new ServiceCollection() + .AddHttpClientService("https://jsonplaceholder.typicode.com/todos") + .BuildServiceProvider(); - _service = new BaseHttpClientService(_httpClient, _cookieContainer, _optionsMock.Object); + _service = _serviceProvider.GetRequiredService(); } [Test] - public void GetCookies_ShouldReturnCookies() + public async Task FetchJsonAsync_ShouldReturnJsonResponse() { - // Arrange - var uri = new Uri("https://example.com/test"); - _cookieContainer.Add(uri, new Cookie("test", "value")); - // Act - var cookies = _service.GetCookies("/test"); + var expectedUserId = (int) await _service.FetchAsync("/1", sendWithCookie: false, saveCookie: false) + .ThenAsync(res => res.Json()) + .ThenAsync(todo => todo.userId); // Assert - Assert.AreEqual(1, cookies.Count); - Assert.AreEqual("test", cookies[0].Name); - Assert.AreEqual("value", cookies[0].Value); - } - - [Test] - public async Task FetchAsync_ShouldSendRequestWithMethodAndBody() - { - // Arrange - var responseMessage = new HttpResponseMessage(HttpStatusCode.OK); - _messageHandlerMock - .Protected() - .Setup>( - "SendAsync", - ItExpr.IsAny(), - ItExpr.IsAny() - ) - .ReturnsAsync(responseMessage); - - var bodyContent = new StringContent("test body"); - - // Act - var response = await _service.FetchAsync("/test", HttpMethod.Post, body: bodyContent); - - // Assert - Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); - _messageHandlerMock.Protected().Verify( - "SendAsync", - Times.Once(), - ItExpr.Is(req => - req.Method == HttpMethod.Post && - req.RequestUri == new Uri("https://example.com/test") && - req.Content == bodyContent), - ItExpr.IsAny() - ); - } - - [Test] - public async Task FetchAsync_ShouldSendRequestWithForm() - { - // Arrange - var responseMessage = new HttpResponseMessage(HttpStatusCode.OK); - _messageHandlerMock - .Protected() - .Setup>( - "SendAsync", - ItExpr.IsAny(), - ItExpr.IsAny() - ) - .ReturnsAsync(responseMessage); - - var formData = new Dictionary { { "key", "value" } }; - - // Act - var response = await _service.FetchAsync("/test", HttpMethod.Post, form: formData); - - // Assert - Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); - _messageHandlerMock.Protected().Verify( - "SendAsync", - Times.Once(), - ItExpr.Is(req => - req.Method == HttpMethod.Post && - req.RequestUri == new Uri("https://example.com/test") && - req.Content.Headers.ContentType.MediaType == "application/x-www-form-urlencoded"), - ItExpr.IsAny() - ); - } - - [Test] - public async Task FetchAsync_ShouldThrowException_WhenBothBodyAndFormAreSet() - { - // Arrange - var bodyContent = new StringContent("test body"); - var formData = new Dictionary { { "key", "value" } }; - - // Act & Assert - Assert.ThrowsAsync(() => _service.FetchAsync("/test", HttpMethod.Post, body: bodyContent, form: formData)); + Assert.That(expectedUserId, Is.EqualTo(1), "The userId of the fetched JSON object should be 1."); } } -} +} \ No newline at end of file diff --git a/DigitalData.Core.sln b/DigitalData.Core.sln index fbd391b..ea78a17 100644 --- a/DigitalData.Core.sln +++ b/DigitalData.Core.sln @@ -17,6 +17,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DigitalData.Core.Client", " EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DigitalData.Core.Abstractions", "DigitalData.Core.Abstractions\DigitalData.Core.Abstractions.csproj", "{13E40DF1-6123-4838-9BF8-086C94E6ADF6}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DigitalData.Core.ConsoleApp", "DigitalData.Core.ConsoleApp\DigitalData.Core.ConsoleApp.csproj", "{344EEF74-83DD-480A-A1A4-F62E0E3F2102}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -50,6 +52,10 @@ Global {13E40DF1-6123-4838-9BF8-086C94E6ADF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {13E40DF1-6123-4838-9BF8-086C94E6ADF6}.Release|Any CPU.ActiveCfg = Release|Any CPU {13E40DF1-6123-4838-9BF8-086C94E6ADF6}.Release|Any CPU.Build.0 = Release|Any CPU + {344EEF74-83DD-480A-A1A4-F62E0E3F2102}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {344EEF74-83DD-480A-A1A4-F62E0E3F2102}.Debug|Any CPU.Build.0 = Debug|Any CPU + {344EEF74-83DD-480A-A1A4-F62E0E3F2102}.Release|Any CPU.ActiveCfg = Release|Any CPU + {344EEF74-83DD-480A-A1A4-F62E0E3F2102}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE