From 3625f222bc61ff8b13b532afd4d2c1b175608eed Mon Sep 17 00:00:00 2001 From: CLAWLOR Date: Wed, 28 Aug 2024 11:45:19 +0100 Subject: [PATCH 1/6] added facets to models and some test refactoring --- ...esponseToEstablishmentResultMapperTests.cs | 58 +++++++++++++++---- .../AzureSearchResultsTestDouble.cs | 53 ++++++++++++++--- .../TestDoubles/SearchServiceTestDouble.cs | 2 +- .../Models/EstablishmentResults.cs | 11 +++- .../Models/FacetResult.cs | 7 +++ .../SearchForEstablishments/SearchContext.cs | 17 +++++- 6 files changed, 124 insertions(+), 24 deletions(-) create mode 100644 Dfe.Data.SearchPrototype/SearchForEstablishments/Models/FacetResult.cs diff --git a/Dfe.Data.SearchPrototype/Infrastructure/Tests/Mappers/AzureSearchResponseToEstablishmentResultMapperTests.cs b/Dfe.Data.SearchPrototype/Infrastructure/Tests/Mappers/AzureSearchResponseToEstablishmentResultMapperTests.cs index dc84cef..f1bfa5d 100644 --- a/Dfe.Data.SearchPrototype/Infrastructure/Tests/Mappers/AzureSearchResponseToEstablishmentResultMapperTests.cs +++ b/Dfe.Data.SearchPrototype/Infrastructure/Tests/Mappers/AzureSearchResponseToEstablishmentResultMapperTests.cs @@ -12,17 +12,14 @@ namespace Dfe.Data.SearchPrototype.Infrastructure.Tests.Mappers; public sealed class AzureSearchResponseToEstablishmentResultMapperTests { - IMapper _searchResultToEstablishmentMapper; IMapper>, EstablishmentResults> _searchResponseMapper; - IMapper _searchResultToAddressMapper; public AzureSearchResponseToEstablishmentResultMapperTests() { - _searchResultToAddressMapper = new AzureSearchResultToAddressMapper(); - _searchResultToEstablishmentMapper = - new AzureSearchResultToEstablishmentMapper(_searchResultToAddressMapper); _searchResponseMapper = - new AzureSearchResponseToEstablishmentResultMapper(_searchResultToEstablishmentMapper); + new AzureSearchResponseToEstablishmentResultMapper( + new AzureSearchResultToEstablishmentMapper( + new AzureSearchResultToAddressMapper())); } [Fact] @@ -30,9 +27,9 @@ public void MapFrom_WithValidSearchResults_ReturnsConfiguredEstablishments() { // arrange List> searchResultDocuments = - SearchResultFake.SearchResults(); + new SearchResultFakeBuilder().WithSearchResults().Create(); Response> searchResponseFake = - ResponseFake.WithSearchResults(searchResultDocuments); + new ResponseFake().WithSearchResults(searchResultDocuments).Create(); // act EstablishmentResults? mappedResult = _searchResponseMapper.MapFrom(searchResponseFake); @@ -46,12 +43,46 @@ public void MapFrom_WithValidSearchResults_ReturnsConfiguredEstablishments() } } + [Fact] + public void MapFrom_WithNoFacets_ReturnsNullFacetDictionary() + { + // arrange + Response> searchResponseFake = + new ResponseFake() + .WithSearchResults( + new SearchResultFakeBuilder().WithSearchResults().Create()) + .Create(); + + // act + EstablishmentResults? mappedResult = _searchResponseMapper.MapFrom(searchResponseFake); + + // assert + mappedResult.Should().NotBeNull(); + mappedResult.Facets.Should().BeNull(); + } + + [Fact] + public void MapFrom_WithFacetResults_ReturnsFacetDictionary() + { + // arrange + var searchResultsDocuments = new SearchResultFakeBuilder() + .WithSearchResults() + .Create(); + + Response> searchResponseFake = + new ResponseFake().WithSearchResults(searchResultsDocuments).Create(); + } + [Fact] public void MapFrom_WithEmptySearchResults_ReturnsEmptyList() { // arrange + var searchResultsDocuments = new SearchResultFakeBuilder() + .WithEmptySearchResult() + .Create(); + Response> searchResponseFake = - ResponseFake.WithSearchResults(SearchResultFake.EmptySearchResult()); + new ResponseFake().WithSearchResults(searchResultsDocuments).Create(); // act EstablishmentResults? result = _searchResponseMapper.MapFrom(searchResponseFake); @@ -81,10 +112,13 @@ public void MapFrom_WithANullSearchResult_ThrowsInvalidOperationException() { // arrange var searchResultDocuments = - SearchResultFake.SearchResults(); - searchResultDocuments.Add(SearchResultFake.SearchResultWithDocument(null)); + new SearchResultFakeBuilder() + .WithSearchResults() + .IncludeNullDocument() + .Create(); + Response> searchResponseFake = - ResponseFake.WithSearchResults(searchResultDocuments); + new ResponseFake().WithSearchResults(searchResultDocuments).Create(); // act. _searchResponseMapper diff --git a/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/AzureSearchResultsTestDouble.cs b/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/AzureSearchResultsTestDouble.cs index 1f1c29f..ba405bf 100644 --- a/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/AzureSearchResultsTestDouble.cs +++ b/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/AzureSearchResultsTestDouble.cs @@ -1,17 +1,22 @@ using Azure; using Azure.Search.Documents.Models; using Moq; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace Dfe.Data.SearchPrototype.Infrastructure.Tests.TestDoubles; -public static class SearchResultFake +public class SearchResultFakeBuilder { - public static List> EmptySearchResult() + private List>? _establishmentSearchResults; + + public SearchResultFakeBuilder WithEmptySearchResult() { - return new List>(); + _establishmentSearchResults = new List>(); + return this; } - public static List> SearchResults() + public SearchResultFakeBuilder WithSearchResults() { int amount = new Bogus.Faker().Random.Number(1, 10); var searchResults = new List>(); @@ -23,22 +28,54 @@ public static List> SearchResults() EstablishmentTestDouble.Create() )); } - return searchResults; + _establishmentSearchResults = searchResults; + return this; + } + + public SearchResultFakeBuilder IncludeNullDocument() + { + if(_establishmentSearchResults == null){ + _establishmentSearchResults= new List>(); + } + _establishmentSearchResults.Add(SearchModelFactory + .SearchResult( + null!, 1.00, new Dictionary>())); + return this; } public static SearchResult SearchResultWithDocument(Establishment? document) => SearchModelFactory .SearchResult( document!, 1.00, new Dictionary>()); + + public List> Create() + { + return _establishmentSearchResults ?? throw new NullReferenceException(); + } } -public static class ResponseFake +public class ResponseFake { - public static Response> WithSearchResults(IEnumerable> searchResults) + private IEnumerable>? _searchResults; + private Dictionary>? _facetResults; + + public ResponseFake WithSearchResults(IEnumerable> searchResults) + { + _searchResults = searchResults; + return this; + } + + public ResponseFake WithFacets(Dictionary> facetResults) + { + _facetResults = facetResults; + return this; + } + + public Response> Create() { var responseMock = new Mock(); return Response.FromValue( SearchModelFactory.SearchResults( - searchResults, 100, null, null, responseMock.Object), responseMock.Object); + _searchResults, 100, _facetResults, null, responseMock.Object), responseMock.Object); } } diff --git a/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/SearchServiceTestDouble.cs b/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/SearchServiceTestDouble.cs index ee379c4..21acc17 100644 --- a/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/SearchServiceTestDouble.cs +++ b/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/SearchServiceTestDouble.cs @@ -31,7 +31,7 @@ public static ISearchByKeywordService MockSearchService(string keyword, string c Task.FromResult( Response.FromValue( SearchModelFactory.SearchResults( - SearchResultFake.SearchResults(), 100, null, null, responseMock.Object), responseMock.Object)); + new SearchResultFakeBuilder().WithSearchResults().Create(), 100, null, null, responseMock.Object), responseMock.Object)); return MockFor(validServiceResponseFake, keyword, collection); } diff --git a/Dfe.Data.SearchPrototype/SearchForEstablishments/Models/EstablishmentResults.cs b/Dfe.Data.SearchPrototype/SearchForEstablishments/Models/EstablishmentResults.cs index dfe7a36..3118d34 100644 --- a/Dfe.Data.SearchPrototype/SearchForEstablishments/Models/EstablishmentResults.cs +++ b/Dfe.Data.SearchPrototype/SearchForEstablishments/Models/EstablishmentResults.cs @@ -10,8 +10,15 @@ public sealed class EstablishmentResults /// public IReadOnlyCollection Establishments => _establishments.AsReadOnly(); + /// + /// The readonly dictionary of facet results + /// + public IReadOnlyDictionary>? Facets => _facets?.AsReadOnly() ?? null; + private readonly List _establishments; + private readonly Dictionary>? _facets; + /// /// Default constuctor /// @@ -24,8 +31,10 @@ public EstablishmentResults() /// Constructor with the following parameters /// /// List of Establishments - public EstablishmentResults(IEnumerable establishments) + /// Dictionary of facets results returned + public EstablishmentResults(IEnumerable establishments, Dictionary>? facetResults = null) { _establishments = establishments.ToList(); + _facets = facetResults; } } \ No newline at end of file diff --git a/Dfe.Data.SearchPrototype/SearchForEstablishments/Models/FacetResult.cs b/Dfe.Data.SearchPrototype/SearchForEstablishments/Models/FacetResult.cs new file mode 100644 index 0000000..2d82bdc --- /dev/null +++ b/Dfe.Data.SearchPrototype/SearchForEstablishments/Models/FacetResult.cs @@ -0,0 +1,7 @@ +namespace Dfe.Data.SearchPrototype.SearchForEstablishments.Models; + +public class FacetResult +{ + public string Value { get; set; } + public int Count { get; set; } +} diff --git a/Dfe.Data.SearchPrototype/SearchForEstablishments/SearchContext.cs b/Dfe.Data.SearchPrototype/SearchForEstablishments/SearchContext.cs index a4d52c9..32b0b7d 100644 --- a/Dfe.Data.SearchPrototype/SearchForEstablishments/SearchContext.cs +++ b/Dfe.Data.SearchPrototype/SearchForEstablishments/SearchContext.cs @@ -10,6 +10,11 @@ public sealed class SearchContext /// public string SearchKeyword { get; } + /// + /// The facets to be returned + /// + public IList? Facets { get; } + /// /// The target collection on which to apply the search. /// @@ -25,8 +30,11 @@ public sealed class SearchContext /// /// The target collection on which to apply the search. /// + /// + /// The facets to be returned. + /// /// - public SearchContext(string searchKeyword, string targetCollection) + public SearchContext(string searchKeyword, string targetCollection, IList? facets = null) { SearchKeyword = string.IsNullOrWhiteSpace(searchKeyword) ? @@ -35,6 +43,8 @@ public SearchContext(string searchKeyword, string targetCollection) TargetCollection = string.IsNullOrWhiteSpace(targetCollection) ? throw new ArgumentNullException(nameof(targetCollection)) : targetCollection; + + Facets = facets; } /// @@ -46,8 +56,11 @@ public SearchContext(string searchKeyword, string targetCollection) /// /// The underlying collection on which to undertake the search. /// + /// + /// The facets to be returned. + /// /// /// A configured T:Dfe.Data.SearchPrototype.Search.SearchContext instance. /// - public static SearchContext Create(string searchKeyword, string targetCollection) => new(searchKeyword, targetCollection); + public static SearchContext Create(string searchKeyword, string targetCollection, IList? facets = null) => new(searchKeyword, targetCollection, facets); } \ No newline at end of file From 4a723cdcd63b19f27c875929a18abc63ed76bf55 Mon Sep 17 00:00:00 2001 From: CLAWLOR Date: Wed, 28 Aug 2024 15:09:26 +0100 Subject: [PATCH 2/6] facet faker and failing test for mapper --- ...esponseToEstablishmentResultMapperTests.cs | 22 ++++++--- .../AzureSearchResponseTestDoubleBuilder.cs | 31 ++++++++++++ .../TestDoubles/FacetsResultsFakeBuilder.cs | 49 +++++++++++++++++++ ...stDouble.cs => SearchResultFakeBuilder.cs} | 31 +----------- 4 files changed, 97 insertions(+), 36 deletions(-) create mode 100644 Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/AzureSearchResponseTestDoubleBuilder.cs create mode 100644 Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/FacetsResultsFakeBuilder.cs rename Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/{AzureSearchResultsTestDouble.cs => SearchResultFakeBuilder.cs} (63%) diff --git a/Dfe.Data.SearchPrototype/Infrastructure/Tests/Mappers/AzureSearchResponseToEstablishmentResultMapperTests.cs b/Dfe.Data.SearchPrototype/Infrastructure/Tests/Mappers/AzureSearchResponseToEstablishmentResultMapperTests.cs index f1bfa5d..f02de1b 100644 --- a/Dfe.Data.SearchPrototype/Infrastructure/Tests/Mappers/AzureSearchResponseToEstablishmentResultMapperTests.cs +++ b/Dfe.Data.SearchPrototype/Infrastructure/Tests/Mappers/AzureSearchResponseToEstablishmentResultMapperTests.cs @@ -29,7 +29,7 @@ public void MapFrom_WithValidSearchResults_ReturnsConfiguredEstablishments() List> searchResultDocuments = new SearchResultFakeBuilder().WithSearchResults().Create(); Response> searchResponseFake = - new ResponseFake().WithSearchResults(searchResultDocuments).Create(); + new AzureSearchResponseTestDoubleBuilder().WithSearchResults(searchResultDocuments).Create(); // act EstablishmentResults? mappedResult = _searchResponseMapper.MapFrom(searchResponseFake); @@ -48,7 +48,7 @@ public void MapFrom_WithNoFacets_ReturnsNullFacetDictionary() { // arrange Response> searchResponseFake = - new ResponseFake() + new AzureSearchResponseTestDoubleBuilder() .WithSearchResults( new SearchResultFakeBuilder().WithSearchResults().Create()) .Create(); @@ -68,9 +68,19 @@ public void MapFrom_WithFacetResults_ReturnsFacetDictionary() var searchResultsDocuments = new SearchResultFakeBuilder() .WithSearchResults() .Create(); - + var facetsResults = new FacetsResultsFakeBuilder().WithEducationPhaseFacet().Create(); Response> searchResponseFake = - new ResponseFake().WithSearchResults(searchResultsDocuments).Create(); + new AzureSearchResponseTestDoubleBuilder() + .WithSearchResults(searchResultsDocuments) + .WithFacets(facetsResults) + .Create(); + + // act + EstablishmentResults? mappedResult = _searchResponseMapper.MapFrom(searchResponseFake); + + // assert + mappedResult.Should().NotBeNull(); + mappedResult.Facets.Should().NotBeNull(); } [Fact] @@ -82,7 +92,7 @@ public void MapFrom_WithEmptySearchResults_ReturnsEmptyList() .Create(); Response> searchResponseFake = - new ResponseFake().WithSearchResults(searchResultsDocuments).Create(); + new AzureSearchResponseTestDoubleBuilder().WithSearchResults(searchResultsDocuments).Create(); // act EstablishmentResults? result = _searchResponseMapper.MapFrom(searchResponseFake); @@ -118,7 +128,7 @@ public void MapFrom_WithANullSearchResult_ThrowsInvalidOperationException() .Create(); Response> searchResponseFake = - new ResponseFake().WithSearchResults(searchResultDocuments).Create(); + new AzureSearchResponseTestDoubleBuilder().WithSearchResults(searchResultDocuments).Create(); // act. _searchResponseMapper diff --git a/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/AzureSearchResponseTestDoubleBuilder.cs b/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/AzureSearchResponseTestDoubleBuilder.cs new file mode 100644 index 0000000..c33af17 --- /dev/null +++ b/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/AzureSearchResponseTestDoubleBuilder.cs @@ -0,0 +1,31 @@ +using Azure.Search.Documents.Models; +using Azure; +using Moq; + +namespace Dfe.Data.SearchPrototype.Infrastructure.Tests.TestDoubles; + +public class AzureSearchResponseTestDoubleBuilder +{ + private IEnumerable>? _searchResults; + private Dictionary>? _facetResults; + + public AzureSearchResponseTestDoubleBuilder WithSearchResults(IEnumerable> searchResults) + { + _searchResults = searchResults; + return this; + } + + public AzureSearchResponseTestDoubleBuilder WithFacets(Dictionary> facetResults) + { + _facetResults = facetResults; + return this; + } + + public Response> Create() + { + var responseMock = new Mock(); + return Response.FromValue( + SearchModelFactory.SearchResults( + _searchResults, 100, _facetResults, null, responseMock.Object), responseMock.Object); + } +} diff --git a/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/FacetsResultsFakeBuilder.cs b/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/FacetsResultsFakeBuilder.cs new file mode 100644 index 0000000..e611c94 --- /dev/null +++ b/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/FacetsResultsFakeBuilder.cs @@ -0,0 +1,49 @@ +using Azure.Search.Documents.Models; + +namespace Dfe.Data.SearchPrototype.Infrastructure.Tests.TestDoubles; + +public class FacetsResultsFakeBuilder +{ + private Dictionary>? _facets; + + public FacetsResultsFakeBuilder WithEducationPhaseFacet() + { + var facetResults = new List(); + int resultsCountAnyNumber = new Bogus.Faker().Random.Int(0, 50); + var facetResult = new Dictionary() + { + ["value"] = "Primary" + }; + facetResults.Add(SearchModelFactory.FacetResult(resultsCountAnyNumber, facetResult)); + facetResult = new Dictionary() + { + ["value"] = "Secondary" + }; + facetResults.Add(SearchModelFactory.FacetResult(resultsCountAnyNumber, facetResult)); + facetResult = new Dictionary() + { + ["value"] = "Post16" + }; + facetResults.Add(SearchModelFactory.FacetResult(resultsCountAnyNumber, facetResult)); + Dictionary> facet = new() { ["EducationPhase"] = facetResults }; + + if (_facets == null) + { + _facets = new Dictionary>(); + } + foreach(var kvp in facet) + { + _facets.Add(kvp.Key, kvp.Value); + } + return this; + } + + public Dictionary> Create() + { + if (_facets == null) + { + throw new NullReferenceException("Facet fake has no facets set"); + } + return _facets; + } +} diff --git a/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/AzureSearchResultsTestDouble.cs b/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/SearchResultFakeBuilder.cs similarity index 63% rename from Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/AzureSearchResultsTestDouble.cs rename to Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/SearchResultFakeBuilder.cs index ba405bf..005a7af 100644 --- a/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/AzureSearchResultsTestDouble.cs +++ b/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/SearchResultFakeBuilder.cs @@ -1,8 +1,4 @@ -using Azure; -using Azure.Search.Documents.Models; -using Moq; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; +using Azure.Search.Documents.Models; namespace Dfe.Data.SearchPrototype.Infrastructure.Tests.TestDoubles; @@ -54,28 +50,3 @@ public List> Create() } } -public class ResponseFake -{ - private IEnumerable>? _searchResults; - private Dictionary>? _facetResults; - - public ResponseFake WithSearchResults(IEnumerable> searchResults) - { - _searchResults = searchResults; - return this; - } - - public ResponseFake WithFacets(Dictionary> facetResults) - { - _facetResults = facetResults; - return this; - } - - public Response> Create() - { - var responseMock = new Mock(); - return Response.FromValue( - SearchModelFactory.SearchResults( - _searchResults, 100, _facetResults, null, responseMock.Object), responseMock.Object); - } -} From 90c83d1f048a327c9e1864cfef0f100ce9b21153 Mon Sep 17 00:00:00 2001 From: CLAWLOR Date: Wed, 28 Aug 2024 17:06:32 +0100 Subject: [PATCH 3/6] ongoing changes- checking in so can change branches --- ...archResponseToEstablishmentResultMapper.cs | 8 ++++-- .../AzureSearchResultToEstablishmentMapper.cs | 1 + ...esponseToEstablishmentResultMapperTests.cs | 10 ++++++- .../TestDoubles/FacetsResultsFakeBuilder.cs | 27 +++++++++++++++++++ .../Models/EstablishmentResults.cs | 2 +- .../SearchByKeywordResponse.cs | 3 +++ .../SearchResponseStatus.cs | 12 +++++++++ 7 files changed, 59 insertions(+), 4 deletions(-) diff --git a/Dfe.Data.SearchPrototype/Infrastructure/Mappers/AzureSearchResponseToEstablishmentResultMapper.cs b/Dfe.Data.SearchPrototype/Infrastructure/Mappers/AzureSearchResponseToEstablishmentResultMapper.cs index 48627d1..0341fad 100644 --- a/Dfe.Data.SearchPrototype/Infrastructure/Mappers/AzureSearchResponseToEstablishmentResultMapper.cs +++ b/Dfe.Data.SearchPrototype/Infrastructure/Mappers/AzureSearchResponseToEstablishmentResultMapper.cs @@ -46,9 +46,10 @@ public AzureSearchResponseToEstablishmentResultMapper(IMapper> input) { ArgumentNullException.ThrowIfNull(input); + Dictionary>? facetResults; var results = input.Value.GetResults(); - + var facets = input.Value.Facets; if (results.Any()) { var mappedResults = results.Select(result => @@ -57,7 +58,10 @@ public EstablishmentResults MapFrom(Response> input : throw new InvalidOperationException( "Search result document object cannot be null.") ); - return new EstablishmentResults(mappedResults); + //if (facets.Any()) { + // facetResults = _azureFacetResultsToApplicationFacetResultsMapper.MapFrom(facets); + //} + return new EstablishmentResults(mappedResults, facetResults); } else { diff --git a/Dfe.Data.SearchPrototype/Infrastructure/Mappers/AzureSearchResultToEstablishmentMapper.cs b/Dfe.Data.SearchPrototype/Infrastructure/Mappers/AzureSearchResultToEstablishmentMapper.cs index 0732510..1bae09a 100644 --- a/Dfe.Data.SearchPrototype/Infrastructure/Mappers/AzureSearchResultToEstablishmentMapper.cs +++ b/Dfe.Data.SearchPrototype/Infrastructure/Mappers/AzureSearchResultToEstablishmentMapper.cs @@ -37,6 +37,7 @@ public AzureSearchResultToEstablishmentMapper( /// public SearchForEstablishments.Models.Establishment MapFrom(Establishment input) { + // TODO - only throw for really essential stuff ArgumentException.ThrowIfNullOrEmpty(input.id, nameof(input.id)); ArgumentException.ThrowIfNullOrEmpty(input.ESTABLISHMENTNAME, nameof(input.ESTABLISHMENTNAME)); ArgumentException.ThrowIfNullOrEmpty(input.TYPEOFESTABLISHMENTNAME, nameof(input.TYPEOFESTABLISHMENTNAME)); diff --git a/Dfe.Data.SearchPrototype/Infrastructure/Tests/Mappers/AzureSearchResponseToEstablishmentResultMapperTests.cs b/Dfe.Data.SearchPrototype/Infrastructure/Tests/Mappers/AzureSearchResponseToEstablishmentResultMapperTests.cs index f02de1b..5b8cbb8 100644 --- a/Dfe.Data.SearchPrototype/Infrastructure/Tests/Mappers/AzureSearchResponseToEstablishmentResultMapperTests.cs +++ b/Dfe.Data.SearchPrototype/Infrastructure/Tests/Mappers/AzureSearchResponseToEstablishmentResultMapperTests.cs @@ -68,7 +68,11 @@ public void MapFrom_WithFacetResults_ReturnsFacetDictionary() var searchResultsDocuments = new SearchResultFakeBuilder() .WithSearchResults() .Create(); - var facetsResults = new FacetsResultsFakeBuilder().WithEducationPhaseFacet().Create(); + var facetsResults = new FacetsResultsFakeBuilder() + .WithEducationPhaseFacet() + .WithAutoGeneratedFacet() + .Create(); + Response> searchResponseFake = new AzureSearchResponseTestDoubleBuilder() .WithSearchResults(searchResultsDocuments) @@ -81,6 +85,10 @@ public void MapFrom_WithFacetResults_ReturnsFacetDictionary() // assert mappedResult.Should().NotBeNull(); mappedResult.Facets.Should().NotBeNull(); + foreach (var facet in facetsResults) + { + mappedResult.Facets![facet.Key].Should().NotBeEmpty(); + } } [Fact] diff --git a/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/FacetsResultsFakeBuilder.cs b/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/FacetsResultsFakeBuilder.cs index e611c94..2f084bb 100644 --- a/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/FacetsResultsFakeBuilder.cs +++ b/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/FacetsResultsFakeBuilder.cs @@ -38,6 +38,33 @@ public FacetsResultsFakeBuilder WithEducationPhaseFacet() return this; } + public FacetsResultsFakeBuilder WithAutoGeneratedFacet() + { + var facetResults = new List(); + int resultsCountAnyNumber = new Bogus.Faker().Random.Int(0, 50); + var facetValuesCount = new Bogus.Faker().Random.Int(0, 10); + for(int i=0; i() + { + ["value"] = new Bogus.Faker().Name.JobTitle() + }; + facetResults.Add(SearchModelFactory.FacetResult(resultsCountAnyNumber, facetResult)); + } + + Dictionary> facet = new() { [new Bogus.Faker().Name.JobType()] = facetResults }; + + if (_facets == null) + { + _facets = new Dictionary>(); + } + foreach (var kvp in facet) + { + _facets.Add(kvp.Key, kvp.Value); + } + return this; + } + public Dictionary> Create() { if (_facets == null) diff --git a/Dfe.Data.SearchPrototype/SearchForEstablishments/Models/EstablishmentResults.cs b/Dfe.Data.SearchPrototype/SearchForEstablishments/Models/EstablishmentResults.cs index 3118d34..51f4630 100644 --- a/Dfe.Data.SearchPrototype/SearchForEstablishments/Models/EstablishmentResults.cs +++ b/Dfe.Data.SearchPrototype/SearchForEstablishments/Models/EstablishmentResults.cs @@ -11,7 +11,7 @@ public sealed class EstablishmentResults public IReadOnlyCollection Establishments => _establishments.AsReadOnly(); /// - /// The readonly dictionary of facet results + /// The readonly dictionary of facet results keyed to the faceted field /// public IReadOnlyDictionary>? Facets => _facets?.AsReadOnly() ?? null; diff --git a/Dfe.Data.SearchPrototype/SearchForEstablishments/SearchByKeywordResponse.cs b/Dfe.Data.SearchPrototype/SearchForEstablishments/SearchByKeywordResponse.cs index 369f384..32dbc97 100644 --- a/Dfe.Data.SearchPrototype/SearchForEstablishments/SearchByKeywordResponse.cs +++ b/Dfe.Data.SearchPrototype/SearchForEstablishments/SearchByKeywordResponse.cs @@ -13,6 +13,9 @@ public sealed class SearchByKeywordResponse /// The readonly collection of T:Dfe.Data.SearchPrototype.Search.Establishment search results. /// public IReadOnlyCollection EstablishmentResults { get;} + /// + /// The Dfe.Data.SearchPrototype.SearchForEstablishments.SearchResponseStatus + /// public SearchResponseStatus Status { get; set; } /// diff --git a/Dfe.Data.SearchPrototype/SearchForEstablishments/SearchResponseStatus.cs b/Dfe.Data.SearchPrototype/SearchForEstablishments/SearchResponseStatus.cs index 623f2f0..ad82ae9 100644 --- a/Dfe.Data.SearchPrototype/SearchForEstablishments/SearchResponseStatus.cs +++ b/Dfe.Data.SearchPrototype/SearchForEstablishments/SearchResponseStatus.cs @@ -1,8 +1,20 @@ namespace Dfe.Data.SearchPrototype.SearchForEstablishments; +/// +/// The status of the search response +/// public enum SearchResponseStatus { + /// + /// The search request completed successfully + /// Success, + /// + /// The request was not valid + /// InvalidRequest, + /// + /// The request was submitted and resulted in an error + /// SearchServiceError } From e1e41b2ceb4035987350b8969655b12ad2180b09 Mon Sep 17 00:00:00 2001 From: CLAWLOR Date: Thu, 29 Aug 2024 19:07:29 +0100 Subject: [PATCH 4/6] Fixes to searchByKeywordResponse --- .../ResultsToResponseMapper.cs | 14 +++++++--- .../SearchByKeywordResponse.cs | 27 +++++++++---------- .../SearchByKeywordUseCase.cs | 10 ++----- .../ResultsToResponseMapperTests.cs | 6 ++--- 4 files changed, 29 insertions(+), 28 deletions(-) diff --git a/Dfe.Data.SearchPrototype/SearchForEstablishments/ResultsToResponseMapper.cs b/Dfe.Data.SearchPrototype/SearchForEstablishments/ResultsToResponseMapper.cs index 1a4a61d..e219297 100644 --- a/Dfe.Data.SearchPrototype/SearchForEstablishments/ResultsToResponseMapper.cs +++ b/Dfe.Data.SearchPrototype/SearchForEstablishments/ResultsToResponseMapper.cs @@ -1,5 +1,6 @@ using Dfe.Data.SearchPrototype.Common.Mappers; using Dfe.Data.SearchPrototype.SearchForEstablishments.Models; +using System.Reflection.Metadata.Ecma335; namespace Dfe.Data.SearchPrototype.SearchForEstablishments; @@ -22,10 +23,17 @@ public class ResultsToResponseMapper : IMapper public SearchByKeywordResponse MapFrom(EstablishmentResults input) { - if(input == null) + SearchResponseStatus status = (input == null) + ? SearchResponseStatus.SearchServiceError + : SearchResponseStatus.Success; + + if (status == SearchResponseStatus.Success) + { + return new(status) { EstablishmentResults = input }; + } + else { - return new() { Status = SearchResponseStatus.SearchServiceError }; + return new(status); } - else return new(input.Establishments) { Status = SearchResponseStatus.Success }; } } diff --git a/Dfe.Data.SearchPrototype/SearchForEstablishments/SearchByKeywordResponse.cs b/Dfe.Data.SearchPrototype/SearchForEstablishments/SearchByKeywordResponse.cs index e3abd67..a8f71f5 100644 --- a/Dfe.Data.SearchPrototype/SearchForEstablishments/SearchByKeywordResponse.cs +++ b/Dfe.Data.SearchPrototype/SearchForEstablishments/SearchByKeywordResponse.cs @@ -2,9 +2,6 @@ namespace Dfe.Data.SearchPrototype.SearchForEstablishments; - - - /// /// This is the object that carries the response (output) back from the /// T:Dfe.Data.SearchPrototype.SearchForEstablishments.SearchByKeywordUseCase instance. @@ -12,27 +9,28 @@ namespace Dfe.Data.SearchPrototype.SearchForEstablishments; public sealed class SearchByKeywordResponse { /// - /// The readonly collection of T:Dfe.Data.SearchPrototype.Search.Establishment search results. + /// The result object that encapsulates the search results + /// returned by the Establishment search /// - public IReadOnlyCollection EstablishmentResults { get;} + public EstablishmentResults? EstablishmentResults { get; init; } /// - /// The readonly collection of T:Dfe.Data.SearchPrototype.Search.EstablishmentFacet returned by the Establishment search + /// The result object that encapsulates the returned by the Establishment search /// - public IReadOnlyCollection? EstablishmentFacetResults { get; } + public EstablishmentFacets? EstablishmentFacetResults { get; init; } /// - /// The return status of the call to the - /// T:Dfe.Data.SearchPrototype.SearchForEstablishments.SearchByKeywordUseCase instance + /// The return status of the call to the instance /// - public SearchResponseStatus Status { get; set; } + public SearchResponseStatus Status { get; } /// - /// Default constructor + /// /// - public SearchByKeywordResponse() + /// + public SearchByKeywordResponse(SearchResponseStatus status) { - EstablishmentResults = new List(); + Status = status; } /// @@ -45,9 +43,10 @@ public SearchByKeywordResponse() /// /// The readonly collection of T:Dfe.Data.SearchPrototype.Search.EstablishmentFacet /// - public SearchByKeywordResponse(IReadOnlyCollection establishments, IReadOnlyCollection? facetResults = null) + public SearchByKeywordResponse(EstablishmentResults establishments, EstablishmentFacets facetResults, SearchResponseStatus status) { EstablishmentResults = establishments; EstablishmentFacetResults = facetResults; + Status = status; } } diff --git a/Dfe.Data.SearchPrototype/SearchForEstablishments/SearchByKeywordUseCase.cs b/Dfe.Data.SearchPrototype/SearchForEstablishments/SearchByKeywordUseCase.cs index bd81c5a..e7e59b9 100644 --- a/Dfe.Data.SearchPrototype/SearchForEstablishments/SearchByKeywordUseCase.cs +++ b/Dfe.Data.SearchPrototype/SearchForEstablishments/SearchByKeywordUseCase.cs @@ -49,10 +49,7 @@ public SearchByKeywordUseCase( public async Task HandleRequest(SearchByKeywordRequest request) { if ((request == null) || (request.Context == null)) { - return new SearchByKeywordResponse() - { - Status = SearchResponseStatus.InvalidRequest - }; + return new SearchByKeywordResponse(SearchResponseStatus.InvalidRequest); }; try @@ -62,10 +59,7 @@ public async Task HandleRequest(SearchByKeywordRequest } catch (Exception) // something went wrong in the infrastructure { - return new SearchByKeywordResponse() - { - Status = SearchResponseStatus.SearchServiceError - }; + return new SearchByKeywordResponse(SearchResponseStatus.SearchServiceError); } } } diff --git a/Dfe.Data.SearchPrototype/Tests/SearchForEstablishments/ResultsToResponseMapperTests.cs b/Dfe.Data.SearchPrototype/Tests/SearchForEstablishments/ResultsToResponseMapperTests.cs index efbe368..900fddd 100644 --- a/Dfe.Data.SearchPrototype/Tests/SearchForEstablishments/ResultsToResponseMapperTests.cs +++ b/Dfe.Data.SearchPrototype/Tests/SearchForEstablishments/ResultsToResponseMapperTests.cs @@ -22,9 +22,9 @@ public void MapFrom_ValidInput_ReturnsCorrectResponse() //assert. response.Should().NotBeNull(); response.Status.Should().Be(SearchResponseStatus.Success); - response.EstablishmentResults.Should().HaveCountGreaterThanOrEqualTo(1); - response.EstablishmentResults!.First().Urn.Should().Be(input.Establishments.First().Urn); - response.EstablishmentResults!.First().Name.Should().Be(input.Establishments.First().Name); + response.EstablishmentResults!.Establishments.Should().HaveCountGreaterThanOrEqualTo(1); + response.EstablishmentResults!.Establishments.First().Urn.Should().Be(input.Establishments.First().Urn); + response.EstablishmentResults!.Establishments.First().Name.Should().Be(input.Establishments.First().Name); } [Fact] From 3b7ca6f521cb45c8098e08c4edc8b5b5fc521a83 Mon Sep 17 00:00:00 2001 From: CLAWLOR Date: Thu, 29 Aug 2024 20:09:15 +0100 Subject: [PATCH 5/6] encapsulate facet results and establishments results into class for use by Adapter and mapper --- .../CognitiveSearchServiceAdapter.cs | 11 +++---- .../CognitiveSearchServiceAdapterTests.cs | 17 ++++++----- ...ResponseToSearchResultsMapperTestDouble.cs | 16 +++++----- .../ISearchServiceAdapter.cs | 2 +- .../Models/SearchResults.cs | 16 ++++++++++ .../ResultsToResponseMapper.cs | 7 ++--- .../SearchByKeywordUseCase.cs | 6 ++-- .../ResultsToResponseMapperTests.cs | 29 +++++++++++++++---- .../SearchByKeywordUseCaseTests.cs | 6 ++-- .../EstablishmentFacetsTestDouble.cs | 16 ++++++++++ .../TestDoubles/SearchResultsTestDouble.cs | 20 +++++++++++++ .../SearchServiceAdapterTestDouble.cs | 2 +- 12 files changed, 110 insertions(+), 38 deletions(-) create mode 100644 Dfe.Data.SearchPrototype/SearchForEstablishments/Models/SearchResults.cs create mode 100644 Dfe.Data.SearchPrototype/Tests/SearchForEstablishments/TestDoubles/EstablishmentFacetsTestDouble.cs create mode 100644 Dfe.Data.SearchPrototype/Tests/SearchForEstablishments/TestDoubles/SearchResultsTestDouble.cs diff --git a/Dfe.Data.SearchPrototype/Infrastructure/CognitiveSearchServiceAdapter.cs b/Dfe.Data.SearchPrototype/Infrastructure/CognitiveSearchServiceAdapter.cs index 720bdbf..5eb9686 100644 --- a/Dfe.Data.SearchPrototype/Infrastructure/CognitiveSearchServiceAdapter.cs +++ b/Dfe.Data.SearchPrototype/Infrastructure/CognitiveSearchServiceAdapter.cs @@ -1,12 +1,13 @@ using Azure; using Azure.Search.Documents; -using Azure.Search.Documents.Models; using Dfe.Data.Common.Infrastructure.CognitiveSearch.SearchByKeyword; using Dfe.Data.SearchPrototype.Common.Mappers; using Dfe.Data.SearchPrototype.Infrastructure.Options; using Dfe.Data.SearchPrototype.SearchForEstablishments; using Dfe.Data.SearchPrototype.SearchForEstablishments.Models; +using AzureModels = Azure.Search.Documents.Models; + namespace Dfe.Data.SearchPrototype.Infrastructure; /// @@ -17,7 +18,7 @@ public sealed class CognitiveSearchServiceAdapter : ISearchServic { private readonly ISearchByKeywordService _searchByKeywordService; private readonly ISearchOptionsFactory _searchOptionsFactory; - private readonly IMapper>, EstablishmentResults> _searchResponseMapper; + private readonly IMapper>, SearchResults> _searchResponseMapper; /// /// The following dependencies include the core cognitive search service definition, @@ -35,7 +36,7 @@ public sealed class CognitiveSearchServiceAdapter : ISearchServic public CognitiveSearchServiceAdapter( ISearchByKeywordService searchByKeywordService, ISearchOptionsFactory searchOptionsFactory, - IMapper>, EstablishmentResults> searchResponseMapper) + IMapper>, SearchResults> searchResponseMapper) { _searchOptionsFactory = searchOptionsFactory; _searchByKeywordService = searchByKeywordService; @@ -62,14 +63,14 @@ public CognitiveSearchServiceAdapter( /// Exception thrown if the data cannot be mapped /// - public async Task SearchAsync(SearchContext searchContext) + public async Task SearchAsync(SearchContext searchContext) { SearchOptions searchOptions = _searchOptionsFactory.GetSearchOptions(searchContext.TargetCollection) ?? throw new ApplicationException( $"Search options cannot be derived for {searchContext.TargetCollection}."); - Response> searchResults = + Response> searchResults = await _searchByKeywordService.SearchAsync( searchContext.SearchKeyword, searchContext.TargetCollection, diff --git a/Dfe.Data.SearchPrototype/Infrastructure/Tests/CognitiveSearchServiceAdapterTests.cs b/Dfe.Data.SearchPrototype/Infrastructure/Tests/CognitiveSearchServiceAdapterTests.cs index a594a0a..7455b4f 100644 --- a/Dfe.Data.SearchPrototype/Infrastructure/Tests/CognitiveSearchServiceAdapterTests.cs +++ b/Dfe.Data.SearchPrototype/Infrastructure/Tests/CognitiveSearchServiceAdapterTests.cs @@ -17,17 +17,17 @@ public sealed class CognitiveSearchServiceAdapterTests private static CognitiveSearchServiceAdapter CreateServiceAdapterWith( ISearchByKeywordService searchByKeywordService, ISearchOptionsFactory searchOptionsFactory, - IMapper>, EstablishmentResults> searchResponseMapper + IMapper>, SearchResults> searchResponseMapper ) => new(searchByKeywordService, searchOptionsFactory, searchResponseMapper); [Fact] - public async Task Search_WithValidSearchContext_ReturnsConfiguredResults() + public async Task Search_WithValidSearchContext_CallsMapper() { // arrange var mockService = SearchServiceTestDouble.MockSearchService("SearchKeyword", "TargetCollection"); var mockSearchOptionsFactory = SearchOptionsFactoryTestDouble.MockSearchOptionsFactory(); - var mockMapper = AzureSearchResponseToSearchResultsMapperTestDouble.MockFor(new EstablishmentResults()); + var mockMapper = AzureSearchResponseToSearchResultsMapperTestDouble.MockFor(new SearchResults()); ISearchServiceAdapter cognitiveSearchServiceAdapter = CreateServiceAdapterWith( @@ -36,14 +36,13 @@ public async Task Search_WithValidSearchContext_ReturnsConfiguredResults() mockMapper); // act - EstablishmentResults? response = + SearchResults? response = await cognitiveSearchServiceAdapter.SearchAsync( new SearchContext( searchKeyword: "SearchKeyword", targetCollection: "TargetCollection")); // assert - response.Establishments.Should().NotBeNull(); Mock.Get(mockService).Verify(SearchServiceTestDouble.SearchRequest("SearchKeyword", "TargetCollection"),Times.Once()); Mock.Get(mockSearchOptionsFactory).Verify(SearchOptionsFactoryTestDouble.SearchOption(), Times.Once()); Mock.Get(mockMapper).Verify(AzureSearchResponseToSearchResultsMapperTestDouble.MapFrom(), Times.Once()); @@ -54,7 +53,7 @@ public Task Search_WithNoSearchOptions_ThrowsApplicationException() { var mockService = SearchServiceTestDouble.MockSearchService("SearchKeyword", "TargetCollection"); var mockSearchOptionsFactory = SearchOptionsFactoryTestDouble.MockForNoOptions(); - var mockMapper = AzureSearchResponseToSearchResultsMapperTestDouble.MockFor(new EstablishmentResults()); + var mockMapper = AzureSearchResponseToSearchResultsMapperTestDouble.MockFor(new SearchResults()); // arrange ISearchServiceAdapter cognitiveSearchServiceAdapter = @@ -81,7 +80,7 @@ public async Task Search_WithNoResultsReturned_ReturnsEmptyResults() // arrange var mockService = SearchServiceTestDouble.MockSearchService("SearchKeyword", "TargetCollection"); var mockSearchOptionsFactory = SearchOptionsFactoryTestDouble.MockSearchOptionsFactory(); - var mockMapper = AzureSearchResponseToSearchResultsMapperTestDouble.MockFor(new EstablishmentResults()); + var mockMapper = AzureSearchResponseToSearchResultsMapperTestDouble.MockFor(new SearchResults()); ISearchServiceAdapter cognitiveSearchServiceAdapter = CreateServiceAdapterWith( @@ -95,7 +94,9 @@ public async Task Search_WithNoResultsReturned_ReturnsEmptyResults() targetCollection: "TargetCollection")); // assert - response.Establishments.Should().BeEmpty(); + response.Should().NotBeNull(); + response.Establishments.Should().BeNull(); + response.Facets.Should().BeNull(); } [Fact] diff --git a/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/AzureSearchResponseToSearchResultsMapperTestDouble.cs b/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/AzureSearchResponseToSearchResultsMapperTestDouble.cs index 1d28f33..d608d0c 100644 --- a/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/AzureSearchResponseToSearchResultsMapperTestDouble.cs +++ b/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/AzureSearchResponseToSearchResultsMapperTestDouble.cs @@ -9,23 +9,23 @@ namespace Dfe.Data.SearchPrototype.Infrastructure.Tests.TestDoubles; internal static class AzureSearchResponseToSearchResultsMapperTestDouble { - public static IMapper>, EstablishmentResults> DefaultMock() => - Mock.Of>, EstablishmentResults>>(); - public static Expression>, EstablishmentResults>, EstablishmentResults>> MapFrom() => + public static IMapper>, SearchResults> DefaultMock() => + Mock.Of>, SearchResults>>(); + public static Expression>, SearchResults>, SearchResults>> MapFrom() => mapper => mapper.MapFrom(It.IsAny>>()); - public static IMapper>, EstablishmentResults> MockFor(EstablishmentResults establishments) + public static IMapper>, SearchResults> MockFor(SearchResults searchResults) { - var mapperMock = new Mock>, EstablishmentResults>>(); + var mapperMock = new Mock>, SearchResults>>(); - mapperMock.Setup(MapFrom()).Returns(establishments); + mapperMock.Setup(MapFrom()).Returns(searchResults); return mapperMock.Object; } - public static IMapper>, EstablishmentResults> MockMapperThrowingArgumentException() + public static IMapper>, SearchResults> MockMapperThrowingArgumentException() { - var mapperMock = new Mock>, EstablishmentResults>>(); + var mapperMock = new Mock>, SearchResults>>(); mapperMock.Setup(MapFrom()).Throws(new ArgumentException()); diff --git a/Dfe.Data.SearchPrototype/SearchForEstablishments/ISearchServiceAdapter.cs b/Dfe.Data.SearchPrototype/SearchForEstablishments/ISearchServiceAdapter.cs index 74c7417..3127cc1 100644 --- a/Dfe.Data.SearchPrototype/SearchForEstablishments/ISearchServiceAdapter.cs +++ b/Dfe.Data.SearchPrototype/SearchForEstablishments/ISearchServiceAdapter.cs @@ -19,5 +19,5 @@ public interface ISearchServiceAdapter /// A configured "T:Dfe.Data.SearchPrototype.Search.Domain.AgregateRoot.Establishments" /// object hydrated from the results of the azure search. /// - Task SearchAsync(SearchContext searchContext); + Task SearchAsync(SearchContext searchContext); } diff --git a/Dfe.Data.SearchPrototype/SearchForEstablishments/Models/SearchResults.cs b/Dfe.Data.SearchPrototype/SearchForEstablishments/Models/SearchResults.cs new file mode 100644 index 0000000..e01e35c --- /dev/null +++ b/Dfe.Data.SearchPrototype/SearchForEstablishments/Models/SearchResults.cs @@ -0,0 +1,16 @@ +namespace Dfe.Data.SearchPrototype.SearchForEstablishments.Models; + +/// +/// The search results +/// +public class SearchResults +{ + /// + /// The returned from the Establishment search + /// + public EstablishmentResults? Establishments { get; init; } + /// + /// The resturned from the Establishment search + /// + public EstablishmentFacets? Facets { get; init; } +} diff --git a/Dfe.Data.SearchPrototype/SearchForEstablishments/ResultsToResponseMapper.cs b/Dfe.Data.SearchPrototype/SearchForEstablishments/ResultsToResponseMapper.cs index e219297..f906b56 100644 --- a/Dfe.Data.SearchPrototype/SearchForEstablishments/ResultsToResponseMapper.cs +++ b/Dfe.Data.SearchPrototype/SearchForEstablishments/ResultsToResponseMapper.cs @@ -1,6 +1,5 @@ using Dfe.Data.SearchPrototype.Common.Mappers; using Dfe.Data.SearchPrototype.SearchForEstablishments.Models; -using System.Reflection.Metadata.Ecma335; namespace Dfe.Data.SearchPrototype.SearchForEstablishments; @@ -8,7 +7,7 @@ namespace Dfe.Data.SearchPrototype.SearchForEstablishments; /// Facilitates mapping from the received T:Dfe.Data.SearchPrototype.Search.EstablishmentResults /// into the required T:Dfe.Data.SearchPrototype.SearchForEstablishments.SearchByKeywordResponse object. /// -public class ResultsToResponseMapper : IMapper +public class ResultsToResponseMapper : IMapper { /// /// The mapping input is the T:Dfe.Data.SearchPrototype.Search.EstablishmentResults which, if successful, @@ -21,7 +20,7 @@ public class ResultsToResponseMapper : IMapper /// A configured T:Dfe.Data.SearchPrototype.SearchForEstablishments.SearchByKeywordResponse instance. /// - public SearchByKeywordResponse MapFrom(EstablishmentResults input) + public SearchByKeywordResponse MapFrom(SearchResults input) { SearchResponseStatus status = (input == null) ? SearchResponseStatus.SearchServiceError @@ -29,7 +28,7 @@ public SearchByKeywordResponse MapFrom(EstablishmentResults input) if (status == SearchResponseStatus.Success) { - return new(status) { EstablishmentResults = input }; + return new(status) { EstablishmentResults = input!.Establishments, EstablishmentFacetResults = input.Facets }; } else { diff --git a/Dfe.Data.SearchPrototype/SearchForEstablishments/SearchByKeywordUseCase.cs b/Dfe.Data.SearchPrototype/SearchForEstablishments/SearchByKeywordUseCase.cs index e7e59b9..9e74496 100644 --- a/Dfe.Data.SearchPrototype/SearchForEstablishments/SearchByKeywordUseCase.cs +++ b/Dfe.Data.SearchPrototype/SearchForEstablishments/SearchByKeywordUseCase.cs @@ -13,7 +13,7 @@ namespace Dfe.Data.SearchPrototype.SearchForEstablishments; public sealed class SearchByKeywordUseCase : IUseCase { private readonly ISearchServiceAdapter _searchServiceAdapter; - private readonly IMapper _resultsToResponseMapper; + private readonly IMapper _resultsToResponseMapper; /// /// The following dependencies include the core cognitive search service definition, @@ -29,7 +29,7 @@ public sealed class SearchByKeywordUseCase : IUseCase public SearchByKeywordUseCase( ISearchServiceAdapter searchServiceAdapter, - IMapper resultsToResponseMapper) + IMapper resultsToResponseMapper) { _searchServiceAdapter = searchServiceAdapter; _resultsToResponseMapper = resultsToResponseMapper; @@ -54,7 +54,7 @@ public async Task HandleRequest(SearchByKeywordRequest try { - EstablishmentResults establishmentResults = await _searchServiceAdapter.SearchAsync(request.Context); + SearchResults establishmentResults = await _searchServiceAdapter.SearchAsync(request.Context); return _resultsToResponseMapper.MapFrom(establishmentResults); } catch (Exception) // something went wrong in the infrastructure diff --git a/Dfe.Data.SearchPrototype/Tests/SearchForEstablishments/ResultsToResponseMapperTests.cs b/Dfe.Data.SearchPrototype/Tests/SearchForEstablishments/ResultsToResponseMapperTests.cs index 900fddd..0784584 100644 --- a/Dfe.Data.SearchPrototype/Tests/SearchForEstablishments/ResultsToResponseMapperTests.cs +++ b/Dfe.Data.SearchPrototype/Tests/SearchForEstablishments/ResultsToResponseMapperTests.cs @@ -13,8 +13,8 @@ public sealed class ResultsToResponseMapperTests public void MapFrom_ValidInput_ReturnsCorrectResponse() { // arrange. - EstablishmentResults input = EstablishmentResultsTestDouble.Create(); - IMapper mapper = new ResultsToResponseMapper(); + var input = SearchResultsTestDouble.Create(); + IMapper mapper = new ResultsToResponseMapper(); // act. SearchByKeywordResponse response = mapper.MapFrom(input); @@ -23,15 +23,34 @@ public void MapFrom_ValidInput_ReturnsCorrectResponse() response.Should().NotBeNull(); response.Status.Should().Be(SearchResponseStatus.Success); response.EstablishmentResults!.Establishments.Should().HaveCountGreaterThanOrEqualTo(1); - response.EstablishmentResults!.Establishments.First().Urn.Should().Be(input.Establishments.First().Urn); - response.EstablishmentResults!.Establishments.First().Name.Should().Be(input.Establishments.First().Name); + response.EstablishmentResults!.Establishments.First().Urn.Should().Be(input.Establishments!.Establishments.First().Urn); + response.EstablishmentResults!.Establishments.First().Name.Should().Be(input.Establishments.Establishments.First().Name); + response.EstablishmentFacetResults!.Facets.Should().HaveCountGreaterThanOrEqualTo(1); + response.EstablishmentFacetResults!.Facets.First().Name.Should().Be(input.Facets!.Facets.First().Name); + } + + [Fact] + public void MapFrom_EmptyResults_ReturnsSuccessResponse() + { + // arrange. + var input = SearchResultsTestDouble.CreateWithNoResults(); + IMapper mapper = new ResultsToResponseMapper(); + + // act. + SearchByKeywordResponse response = mapper.MapFrom(input); + + //assert. + response.Should().NotBeNull(); + response.Status.Should().Be(SearchResponseStatus.Success); + response.EstablishmentResults.Should().BeNull(); + response.EstablishmentFacetResults.Should().BeNull(); } [Fact] public void MapFrom_NullInput_ReturnsErrorResponse() { // arrange. - IMapper mapper = new ResultsToResponseMapper(); + IMapper mapper = new ResultsToResponseMapper(); // act SearchByKeywordResponse response = mapper.MapFrom(null!); diff --git a/Dfe.Data.SearchPrototype/Tests/SearchForEstablishments/SearchByKeywordUseCaseTests.cs b/Dfe.Data.SearchPrototype/Tests/SearchForEstablishments/SearchByKeywordUseCaseTests.cs index bf22945..aa808b9 100644 --- a/Dfe.Data.SearchPrototype/Tests/SearchForEstablishments/SearchByKeywordUseCaseTests.cs +++ b/Dfe.Data.SearchPrototype/Tests/SearchForEstablishments/SearchByKeywordUseCaseTests.cs @@ -12,14 +12,14 @@ public sealed class SearchByKeywordUseCaseTests { private readonly SearchByKeywordUseCase _useCase; private ISearchServiceAdapter _searchServiceAdapter; - private IMapper _mapper; + private IMapper _mapper; public SearchByKeywordUseCaseTests() { // arrange _searchServiceAdapter = SearchServiceAdapterTestDouble.MockFor( - EstablishmentResultsTestDouble.Create()); + SearchResultsTestDouble.Create()); _mapper = new ResultsToResponseMapper(); _useCase = new(_searchServiceAdapter, _mapper); @@ -73,7 +73,7 @@ public async Task HandleRequest_NoResults_ReturnsSuccess() SearchByKeywordRequest request = new("searchkeyword", "target collection"); Mock.Get(_searchServiceAdapter) .Setup(adapter => adapter.SearchAsync(It.IsAny())) - .ReturnsAsync(EstablishmentResultsTestDouble.CreateWithNoResults); + .ReturnsAsync(SearchResultsTestDouble.CreateWithNoResults); // act var response = await _useCase.HandleRequest(request); diff --git a/Dfe.Data.SearchPrototype/Tests/SearchForEstablishments/TestDoubles/EstablishmentFacetsTestDouble.cs b/Dfe.Data.SearchPrototype/Tests/SearchForEstablishments/TestDoubles/EstablishmentFacetsTestDouble.cs new file mode 100644 index 0000000..c0a4bb0 --- /dev/null +++ b/Dfe.Data.SearchPrototype/Tests/SearchForEstablishments/TestDoubles/EstablishmentFacetsTestDouble.cs @@ -0,0 +1,16 @@ +using Dfe.Data.SearchPrototype.SearchForEstablishments.Models; + +namespace Dfe.Data.SearchPrototype.Tests.SearchForEstablishments.TestDoubles; + +public static class EstablishmentFacetsTestDouble +{ + public static EstablishmentFacets Create() + { + var facets = new List( + new List() + { + new EstablishmentFacet("name", new List() { new FacetResult("value1", 1)}) + }); + return new EstablishmentFacets(facets); + } +} diff --git a/Dfe.Data.SearchPrototype/Tests/SearchForEstablishments/TestDoubles/SearchResultsTestDouble.cs b/Dfe.Data.SearchPrototype/Tests/SearchForEstablishments/TestDoubles/SearchResultsTestDouble.cs new file mode 100644 index 0000000..ca70edc --- /dev/null +++ b/Dfe.Data.SearchPrototype/Tests/SearchForEstablishments/TestDoubles/SearchResultsTestDouble.cs @@ -0,0 +1,20 @@ +using Dfe.Data.SearchPrototype.SearchForEstablishments.Models; + +namespace Dfe.Data.SearchPrototype.Tests.SearchForEstablishments.TestDoubles; + +public static class SearchResultsTestDouble +{ + public static SearchResults Create() + { + return new SearchResults() + { + Facets = EstablishmentFacetsTestDouble.Create(), + Establishments = EstablishmentResultsTestDouble.Create() + }; + } + + public static SearchResults CreateWithNoResults() + { + return new SearchResults(); + } +} diff --git a/Dfe.Data.SearchPrototype/Tests/SearchForEstablishments/TestDoubles/SearchServiceAdapterTestDouble.cs b/Dfe.Data.SearchPrototype/Tests/SearchForEstablishments/TestDoubles/SearchServiceAdapterTestDouble.cs index d8a6d96..bb10b89 100644 --- a/Dfe.Data.SearchPrototype/Tests/SearchForEstablishments/TestDoubles/SearchServiceAdapterTestDouble.cs +++ b/Dfe.Data.SearchPrototype/Tests/SearchForEstablishments/TestDoubles/SearchServiceAdapterTestDouble.cs @@ -6,7 +6,7 @@ namespace Dfe.Data.SearchPrototype.Tests.SearchForEstablishments.TestDoubles; public static class SearchServiceAdapterTestDouble { - public static ISearchServiceAdapter MockFor(EstablishmentResults establishmentResults) + public static ISearchServiceAdapter MockFor(SearchResults establishmentResults) { Mock searchServiceAdapter = new(); From 7e964013554ecc387a167c29bd002a1b79f8ece1 Mon Sep 17 00:00:00 2001 From: CLAWLOR Date: Fri, 30 Aug 2024 07:53:33 +0100 Subject: [PATCH 6/6] --- .../Mappers/AzureFacetResultToEstablishmentFacetsMapperTests.cs | 2 +- .../SearchForEstablishments/SearchContext.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dfe.Data.SearchPrototype/Infrastructure/Tests/Mappers/AzureFacetResultToEstablishmentFacetsMapperTests.cs b/Dfe.Data.SearchPrototype/Infrastructure/Tests/Mappers/AzureFacetResultToEstablishmentFacetsMapperTests.cs index d037712..30382ba 100644 --- a/Dfe.Data.SearchPrototype/Infrastructure/Tests/Mappers/AzureFacetResultToEstablishmentFacetsMapperTests.cs +++ b/Dfe.Data.SearchPrototype/Infrastructure/Tests/Mappers/AzureFacetResultToEstablishmentFacetsMapperTests.cs @@ -66,4 +66,4 @@ public void MapFrom_WithNonStringFacetResults_ThrowsInvalidCastException() exception.Message.Should().Be("Unable to cast object of type 'System.Boolean' to type 'System.String'."); } -} \ No newline at end of file +} diff --git a/Dfe.Data.SearchPrototype/SearchForEstablishments/SearchContext.cs b/Dfe.Data.SearchPrototype/SearchForEstablishments/SearchContext.cs index 32b0b7d..70e9638 100644 --- a/Dfe.Data.SearchPrototype/SearchForEstablishments/SearchContext.cs +++ b/Dfe.Data.SearchPrototype/SearchForEstablishments/SearchContext.cs @@ -63,4 +63,4 @@ public SearchContext(string searchKeyword, string targetCollection, IList public static SearchContext Create(string searchKeyword, string targetCollection, IList? facets = null) => new(searchKeyword, targetCollection, facets); -} \ No newline at end of file +}