From 81f53b5b769c0fc879038d329443a6ce57ac2aec Mon Sep 17 00:00:00 2001 From: Kasper Christiansen Date: Wed, 10 Jul 2024 12:09:27 +0200 Subject: [PATCH] Extended search query and added tests --- src/Kwtc.Tjek.Client.Abstractions/IClient.cs | 8 +- .../Models/Si.cs | 5 +- src/Kwtc.Tjek.Client/Client.cs | 42 +++++- src/Kwtc.Tjek.Client/StringExtensions.cs | 2 +- .../UnitTests/ClientTests.cs | 134 +++++++++++++++++- 5 files changed, 183 insertions(+), 8 deletions(-) diff --git a/src/Kwtc.Tjek.Client.Abstractions/IClient.cs b/src/Kwtc.Tjek.Client.Abstractions/IClient.cs index bbc2ff8..66baef9 100644 --- a/src/Kwtc.Tjek.Client.Abstractions/IClient.cs +++ b/src/Kwtc.Tjek.Client.Abstractions/IClient.cs @@ -4,5 +4,11 @@ namespace Kwtc.Tjek.Client.Abstractions; public interface IClient { - Task> Search(string query, CancellationToken cancellationToken = default); + public Task> Search( + string query, + string? dealerId = null, + string? catalogId = null, + string? publicationType = null, + int? limit = null, + CancellationToken cancellationToken = default); } diff --git a/src/Kwtc.Tjek.Client.Abstractions/Models/Si.cs b/src/Kwtc.Tjek.Client.Abstractions/Models/Si.cs index c3c3d5f..8c54808 100644 --- a/src/Kwtc.Tjek.Client.Abstractions/Models/Si.cs +++ b/src/Kwtc.Tjek.Client.Abstractions/Models/Si.cs @@ -1,8 +1,11 @@ namespace Kwtc.Tjek.Client.Abstractions.Models; +/// +/// System of units +/// public class Si { public long Factor { get; set; } - public long To { get; set; } + public long Symbol { get; set; } } diff --git a/src/Kwtc.Tjek.Client/Client.cs b/src/Kwtc.Tjek.Client/Client.cs index 894f330..be580a2 100644 --- a/src/Kwtc.Tjek.Client/Client.cs +++ b/src/Kwtc.Tjek.Client/Client.cs @@ -1,4 +1,5 @@ -using System.Text.Json; +using System.Text; +using System.Text.Json; using CommunityToolkit.Diagnostics; using Kwtc.Tjek.Client.Abstractions; using Kwtc.Tjek.Client.Abstractions.Models; @@ -14,12 +15,30 @@ public Client(IHttpClientFactory httpClientFactory) this.httpClientFactory = httpClientFactory; } - public async Task> Search(string query, CancellationToken cancellationToken = default) + public async Task> Search( + string query, + string? dealerId = null, + string? catalogId = null, + string? publicationType = null, + int? limit = null, + CancellationToken cancellationToken = default) { Guard.IsNotNullOrEmpty(query, nameof(query)); - + + // Build query string + var builder = new StringBuilder(); + builder.Append($"?query={query.ToValidUri()}"); + + var queryString = BuildQueryString(new Dictionary + { + { "dealer_id", $"{dealerId}" }, + { "catalog_id", $"{catalogId}" }, + { "types", $"{publicationType}" }, + { "limit", $"{limit}" }, + }, builder); + var client = this.httpClientFactory.CreateClient(Constants.HttpClientName); - var response = await client.GetAsync($"v2/offers/search?query={query.ToValidUri()}", cancellationToken); + var response = await client.GetAsync($"v2/offers/search{queryString}", cancellationToken); response.EnsureSuccessStatusCode(); @@ -33,4 +52,19 @@ public async Task> Search(string query, CancellationToken c return result ?? []; } + + private static string BuildQueryString(IDictionary parameters, StringBuilder builder) + { + foreach (var parameter in parameters) + { + if (string.IsNullOrEmpty(parameter.Value)) + { + continue; + } + + builder.Append($"&{parameter.Key}={parameter.Value.ToValidUri()}"); + } + + return builder.ToString(); + } } diff --git a/src/Kwtc.Tjek.Client/StringExtensions.cs b/src/Kwtc.Tjek.Client/StringExtensions.cs index 0c2fb65..11cff86 100644 --- a/src/Kwtc.Tjek.Client/StringExtensions.cs +++ b/src/Kwtc.Tjek.Client/StringExtensions.cs @@ -5,7 +5,7 @@ public static class StringExtensions public static string ToValidUri(this string input) { var str = input; - str = System.Text.RegularExpressions.Regex.Replace(str, @"[^a-zA-Z0-9\s-æøåÆØÅ]", ""); + str = System.Text.RegularExpressions.Regex.Replace(str, @"[^a-zA-Z0-9\s-æøåÆØÅ?&=]", ""); str = System.Text.RegularExpressions.Regex.Replace(str, @"\s+", " ").Trim(); return Uri.EscapeDataString(str); diff --git a/test/Kwtc.Tjek.Client.Tests/UnitTests/ClientTests.cs b/test/Kwtc.Tjek.Client.Tests/UnitTests/ClientTests.cs index 718ba1e..02f28ea 100644 --- a/test/Kwtc.Tjek.Client.Tests/UnitTests/ClientTests.cs +++ b/test/Kwtc.Tjek.Client.Tests/UnitTests/ClientTests.cs @@ -108,6 +108,138 @@ public async Task Search_ValidSearchTermUnexpectedResponseContent_ShouldThrow() this.httpClientFactoryMock.Verify(x => x.CreateClient(Constants.HttpClientName), Times.Once); } + [Fact] + public async Task Search_ValidSearchTermAndDealerIdExpectedResponse_ShouldSendRequest() + { + // Arrange + const string searchTerm = "ValidSearchTerm"; + var httpClient = GetMockedClient( + uri: $"search?query={searchTerm.ToValidUri()}&dealer_id=123", + content: JsonSerializer.Serialize(new List { new() }) + ); + var sut = GetSut(); + + this.httpClientFactoryMock + .Setup(x => x.CreateClient(Constants.HttpClientName)) + .Returns(httpClient); + + // Act + await sut.Search(query: searchTerm, dealerId: "123"); + + // Assert + this.httpClientFactoryMock.Verify(x => x.CreateClient(Constants.HttpClientName), Times.Once); + } + + [Fact] + public async Task Search_ValidSearchTermAndDealerIdLimitExpectedResponse_ShouldSendRequest() + { + // Arrange + const string searchTerm = "ValidSearchTerm"; + var httpClient = GetMockedClient( + uri: $"search?query={searchTerm.ToValidUri()}&dealer_id=123&limit=10", + content: JsonSerializer.Serialize(new List { new() }) + ); + var sut = GetSut(); + + this.httpClientFactoryMock + .Setup(x => x.CreateClient(Constants.HttpClientName)) + .Returns(httpClient); + + // Act + await sut.Search(query: searchTerm, dealerId: "123", limit: 10); + + // Assert + this.httpClientFactoryMock.Verify(x => x.CreateClient(Constants.HttpClientName), Times.Once); + } + + [Fact] + public async Task Search_ValidSearchTermAndDealerIdNullExpectedResponse_RequestShouldNotContainDealerId() + { + // Arrange + const string searchTerm = "ValidSearchTerm"; + var httpClient = GetMockedClient( + uri: $"search?query={searchTerm.ToValidUri()}&limit=10", + content: JsonSerializer.Serialize(new List { new() }) + ); + var sut = GetSut(); + + this.httpClientFactoryMock + .Setup(x => x.CreateClient(Constants.HttpClientName)) + .Returns(httpClient); + + // Act + await sut.Search(query: searchTerm, dealerId: null, limit: 10); + + // Assert + this.httpClientFactoryMock.Verify(x => x.CreateClient(Constants.HttpClientName), Times.Once); + } + + [Fact] + public async Task Search_ValidSearchTermAndCatalogIdExpectedResponse_ShouldSendRequest() + { + // Arrange + const string searchTerm = "ValidSearchTerm"; + var httpClient = GetMockedClient( + uri: $"search?query={searchTerm.ToValidUri()}&catalog_id=123", + content: JsonSerializer.Serialize(new List { new() }) + ); + var sut = GetSut(); + + this.httpClientFactoryMock + .Setup(x => x.CreateClient(Constants.HttpClientName)) + .Returns(httpClient); + + // Act + await sut.Search(query: searchTerm, catalogId: "123"); + + // Assert + this.httpClientFactoryMock.Verify(x => x.CreateClient(Constants.HttpClientName), Times.Once); + } + + [Fact] + public async Task Search_ValidSearchTermAndPublicationTypeExpectedResponse_ShouldSendRequest() + { + // Arrange + const string searchTerm = "ValidSearchTerm"; + var httpClient = GetMockedClient( + uri: $"search?query={searchTerm.ToValidUri()}&types=123", + content: JsonSerializer.Serialize(new List { new() }) + ); + var sut = GetSut(); + + this.httpClientFactoryMock + .Setup(x => x.CreateClient(Constants.HttpClientName)) + .Returns(httpClient); + + // Act + await sut.Search(query: searchTerm, publicationType: "123"); + + // Assert + this.httpClientFactoryMock.Verify(x => x.CreateClient(Constants.HttpClientName), Times.Once); + } + + [Fact] + public async Task Search_ValidSearchTermAndLimitExpectedResponse_ShouldSendRequest() + { + // Arrange + const string searchTerm = "ValidSearchTerm"; + var httpClient = GetMockedClient( + uri: $"search?query={searchTerm.ToValidUri()}&limit=10", + content: JsonSerializer.Serialize(new List { new() }) + ); + var sut = GetSut(); + + this.httpClientFactoryMock + .Setup(x => x.CreateClient(Constants.HttpClientName)) + .Returns(httpClient); + + // Act + await sut.Search(query: searchTerm, limit: 10); + + // Assert + this.httpClientFactoryMock.Verify(x => x.CreateClient(Constants.HttpClientName), Times.Once); + } + private static HttpClient GetMockedClient(string uri, HttpStatusCode statusCode = HttpStatusCode.OK, string content = "") { var httpMessageHandlerMock = new Mock(); @@ -116,7 +248,7 @@ private static HttpClient GetMockedClient(string uri, HttpStatusCode statusCode .Setup>( "SendAsync", ItExpr.Is(x => - x.RequestUri!.AbsoluteUri.EndsWith(uri) + x.RequestUri!.AbsoluteUri.Contains(uri) ), ItExpr.IsAny()) .ReturnsAsync(new HttpResponseMessage