From 39895e45171c133c2c39e0af12b27efd35eb0091 Mon Sep 17 00:00:00 2001
From: Catherine Lawlor <55924986+CathLass@users.noreply.github.com>
Date: Thu, 8 Aug 2024 11:19:23 +0100
Subject: [PATCH] Cl/handle search infrastructure exceptions (#35)
* rename EstablishmentStatusCode
* Models folder
* remove NoResults status code
* build warnings
* PR comments
---
.../CognitiveSearchServiceAdapter.cs | 5 ++
...archResponseToEstablishmentResultMapper.cs | 9 ++-
.../AzureSearchResultToAddressMapper.cs | 2 +-
...AzureSearchResultToEducationPhaseMapper.cs | 2 +-
.../AzureSearchResultToEstablishmentMapper.cs | 12 +--
.../CognitiveSearchServiceAdapterTests.cs | 73 +++++++++++++------
...esponseToEstablishmentResultMapperTests.cs | 8 +-
...eSearchResultToEstablishmentMapperTests.cs | 20 ++---
...ResponseToSearchResultsMapperTestDouble.cs | 13 ++--
.../TestDoubles/EstablishmentTestDouble.cs | 4 +-
.../SearchOptionsFactoryTestDouble.cs | 2 +-
.../TestDoubles/SearchServiceTestDouble.cs | 2 +-
.../EstablishmentTestExtensionMethods.cs | 2 +-
.../ISearchServiceAdapter.cs | 4 +-
.../{ => Models}/Address.cs | 2 +-
.../{ => Models}/EducationPhase.cs | 2 +-
.../{ => Models}/Establishment.cs | 8 +-
.../{ => Models}/EstablishmentResults.cs | 2 +-
.../Models/EstablishmentStatusCode.cs | 8 ++
.../ResultsToResponseMapper.cs | 9 ++-
.../SearchByKeywordResponse.cs | 17 ++++-
.../SearchByKeywordUseCase.cs | 24 ++++--
.../SearchResponseStatus.cs | 8 ++
.../SearchForEstablishments/StatusCode.cs | 8 --
.../ResultsToResponseMapperTests.cs | 17 ++++-
.../SearchByKeywordUseCaseTests.cs | 63 ++++++++++++----
.../EstablishmentResultsTestDouble.cs | 2 +-
.../TestDoubles/EstablishmentTestDouble.cs | 6 +-
.../SearchServiceAdapterTestDouble.cs | 1 +
29 files changed, 231 insertions(+), 104 deletions(-)
rename Dfe.Data.SearchPrototype/SearchForEstablishments/{ => Models}/Address.cs (95%)
rename Dfe.Data.SearchPrototype/SearchForEstablishments/{ => Models}/EducationPhase.cs (96%)
rename Dfe.Data.SearchPrototype/SearchForEstablishments/{ => Models}/Establishment.cs (85%)
rename Dfe.Data.SearchPrototype/SearchForEstablishments/{ => Models}/EstablishmentResults.cs (92%)
create mode 100644 Dfe.Data.SearchPrototype/SearchForEstablishments/Models/EstablishmentStatusCode.cs
create mode 100644 Dfe.Data.SearchPrototype/SearchForEstablishments/SearchResponseStatus.cs
delete mode 100644 Dfe.Data.SearchPrototype/SearchForEstablishments/StatusCode.cs
diff --git a/Dfe.Data.SearchPrototype/Infrastructure/CognitiveSearchServiceAdapter.cs b/Dfe.Data.SearchPrototype/Infrastructure/CognitiveSearchServiceAdapter.cs
index 70aef74..720bdbf 100644
--- a/Dfe.Data.SearchPrototype/Infrastructure/CognitiveSearchServiceAdapter.cs
+++ b/Dfe.Data.SearchPrototype/Infrastructure/CognitiveSearchServiceAdapter.cs
@@ -5,6 +5,7 @@
using Dfe.Data.SearchPrototype.Common.Mappers;
using Dfe.Data.SearchPrototype.Infrastructure.Options;
using Dfe.Data.SearchPrototype.SearchForEstablishments;
+using Dfe.Data.SearchPrototype.SearchForEstablishments.Models;
namespace Dfe.Data.SearchPrototype.Infrastructure;
@@ -57,6 +58,10 @@ public CognitiveSearchServiceAdapter(
/// is unrecoverable, or no azure search results are returned which should never be the
/// case given no matches should return an empty wrapper result object.
///
+ ///
+ /// Exception thrown if the data cannot be mapped
+ ///
+
public async Task SearchAsync(SearchContext searchContext)
{
SearchOptions searchOptions =
diff --git a/Dfe.Data.SearchPrototype/Infrastructure/Mappers/AzureSearchResponseToEstablishmentResultMapper.cs b/Dfe.Data.SearchPrototype/Infrastructure/Mappers/AzureSearchResponseToEstablishmentResultMapper.cs
index b2230df..48627d1 100644
--- a/Dfe.Data.SearchPrototype/Infrastructure/Mappers/AzureSearchResponseToEstablishmentResultMapper.cs
+++ b/Dfe.Data.SearchPrototype/Infrastructure/Mappers/AzureSearchResponseToEstablishmentResultMapper.cs
@@ -1,7 +1,7 @@
using Azure;
using Azure.Search.Documents.Models;
using Dfe.Data.SearchPrototype.Common.Mappers;
-using Dfe.Data.SearchPrototype.SearchForEstablishments;
+using Dfe.Data.SearchPrototype.SearchForEstablishments.Models;
namespace Dfe.Data.SearchPrototype.Infrastructure.Mappers;
@@ -11,7 +11,7 @@ namespace Dfe.Data.SearchPrototype.Infrastructure.Mappers;
///
public sealed class AzureSearchResponseToEstablishmentResultMapper : IMapper>, EstablishmentResults>
{
- private readonly IMapper _azureSearchResultToEstablishmentMapper;
+ private readonly IMapper _azureSearchResultToEstablishmentMapper;
///
/// The following mapping dependency provides the functionality to map from a raw Azure
@@ -21,7 +21,7 @@ public sealed class AzureSearchResponseToEstablishmentResultMapper : IMapper
/// Mapper used to map from the raw Azure search result to a T:Dfe.Data.SearchPrototype.Search.Establishment instance.
///
- public AzureSearchResponseToEstablishmentResultMapper(IMapper azureSearchResultToEstablishmentMapper)
+ public AzureSearchResponseToEstablishmentResultMapper(IMapper azureSearchResultToEstablishmentMapper)
{
_azureSearchResultToEstablishmentMapper = azureSearchResultToEstablishmentMapper;
}
@@ -40,6 +40,9 @@ public AzureSearchResponseToEstablishmentResultMapper(IMapper
/// Exception thrown if an invalid document is derived from the Azure search result.
///
+ ///
+ /// Exception thrown if the data cannot be mapped
+ ///
public EstablishmentResults MapFrom(Response> input)
{
ArgumentNullException.ThrowIfNull(input);
diff --git a/Dfe.Data.SearchPrototype/Infrastructure/Mappers/AzureSearchResultToAddressMapper.cs b/Dfe.Data.SearchPrototype/Infrastructure/Mappers/AzureSearchResultToAddressMapper.cs
index 28a37dd..1303179 100644
--- a/Dfe.Data.SearchPrototype/Infrastructure/Mappers/AzureSearchResultToAddressMapper.cs
+++ b/Dfe.Data.SearchPrototype/Infrastructure/Mappers/AzureSearchResultToAddressMapper.cs
@@ -1,5 +1,5 @@
using Dfe.Data.SearchPrototype.Common.Mappers;
-using Dfe.Data.SearchPrototype.SearchForEstablishments;
+using Dfe.Data.SearchPrototype.SearchForEstablishments.Models;
namespace Dfe.Data.SearchPrototype.Infrastructure.Mappers;
diff --git a/Dfe.Data.SearchPrototype/Infrastructure/Mappers/AzureSearchResultToEducationPhaseMapper.cs b/Dfe.Data.SearchPrototype/Infrastructure/Mappers/AzureSearchResultToEducationPhaseMapper.cs
index 1fbe1a1..3b97e04 100644
--- a/Dfe.Data.SearchPrototype/Infrastructure/Mappers/AzureSearchResultToEducationPhaseMapper.cs
+++ b/Dfe.Data.SearchPrototype/Infrastructure/Mappers/AzureSearchResultToEducationPhaseMapper.cs
@@ -1,5 +1,5 @@
using Dfe.Data.SearchPrototype.Common.Mappers;
-using Dfe.Data.SearchPrototype.SearchForEstablishments;
+using Dfe.Data.SearchPrototype.SearchForEstablishments.Models;
namespace Dfe.Data.SearchPrototype.Infrastructure.Mappers;
diff --git a/Dfe.Data.SearchPrototype/Infrastructure/Mappers/AzureSearchResultToEstablishmentMapper.cs b/Dfe.Data.SearchPrototype/Infrastructure/Mappers/AzureSearchResultToEstablishmentMapper.cs
index 1ea09fa..c8cc437 100644
--- a/Dfe.Data.SearchPrototype/Infrastructure/Mappers/AzureSearchResultToEstablishmentMapper.cs
+++ b/Dfe.Data.SearchPrototype/Infrastructure/Mappers/AzureSearchResultToEstablishmentMapper.cs
@@ -1,5 +1,5 @@
using Dfe.Data.SearchPrototype.Common.Mappers;
-using Dfe.Data.SearchPrototype.SearchForEstablishments;
+using Dfe.Data.SearchPrototype.SearchForEstablishments.Models;
namespace Dfe.Data.SearchPrototype.Infrastructure.Mappers;
@@ -7,7 +7,7 @@ namespace Dfe.Data.SearchPrototype.Infrastructure.Mappers;
/// Facilitates mapping from the received T:Dfe.Data.SearchPrototype.Infrastructure.Establishment
/// into the required T:Dfe.Data.SearchPrototype.SearchForEstablishments.Establishment object.
///
-public sealed class AzureSearchResultToEstablishmentMapper : IMapper
+public sealed class AzureSearchResultToEstablishmentMapper : IMapper
{
private readonly IMapper _addressMapper;
private readonly IMapper _educationPhaseMapper;
@@ -39,17 +39,17 @@ public AzureSearchResultToEstablishmentMapper(
///
/// Exception thrown if the id, name, or type of an establishment is not provided
///
- public SearchForEstablishments.Establishment MapFrom(Establishment input)
+ public SearchForEstablishments.Models.Establishment MapFrom(Establishment input)
{
ArgumentException.ThrowIfNullOrEmpty(input.id, nameof(input.id));
ArgumentException.ThrowIfNullOrEmpty(input.ESTABLISHMENTNAME, nameof(input.ESTABLISHMENTNAME));
ArgumentException.ThrowIfNullOrEmpty(input.TYPEOFESTABLISHMENTNAME, nameof(input.ESTABLISHMENTNAME));
var statusCode = input.ESTABLISHMENTSTATUSCODE == "1"
- ? StatusCode.Open
+ ? EstablishmentStatusCode.Open
: input.ESTABLISHMENTSTATUSCODE == "0"
- ? StatusCode.Closed
- : StatusCode.Unknown;
+ ? EstablishmentStatusCode.Closed
+ : EstablishmentStatusCode.Unknown;
return new(
urn: input.id,
diff --git a/Dfe.Data.SearchPrototype/Infrastructure/Tests/CognitiveSearchServiceAdapterTests.cs b/Dfe.Data.SearchPrototype/Infrastructure/Tests/CognitiveSearchServiceAdapterTests.cs
index 6671aaf..a594a0a 100644
--- a/Dfe.Data.SearchPrototype/Infrastructure/Tests/CognitiveSearchServiceAdapterTests.cs
+++ b/Dfe.Data.SearchPrototype/Infrastructure/Tests/CognitiveSearchServiceAdapterTests.cs
@@ -5,6 +5,7 @@
using Dfe.Data.SearchPrototype.Infrastructure.Options;
using Dfe.Data.SearchPrototype.Infrastructure.Tests.TestDoubles;
using Dfe.Data.SearchPrototype.SearchForEstablishments;
+using Dfe.Data.SearchPrototype.SearchForEstablishments.Models;
using FluentAssertions;
using Moq;
using Xunit;
@@ -21,12 +22,12 @@ IMapper>, EstablishmentResults> searchResp
new(searchByKeywordService, searchOptionsFactory, searchResponseMapper);
[Fact]
- public async Task Search_With_Valid_SearchContext_Returns_Configured_Results()
+ public async Task Search_WithValidSearchContext_ReturnsConfiguredResults()
{
// arrange
- var mockSearchOptionsFactory = SearchOptionsFactoryTestDouble.MockSearchOptionsFactory();
var mockService = SearchServiceTestDouble.MockSearchService("SearchKeyword", "TargetCollection");
- var mockMapper = AzureSearchResponseToSearchResultsMapperTestDouble.MockDefaultMapper();
+ var mockSearchOptionsFactory = SearchOptionsFactoryTestDouble.MockSearchOptionsFactory();
+ var mockMapper = AzureSearchResponseToSearchResultsMapperTestDouble.MockFor(new EstablishmentResults());
ISearchServiceAdapter cognitiveSearchServiceAdapter =
CreateServiceAdapterWith(
@@ -49,14 +50,18 @@ await cognitiveSearchServiceAdapter.SearchAsync(
}
[Fact]
- public Task Search_With_Valid_SearchContext_No_Options_Returned_Throws_ApplicationException()
+ public Task Search_WithNoSearchOptions_ThrowsApplicationException()
{
+ var mockService = SearchServiceTestDouble.MockSearchService("SearchKeyword", "TargetCollection");
+ var mockSearchOptionsFactory = SearchOptionsFactoryTestDouble.MockForNoOptions();
+ var mockMapper = AzureSearchResponseToSearchResultsMapperTestDouble.MockFor(new EstablishmentResults());
+
// arrange
ISearchServiceAdapter cognitiveSearchServiceAdapter =
CreateServiceAdapterWith(
- SearchServiceTestDouble.MockSearchService("SearchKeyword", "TargetCollection"),
- SearchOptionsFactoryTestDouble.MockForDefaultResult(),
- AzureSearchResponseToSearchResultsMapperTestDouble.MockDefaultMapper());
+ mockService,
+ mockSearchOptionsFactory,
+ mockMapper);
// act.
return cognitiveSearchServiceAdapter
@@ -65,30 +70,54 @@ await serviceAdapter.SearchAsync(
new SearchContext(
searchKeyword: "SearchKeyword",
targetCollection: "TargetCollection")))
- .Should()
- .ThrowAsync()
- .WithMessage("Search options cannot be derived for TargetCollection.");
+ .Should()
+ .ThrowAsync()
+ .WithMessage("Search options cannot be derived for TargetCollection.");
}
[Fact]
- public Task Search_With_Valid_SearchContext_No_Results_Returned_Throws_ApplicationException()
+ public async Task Search_WithNoResultsReturned_ReturnsEmptyResults()
{
// arrange
+ var mockService = SearchServiceTestDouble.MockSearchService("SearchKeyword", "TargetCollection");
+ var mockSearchOptionsFactory = SearchOptionsFactoryTestDouble.MockSearchOptionsFactory();
+ var mockMapper = AzureSearchResponseToSearchResultsMapperTestDouble.MockFor(new EstablishmentResults());
+
ISearchServiceAdapter cognitiveSearchServiceAdapter =
CreateServiceAdapterWith(
- SearchServiceTestDouble.DefaultMock(),
- SearchOptionsFactoryTestDouble.MockSearchOptionsFactory(),
- AzureSearchResponseToSearchResultsMapperTestDouble.MockDefaultMapper());
+ mockService,
+ mockSearchOptionsFactory,
+ mockMapper);
// act.
+ var response = await cognitiveSearchServiceAdapter.SearchAsync(new SearchContext(
+ searchKeyword: "SearchKeyword",
+ targetCollection: "TargetCollection"));
+
+ // assert
+ response.Establishments.Should().BeEmpty();
+ }
+
+ [Fact]
+ public Task Search_MapperThrowsException_ExceptionPassesThrough()
+ {
+ // arrange
+ var mockService = SearchServiceTestDouble.MockSearchService("SearchKeyword", "TargetCollection");
+ var mockSearchOptionsFactory = SearchOptionsFactoryTestDouble.MockSearchOptionsFactory();
+ var mockMapper = AzureSearchResponseToSearchResultsMapperTestDouble.MockMapperThrowingArgumentException();
+
+ ISearchServiceAdapter cognitiveSearchServiceAdapter =
+ CreateServiceAdapterWith(
+ mockService,
+ mockSearchOptionsFactory,
+ mockMapper);
+
+ // act, assert.
return cognitiveSearchServiceAdapter
- .Invoking(async serviceAdapter =>
- await serviceAdapter.SearchAsync(
- new SearchContext(
- searchKeyword: "SearchKeyword",
- targetCollection: "TargetCollection")))
- .Should()
- .ThrowAsync()
- .WithMessage("Unable to derive search results based on input SearchKeyword.");
+ .Invoking(adapter => adapter.SearchAsync(new SearchContext(
+ searchKeyword: "SearchKeyword",
+ targetCollection: "TargetCollection")))
+ .Should()
+ .ThrowAsync< ArgumentException>();
}
}
diff --git a/Dfe.Data.SearchPrototype/Infrastructure/Tests/Mappers/AzureSearchResponseToEstablishmentResultMapperTests.cs b/Dfe.Data.SearchPrototype/Infrastructure/Tests/Mappers/AzureSearchResponseToEstablishmentResultMapperTests.cs
index 074ec06..e4b3676 100644
--- a/Dfe.Data.SearchPrototype/Infrastructure/Tests/Mappers/AzureSearchResponseToEstablishmentResultMapperTests.cs
+++ b/Dfe.Data.SearchPrototype/Infrastructure/Tests/Mappers/AzureSearchResponseToEstablishmentResultMapperTests.cs
@@ -4,7 +4,7 @@
using Dfe.Data.SearchPrototype.Infrastructure.Mappers;
using Dfe.Data.SearchPrototype.Infrastructure.Tests.TestDoubles;
using Dfe.Data.SearchPrototype.Infrastructure.Tests.TestHelpers;
-using Dfe.Data.SearchPrototype.SearchForEstablishments;
+using Dfe.Data.SearchPrototype.SearchForEstablishments.Models;
using FluentAssertions;
using Xunit;
@@ -12,10 +12,10 @@ namespace Dfe.Data.SearchPrototype.Infrastructure.Tests.Mappers;
public sealed class AzureSearchResponseToEstablishmentResultMapperTests
{
- IMapper _searchResultToEstablishmentMapper;
+ IMapper _searchResultToEstablishmentMapper;
IMapper>, EstablishmentResults> _searchResponseMapper;
- IMapper _searchResultToAddressMapper;
- IMapper _searchResultToEducationPhaseMapper;
+ IMapper _searchResultToAddressMapper;
+ IMapper _searchResultToEducationPhaseMapper;
public AzureSearchResponseToEstablishmentResultMapperTests()
{
diff --git a/Dfe.Data.SearchPrototype/Infrastructure/Tests/Mappers/AzureSearchResultToEstablishmentMapperTests.cs b/Dfe.Data.SearchPrototype/Infrastructure/Tests/Mappers/AzureSearchResultToEstablishmentMapperTests.cs
index a16c00f..312e792 100644
--- a/Dfe.Data.SearchPrototype/Infrastructure/Tests/Mappers/AzureSearchResultToEstablishmentMapperTests.cs
+++ b/Dfe.Data.SearchPrototype/Infrastructure/Tests/Mappers/AzureSearchResultToEstablishmentMapperTests.cs
@@ -1,7 +1,7 @@
using Dfe.Data.SearchPrototype.Common.Mappers;
using Dfe.Data.SearchPrototype.Infrastructure.Mappers;
using Dfe.Data.SearchPrototype.Infrastructure.Tests.TestDoubles;
-using Dfe.Data.SearchPrototype.SearchForEstablishments;
+using Dfe.Data.SearchPrototype.SearchForEstablishments.Models;
using FluentAssertions;
using Xunit;
@@ -9,9 +9,9 @@ namespace Dfe.Data.SearchPrototype.Infrastructure.Tests.Mappers;
public sealed class AzureSearchResultToEstablishmentMapperTests
{
- IMapper _establishmentMapper;
- IMapper _addressMapper;
- IMapper _educationPhaseMapper;
+ IMapper _establishmentMapper;
+ IMapper _addressMapper;
+ IMapper _educationPhaseMapper;
public AzureSearchResultToEstablishmentMapperTests()
{
@@ -21,16 +21,16 @@ public AzureSearchResultToEstablishmentMapperTests()
}
[Theory]
- [InlineData("1", StatusCode.Open)]
- [InlineData("0", StatusCode.Closed)]
- [InlineData("", StatusCode.Unknown)]
- public void MapFrom_With_Valid_Search_Result_Returns_Configured_Establishment(string statusCode, StatusCode expectedStatusCode)
+ [InlineData("1", EstablishmentStatusCode.Open)]
+ [InlineData("0", EstablishmentStatusCode.Closed)]
+ [InlineData("", EstablishmentStatusCode.Unknown)]
+ public void MapFrom_With_Valid_Search_Result_Returns_Configured_Establishment(string statusCode, EstablishmentStatusCode expectedStatusCode)
{
// arrange
Establishment establishmentFake = EstablishmentTestDouble.CreateWithStatusCode(statusCode);
// act
- SearchForEstablishments.Establishment? result = _establishmentMapper.MapFrom(establishmentFake);
+ SearchForEstablishments.Models.Establishment? result = _establishmentMapper.MapFrom(establishmentFake);
// assert
result.Should().NotBeNull();
@@ -150,7 +150,7 @@ public void MapFrom_With_NullAddressValues_Returns_Configured_Establishment(
};
// act
- SearchForEstablishments.Establishment? result = _establishmentMapper.MapFrom(establishmentFake);
+ SearchForEstablishments.Models.Establishment? result = _establishmentMapper.MapFrom(establishmentFake);
// assert
result.Should().NotBeNull();
diff --git a/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/AzureSearchResponseToSearchResultsMapperTestDouble.cs b/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/AzureSearchResponseToSearchResultsMapperTestDouble.cs
index e49239d..1d28f33 100644
--- a/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/AzureSearchResponseToSearchResultsMapperTestDouble.cs
+++ b/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/AzureSearchResponseToSearchResultsMapperTestDouble.cs
@@ -1,7 +1,7 @@
using Azure;
using Azure.Search.Documents.Models;
using Dfe.Data.SearchPrototype.Common.Mappers;
-using Dfe.Data.SearchPrototype.SearchForEstablishments;
+using Dfe.Data.SearchPrototype.SearchForEstablishments.Models;
using Moq;
using System.Linq.Expressions;
@@ -23,12 +23,13 @@ public static IMapper>, EstablishmentResul
return mapperMock.Object;
}
- public static IMapper>, EstablishmentResults> MockDefaultMapper()
+ public static IMapper>, EstablishmentResults> MockMapperThrowingArgumentException()
{
- var mockMapper = new Mock>, EstablishmentResults>>();
- mockMapper.Setup(mapper => mapper.MapFrom(It.IsAny>>()))
- .Returns(new EstablishmentResults());
- return mockMapper.Object;
+ var mapperMock = new Mock>, EstablishmentResults>>();
+
+ mapperMock.Setup(MapFrom()).Throws(new ArgumentException());
+
+ return mapperMock.Object;
}
internal static class EstablishmentFakes
diff --git a/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/EstablishmentTestDouble.cs b/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/EstablishmentTestDouble.cs
index d298b03..5dcd59b 100644
--- a/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/EstablishmentTestDouble.cs
+++ b/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/EstablishmentTestDouble.cs
@@ -1,6 +1,4 @@
-using Bogus;
-using Dfe.Data.SearchPrototype.SearchForEstablishments;
-using System.IO;
+using Dfe.Data.SearchPrototype.SearchForEstablishments.Models;
namespace Dfe.Data.SearchPrototype.Infrastructure.Tests.TestDoubles;
diff --git a/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/SearchOptionsFactoryTestDouble.cs b/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/SearchOptionsFactoryTestDouble.cs
index e175d34..a98e8be 100644
--- a/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/SearchOptionsFactoryTestDouble.cs
+++ b/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/SearchOptionsFactoryTestDouble.cs
@@ -23,7 +23,7 @@ public static ISearchOptionsFactory MockFor(SearchOptions searchOptions)
public static ISearchOptionsFactory MockSearchOptionsFactory() => MockFor(SearchOptionsFake);
- public static ISearchOptionsFactory MockForDefaultResult() => MockFor(default!);
+ public static ISearchOptionsFactory MockForNoOptions() => MockFor(default!);
public static SearchOptions SearchOptionsFake => new()
{
SearchMode = SearchMode.Any,
diff --git a/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/SearchServiceTestDouble.cs b/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/SearchServiceTestDouble.cs
index 3e2da9b..ee379c4 100644
--- a/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/SearchServiceTestDouble.cs
+++ b/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestDoubles/SearchServiceTestDouble.cs
@@ -41,6 +41,6 @@ public static ISearchByKeywordService MockForDefaultResult()
var validServiceResponseFake =
Task.FromResult>>(default!);
- return MockFor(validServiceResponseFake, string.Empty, string.Empty);
+ return MockFor(validServiceResponseFake, It.IsAny(), It.IsAny());
}
}
diff --git a/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestHelpers/EstablishmentTestExtensionMethods.cs b/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestHelpers/EstablishmentTestExtensionMethods.cs
index 4e51b23..52a9724 100644
--- a/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestHelpers/EstablishmentTestExtensionMethods.cs
+++ b/Dfe.Data.SearchPrototype/Infrastructure/Tests/TestHelpers/EstablishmentTestExtensionMethods.cs
@@ -1,5 +1,5 @@
using Azure.Search.Documents.Models;
-using Dfe.Data.SearchPrototype.SearchForEstablishments;
+using Dfe.Data.SearchPrototype.SearchForEstablishments.Models;
using FluentAssertions;
namespace Dfe.Data.SearchPrototype.Infrastructure.Tests.TestHelpers;
diff --git a/Dfe.Data.SearchPrototype/SearchForEstablishments/ISearchServiceAdapter.cs b/Dfe.Data.SearchPrototype/SearchForEstablishments/ISearchServiceAdapter.cs
index 808114d..74c7417 100644
--- a/Dfe.Data.SearchPrototype/SearchForEstablishments/ISearchServiceAdapter.cs
+++ b/Dfe.Data.SearchPrototype/SearchForEstablishments/ISearchServiceAdapter.cs
@@ -1,4 +1,6 @@
-namespace Dfe.Data.SearchPrototype.SearchForEstablishments;
+using Dfe.Data.SearchPrototype.SearchForEstablishments.Models;
+
+namespace Dfe.Data.SearchPrototype.SearchForEstablishments;
///
/// Describes behaviour for an adaption of core search services infrastructure to allow
diff --git a/Dfe.Data.SearchPrototype/SearchForEstablishments/Address.cs b/Dfe.Data.SearchPrototype/SearchForEstablishments/Models/Address.cs
similarity index 95%
rename from Dfe.Data.SearchPrototype/SearchForEstablishments/Address.cs
rename to Dfe.Data.SearchPrototype/SearchForEstablishments/Models/Address.cs
index cd95f91..a5c3fa0 100644
--- a/Dfe.Data.SearchPrototype/SearchForEstablishments/Address.cs
+++ b/Dfe.Data.SearchPrototype/SearchForEstablishments/Models/Address.cs
@@ -1,4 +1,4 @@
-namespace Dfe.Data.SearchPrototype.SearchForEstablishments;
+namespace Dfe.Data.SearchPrototype.SearchForEstablishments.Models;
public class Address
{
diff --git a/Dfe.Data.SearchPrototype/SearchForEstablishments/EducationPhase.cs b/Dfe.Data.SearchPrototype/SearchForEstablishments/Models/EducationPhase.cs
similarity index 96%
rename from Dfe.Data.SearchPrototype/SearchForEstablishments/EducationPhase.cs
rename to Dfe.Data.SearchPrototype/SearchForEstablishments/Models/EducationPhase.cs
index 26b1a22..4849574 100644
--- a/Dfe.Data.SearchPrototype/SearchForEstablishments/EducationPhase.cs
+++ b/Dfe.Data.SearchPrototype/SearchForEstablishments/Models/EducationPhase.cs
@@ -1,4 +1,4 @@
-namespace Dfe.Data.SearchPrototype.SearchForEstablishments;
+namespace Dfe.Data.SearchPrototype.SearchForEstablishments.Models;
///
/// Object used to encapsulate the education phase of the retrieved establishment.
diff --git a/Dfe.Data.SearchPrototype/SearchForEstablishments/Establishment.cs b/Dfe.Data.SearchPrototype/SearchForEstablishments/Models/Establishment.cs
similarity index 85%
rename from Dfe.Data.SearchPrototype/SearchForEstablishments/Establishment.cs
rename to Dfe.Data.SearchPrototype/SearchForEstablishments/Models/Establishment.cs
index 11658e1..16db28f 100644
--- a/Dfe.Data.SearchPrototype/SearchForEstablishments/Establishment.cs
+++ b/Dfe.Data.SearchPrototype/SearchForEstablishments/Models/Establishment.cs
@@ -1,4 +1,4 @@
-namespace Dfe.Data.SearchPrototype.SearchForEstablishments;
+namespace Dfe.Data.SearchPrototype.SearchForEstablishments.Models;
///
/// Object used to encapsulate the establishment search result.
@@ -20,12 +20,12 @@ public class Establishment
///
/// The read-only type of the establishment.
///
- public string EstablishmentType { get; }
+ public string EstablishmentType { get; }
public EducationPhase EducationPhase { get; }
///
/// The read-only status of the establishment.
///
- public StatusCode EstablishmentStatusCode { get; }
+ public EstablishmentStatusCode EstablishmentStatusCode { get; }
///
/// Establishes an immutable establishment instance via the constructor arguments specified.
///
@@ -44,7 +44,7 @@ public class Establishment
/// ///
/// The status of the given establishment.
///
- public Establishment(string urn, string name, Address address, string establishmentType, EducationPhase educationPhase, StatusCode establishmentStatusCode)
+ public Establishment(string urn, string name, Address address, string establishmentType, EducationPhase educationPhase, EstablishmentStatusCode establishmentStatusCode)
{
Urn = urn;
Name = name;
diff --git a/Dfe.Data.SearchPrototype/SearchForEstablishments/EstablishmentResults.cs b/Dfe.Data.SearchPrototype/SearchForEstablishments/Models/EstablishmentResults.cs
similarity index 92%
rename from Dfe.Data.SearchPrototype/SearchForEstablishments/EstablishmentResults.cs
rename to Dfe.Data.SearchPrototype/SearchForEstablishments/Models/EstablishmentResults.cs
index 59022ff..dfe7a36 100644
--- a/Dfe.Data.SearchPrototype/SearchForEstablishments/EstablishmentResults.cs
+++ b/Dfe.Data.SearchPrototype/SearchForEstablishments/Models/EstablishmentResults.cs
@@ -1,4 +1,4 @@
-namespace Dfe.Data.SearchPrototype.SearchForEstablishments;
+namespace Dfe.Data.SearchPrototype.SearchForEstablishments.Models;
///
/// Object used to encapsulate the aggregation of establishment search results.
diff --git a/Dfe.Data.SearchPrototype/SearchForEstablishments/Models/EstablishmentStatusCode.cs b/Dfe.Data.SearchPrototype/SearchForEstablishments/Models/EstablishmentStatusCode.cs
new file mode 100644
index 0000000..adb76d5
--- /dev/null
+++ b/Dfe.Data.SearchPrototype/SearchForEstablishments/Models/EstablishmentStatusCode.cs
@@ -0,0 +1,8 @@
+namespace Dfe.Data.SearchPrototype.SearchForEstablishments.Models;
+
+public enum EstablishmentStatusCode
+{
+ Closed = 0,
+ Open = 1,
+ Unknown
+}
\ No newline at end of file
diff --git a/Dfe.Data.SearchPrototype/SearchForEstablishments/ResultsToResponseMapper.cs b/Dfe.Data.SearchPrototype/SearchForEstablishments/ResultsToResponseMapper.cs
index 0892d6c..1a4a61d 100644
--- a/Dfe.Data.SearchPrototype/SearchForEstablishments/ResultsToResponseMapper.cs
+++ b/Dfe.Data.SearchPrototype/SearchForEstablishments/ResultsToResponseMapper.cs
@@ -1,4 +1,5 @@
using Dfe.Data.SearchPrototype.Common.Mappers;
+using Dfe.Data.SearchPrototype.SearchForEstablishments.Models;
namespace Dfe.Data.SearchPrototype.SearchForEstablishments;
@@ -21,8 +22,10 @@ public class ResultsToResponseMapper : IMapper
public SearchByKeywordResponse MapFrom(EstablishmentResults input)
{
- SearchByKeywordResponse response = new(input.Establishments);
-
- return response;
+ if(input == null)
+ {
+ return new() { Status = SearchResponseStatus.SearchServiceError };
+ }
+ 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 72ae7d7..369f384 100644
--- a/Dfe.Data.SearchPrototype/SearchForEstablishments/SearchByKeywordResponse.cs
+++ b/Dfe.Data.SearchPrototype/SearchForEstablishments/SearchByKeywordResponse.cs
@@ -1,4 +1,6 @@
-namespace Dfe.Data.SearchPrototype.SearchForEstablishments;
+using Dfe.Data.SearchPrototype.SearchForEstablishments.Models;
+
+namespace Dfe.Data.SearchPrototype.SearchForEstablishments;
///
/// This is the object that carries the response (output) back from the
@@ -10,7 +12,16 @@ public sealed class SearchByKeywordResponse
///
/// The readonly collection of T:Dfe.Data.SearchPrototype.Search.Establishment search results.
///
- public IReadOnlyCollection? EstablishmentResults { get;}
+ public IReadOnlyCollection EstablishmentResults { get;}
+ public SearchResponseStatus Status { get; set; }
+
+ ///
+ /// Default constructor
+ ///
+ public SearchByKeywordResponse()
+ {
+ EstablishmentResults = new List();
+ }
///
/// The following argument is passed via the constructor and is not changeable
@@ -19,7 +30,7 @@ public sealed class SearchByKeywordResponse
///
/// The readonly collection of T:Dfe.Data.SearchPrototype.Search.Establishment search results.
///
- public SearchByKeywordResponse(IReadOnlyCollection? establishments)
+ public SearchByKeywordResponse(IReadOnlyCollection establishments)
{
EstablishmentResults = establishments;
}
diff --git a/Dfe.Data.SearchPrototype/SearchForEstablishments/SearchByKeywordUseCase.cs b/Dfe.Data.SearchPrototype/SearchForEstablishments/SearchByKeywordUseCase.cs
index 7a52e31..bd81c5a 100644
--- a/Dfe.Data.SearchPrototype/SearchForEstablishments/SearchByKeywordUseCase.cs
+++ b/Dfe.Data.SearchPrototype/SearchForEstablishments/SearchByKeywordUseCase.cs
@@ -1,5 +1,6 @@
using Dfe.Data.SearchPrototype.Common.Mappers;
using Dfe.Data.SearchPrototype.Common.CleanArchitecture.Application.UseCase;
+using Dfe.Data.SearchPrototype.SearchForEstablishments.Models;
namespace Dfe.Data.SearchPrototype.SearchForEstablishments;
@@ -47,11 +48,24 @@ public SearchByKeywordUseCase(
///
public async Task HandleRequest(SearchByKeywordRequest request)
{
- ArgumentNullException.ThrowIfNull(request, nameof(SearchByKeywordRequest));
- ArgumentNullException.ThrowIfNull(request.Context, nameof(SearchContext));
+ if ((request == null) || (request.Context == null)) {
+ return new SearchByKeywordResponse()
+ {
+ Status = SearchResponseStatus.InvalidRequest
+ };
+ };
- EstablishmentResults establishmentResults = await _searchServiceAdapter.SearchAsync(request.Context);
-
- return _resultsToResponseMapper.MapFrom(establishmentResults);
+ try
+ {
+ EstablishmentResults establishmentResults = await _searchServiceAdapter.SearchAsync(request.Context);
+ return _resultsToResponseMapper.MapFrom(establishmentResults);
+ }
+ catch (Exception) // something went wrong in the infrastructure
+ {
+ return new SearchByKeywordResponse()
+ {
+ Status = SearchResponseStatus.SearchServiceError
+ };
+ }
}
}
diff --git a/Dfe.Data.SearchPrototype/SearchForEstablishments/SearchResponseStatus.cs b/Dfe.Data.SearchPrototype/SearchForEstablishments/SearchResponseStatus.cs
new file mode 100644
index 0000000..623f2f0
--- /dev/null
+++ b/Dfe.Data.SearchPrototype/SearchForEstablishments/SearchResponseStatus.cs
@@ -0,0 +1,8 @@
+namespace Dfe.Data.SearchPrototype.SearchForEstablishments;
+
+public enum SearchResponseStatus
+{
+ Success,
+ InvalidRequest,
+ SearchServiceError
+}
diff --git a/Dfe.Data.SearchPrototype/SearchForEstablishments/StatusCode.cs b/Dfe.Data.SearchPrototype/SearchForEstablishments/StatusCode.cs
deleted file mode 100644
index d4bce65..0000000
--- a/Dfe.Data.SearchPrototype/SearchForEstablishments/StatusCode.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace Dfe.Data.SearchPrototype.SearchForEstablishments;
-
-public enum StatusCode
-{
- Closed = 0,
- Open = 1,
- Unknown
-}
\ No newline at end of file
diff --git a/Dfe.Data.SearchPrototype/Tests/SearchForEstablishments/ResultsToResponseMapperTests.cs b/Dfe.Data.SearchPrototype/Tests/SearchForEstablishments/ResultsToResponseMapperTests.cs
index db8762e..efbe368 100644
--- a/Dfe.Data.SearchPrototype/Tests/SearchForEstablishments/ResultsToResponseMapperTests.cs
+++ b/Dfe.Data.SearchPrototype/Tests/SearchForEstablishments/ResultsToResponseMapperTests.cs
@@ -1,5 +1,6 @@
using Dfe.Data.SearchPrototype.Common.Mappers;
using Dfe.Data.SearchPrototype.SearchForEstablishments;
+using Dfe.Data.SearchPrototype.SearchForEstablishments.Models;
using Dfe.Data.SearchPrototype.Tests.SearchForEstablishments.TestDoubles;
using FluentAssertions;
using Xunit;
@@ -13,15 +14,29 @@ public void MapFrom_ValidInput_ReturnsCorrectResponse()
{
// arrange.
EstablishmentResults input = EstablishmentResultsTestDouble.Create();
+ IMapper mapper = new ResultsToResponseMapper();
// act.
- IMapper mapper = new ResultsToResponseMapper();
SearchByKeywordResponse response = mapper.MapFrom(input);
//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);
}
+
+ [Fact]
+ public void MapFrom_NullInput_ReturnsErrorResponse()
+ {
+ // arrange.
+ IMapper mapper = new ResultsToResponseMapper();
+
+ // act
+ SearchByKeywordResponse response = mapper.MapFrom(null!);
+
+ // assert
+ response.Status.Should().Be(SearchResponseStatus.SearchServiceError);
+ }
}
diff --git a/Dfe.Data.SearchPrototype/Tests/SearchForEstablishments/SearchByKeywordUseCaseTests.cs b/Dfe.Data.SearchPrototype/Tests/SearchForEstablishments/SearchByKeywordUseCaseTests.cs
index a8bf26b..bf22945 100644
--- a/Dfe.Data.SearchPrototype/Tests/SearchForEstablishments/SearchByKeywordUseCaseTests.cs
+++ b/Dfe.Data.SearchPrototype/Tests/SearchForEstablishments/SearchByKeywordUseCaseTests.cs
@@ -1,7 +1,9 @@
using Dfe.Data.SearchPrototype.Common.Mappers;
using Dfe.Data.SearchPrototype.SearchForEstablishments;
+using Dfe.Data.SearchPrototype.SearchForEstablishments.Models;
using Dfe.Data.SearchPrototype.Tests.SearchForEstablishments.TestDoubles;
using FluentAssertions;
+using Moq;
using Xunit;
namespace Dfe.Data.SearchPrototype.Tests.SearchForEstablishments;
@@ -9,20 +11,22 @@ namespace Dfe.Data.SearchPrototype.Tests.SearchForEstablishments;
public sealed class SearchByKeywordUseCaseTests
{
private readonly SearchByKeywordUseCase _useCase;
+ private ISearchServiceAdapter _searchServiceAdapter;
+ private IMapper _mapper;
public SearchByKeywordUseCaseTests()
{
// arrange
- ISearchServiceAdapter searchServiceAdapter =
+ _searchServiceAdapter =
SearchServiceAdapterTestDouble.MockFor(
EstablishmentResultsTestDouble.Create());
- IMapper mapper = new ResultsToResponseMapper();
- _useCase = new(searchServiceAdapter, mapper);
+ _mapper = new ResultsToResponseMapper();
+ _useCase = new(_searchServiceAdapter, _mapper);
}
[Fact]
- public async Task UseCase_ValidRequest_ReturnsResponse()
+ public async Task HandleRequest_ValidRequest_ReturnsResponse()
{
// arrange
SearchByKeywordRequest request = new("searchkeyword", "target collection");
@@ -31,18 +35,51 @@ public async Task UseCase_ValidRequest_ReturnsResponse()
SearchByKeywordResponse response = await _useCase.HandleRequest(request);
// assert
- response.Should().NotBeNull();
+ response.Status.Should().Be(SearchResponseStatus.Success);
+ }
+
+ [Fact]
+ public async Task HandleRequest_NullSearchByKeywordRequest_ReturnsErrorStatus()
+ {
+ // act
+ var response = await _useCase.HandleRequest(request: null!);
+
+ // assert
+ response.Status.Should()
+ .Be(SearchResponseStatus.InvalidRequest);
+ }
+
+ [Fact]
+ public async Task HandleRequest_ServiceAdapterThrowsException_ReturnsErrorStatus()
+ {
+ // arrange
+ SearchByKeywordRequest request = new("searchkeyword", "target collection");
+ Mock.Get(_searchServiceAdapter)
+ .Setup(adapter => adapter.SearchAsync(It.IsAny()))
+ .ThrowsAsync(new ApplicationException());
+
+ // act
+ var response = await _useCase.HandleRequest(request);
+
+ // assert
+ response.Status.Should()
+ .Be(SearchResponseStatus.SearchServiceError);
}
[Fact]
- public Task UseCase_NullSearchByKeywordRequest_ThrowsArgumentNullException()
+ public async Task HandleRequest_NoResults_ReturnsSuccess()
{
- // act, assert
- return _useCase.Invoking(
- async usecase => await usecase
- .HandleRequest(request: null!))
- .Should()
- .ThrowAsync()
- .WithMessage("Value cannot be null. (Parameter 'SearchByKeywordRequest')");
+ // arrange
+ SearchByKeywordRequest request = new("searchkeyword", "target collection");
+ Mock.Get(_searchServiceAdapter)
+ .Setup(adapter => adapter.SearchAsync(It.IsAny()))
+ .ReturnsAsync(EstablishmentResultsTestDouble.CreateWithNoResults);
+
+ // act
+ var response = await _useCase.HandleRequest(request);
+
+ // assert
+ response.Status.Should()
+ .Be(SearchResponseStatus.Success);
}
}
diff --git a/Dfe.Data.SearchPrototype/Tests/SearchForEstablishments/TestDoubles/EstablishmentResultsTestDouble.cs b/Dfe.Data.SearchPrototype/Tests/SearchForEstablishments/TestDoubles/EstablishmentResultsTestDouble.cs
index 55ca2c2..a609df6 100644
--- a/Dfe.Data.SearchPrototype/Tests/SearchForEstablishments/TestDoubles/EstablishmentResultsTestDouble.cs
+++ b/Dfe.Data.SearchPrototype/Tests/SearchForEstablishments/TestDoubles/EstablishmentResultsTestDouble.cs
@@ -1,4 +1,4 @@
-using Dfe.Data.SearchPrototype.SearchForEstablishments;
+using Dfe.Data.SearchPrototype.SearchForEstablishments.Models;
namespace Dfe.Data.SearchPrototype.Tests.SearchForEstablishments.TestDoubles;
diff --git a/Dfe.Data.SearchPrototype/Tests/SearchForEstablishments/TestDoubles/EstablishmentTestDouble.cs b/Dfe.Data.SearchPrototype/Tests/SearchForEstablishments/TestDoubles/EstablishmentTestDouble.cs
index 503f7b3..90b1ec0 100644
--- a/Dfe.Data.SearchPrototype/Tests/SearchForEstablishments/TestDoubles/EstablishmentTestDouble.cs
+++ b/Dfe.Data.SearchPrototype/Tests/SearchForEstablishments/TestDoubles/EstablishmentTestDouble.cs
@@ -1,5 +1,5 @@
using Bogus;
-using Dfe.Data.SearchPrototype.SearchForEstablishments;
+using Dfe.Data.SearchPrototype.SearchForEstablishments.Models;
using System.IO;
namespace Dfe.Data.SearchPrototype.Tests.SearchForEstablishments.TestDoubles;
@@ -30,8 +30,8 @@ private static string GetEstablishmentPostcodeFake() =>
private static string GetEstablishmentTypeFake() =>
new Faker().Random.Word();
- private static StatusCode GetEstablishmentStatusCodeFake() =>
- (StatusCode)new Faker().Random.Int(0, 2);
+ private static EstablishmentStatusCode GetEstablishmentStatusCodeFake() =>
+ (EstablishmentStatusCode)new Faker().Random.Int(0, 2);
private static string GetEstablishmentEducationPhaseFake() =>
new Faker().Random.Int(0, 1).ToString();
diff --git a/Dfe.Data.SearchPrototype/Tests/SearchForEstablishments/TestDoubles/SearchServiceAdapterTestDouble.cs b/Dfe.Data.SearchPrototype/Tests/SearchForEstablishments/TestDoubles/SearchServiceAdapterTestDouble.cs
index ee8b422..d8a6d96 100644
--- a/Dfe.Data.SearchPrototype/Tests/SearchForEstablishments/TestDoubles/SearchServiceAdapterTestDouble.cs
+++ b/Dfe.Data.SearchPrototype/Tests/SearchForEstablishments/TestDoubles/SearchServiceAdapterTestDouble.cs
@@ -1,4 +1,5 @@
using Dfe.Data.SearchPrototype.SearchForEstablishments;
+using Dfe.Data.SearchPrototype.SearchForEstablishments.Models;
using Moq;
namespace Dfe.Data.SearchPrototype.Tests.SearchForEstablishments.TestDoubles;