Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added common code module project and moved across clean architecture and mapper interfaces #23

Merged
merged 2 commits into from
Jul 29, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions Dfe.Data.SearchPrototype.sln
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dfe.Data.SearchPrototype.We
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NuGet", "NuGet", "{0D4ACC06-9FA9-422A-8F92-92B565879E16}"
ProjectSection(SolutionItems) = preProject
NuGet\DfE.Data.ComponentLibrary.CleanArchitecture.2.0.37-beta-ci-20240610-104522.nupkg = NuGet\DfE.Data.ComponentLibrary.CleanArchitecture.2.0.37-beta-ci-20240610-104522.nupkg
NuGet\DfE.Data.ComponentLibrary.CrossCuttingConcerns.Mapping.2.0.37-beta-ci-20240610-104522.nupkg = NuGet\DfE.Data.ComponentLibrary.CrossCuttingConcerns.Mapping.2.0.37-beta-ci-20240610-104522.nupkg
NuGet\DfE.Data.ComponentLibrary.CrossCuttingConcerns.Json.2.0.37-beta-ci-20240610-104522.nupkg = NuGet\DfE.Data.ComponentLibrary.CrossCuttingConcerns.Json.2.0.37-beta-ci-20240610-104522.nupkg
NuGet\DfE.Data.ComponentLibrary.Infrastructure.CognitiveSearch.2.0.37-beta-ci-20240610-104522.nupkg = NuGet\DfE.Data.ComponentLibrary.Infrastructure.CognitiveSearch.2.0.37-beta-ci-20240610-104522.nupkg
EndProjectSection
Expand All @@ -23,6 +21,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dfe.Data.SearchPrototype.In
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dfe.Data.SearchPrototype.Infrastructure.Tests", "Dfe.Data.SearchPrototype\Infrastructure\Tests\Dfe.Data.SearchPrototype.Infrastructure.Tests.csproj", "{62F6B827-75B0-43A6-9323-297F32016D71}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dfe.Data.SearchPrototype.Common", "Dfe.Data.SearchPrototype\Common\Dfe.Data.SearchPrototype.Common.csproj", "{641005EB-1F41-466C-80C2-FA6F59572474}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dfe.Data.SearchPrototype.Common.Tests", "Dfe.Data.SearchPrototype\Common\Tests\Dfe.Data.SearchPrototype.Common.Tests.csproj", "{78EF8B69-F1FB-461B-8872-18FA7F285305}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -53,6 +55,14 @@ Global
{62F6B827-75B0-43A6-9323-297F32016D71}.Debug|Any CPU.Build.0 = Debug|Any CPU
{62F6B827-75B0-43A6-9323-297F32016D71}.Release|Any CPU.ActiveCfg = Release|Any CPU
{62F6B827-75B0-43A6-9323-297F32016D71}.Release|Any CPU.Build.0 = Release|Any CPU
{641005EB-1F41-466C-80C2-FA6F59572474}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{641005EB-1F41-466C-80C2-FA6F59572474}.Debug|Any CPU.Build.0 = Debug|Any CPU
{641005EB-1F41-466C-80C2-FA6F59572474}.Release|Any CPU.ActiveCfg = Release|Any CPU
{641005EB-1F41-466C-80C2-FA6F59572474}.Release|Any CPU.Build.0 = Release|Any CPU
{78EF8B69-F1FB-461B-8872-18FA7F285305}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{78EF8B69-F1FB-461B-8872-18FA7F285305}.Debug|Any CPU.Build.0 = Debug|Any CPU
{78EF8B69-F1FB-461B-8872-18FA7F285305}.Release|Any CPU.ActiveCfg = Release|Any CPU
{78EF8B69-F1FB-461B-8872-18FA7F285305}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
namespace Dfe.Data.SearchPrototype.Common.CleanArchitecture.Application.UseCase;

/// <summary>
/// Contract which defines the behaviour for a use-case input port without any request params.
/// </summary>
/// <typeparam name="TUseCaseResponse">The runtime type definition of the use-case response object.</typeparam>
public interface IUseCase<TUseCaseResponse>
{
/// <summary>
/// Async method to define how a use-case handles a given request
/// from an exterior source and the appropriate result returned.
/// </summary>
/// <returns>The runtime type definition of the use-case response.</returns>
Task<TUseCaseResponse> HandleRequest();
}

/// <summary>
/// Implement when handling use-case requests when a request object (with parameters) is required.
/// </summary>
/// <typeparam name="TUseCaseRequest">The type of request (input) passed to a given use case.</typeparam>
/// <typeparam name="TUseCaseResponse">The type of response (output) passed back from the given use case.</typeparam>
public interface IUseCase<in TUseCaseRequest, TUseCaseResponse>
{
/// <summary>
/// The invocation of the input-port handle that must be implemented by
/// a use-case that prescribes to the contract. The handle ensures that
/// the use-case-request object prescribed at run-time is correctly handled.
/// </summary>
/// <param name="request">The type of request passed to a given use case.</param>
/// <returns>The type of request object to send to the input-port, defined at run-time.</returns>
Task<TUseCaseResponse> HandleRequest(TUseCaseRequest request);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Dfe.Data.SearchPrototype.Common.CleanArchitecture.Application.UseCase;

/// <summary>
/// Contract which defines the response object to be associated with a use-case request.
/// </summary>
/// <typeparam name="TUseCaseResponse">The runtime type definition of the use-case response object.</typeparam>
public interface IUseCaseRequest<out TUseCaseResponse> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<Compile Remove="Tests\**" />
<EmbeddedResource Remove="Tests\**" />
<None Remove="Tests\**" />
</ItemGroup>

</Project>
18 changes: 18 additions & 0 deletions Dfe.Data.SearchPrototype/Common/Mapper/IMapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
namespace Dfe.Data.SearchPrototype.Common.Mappers;

/// <summary>
///
/// </summary>
/// <typeparam name="TMapFrom"></typeparam>
/// <typeparam name="TMapTo"></typeparam>
public interface IMapper<in TMapFrom, out TMapTo>
{
/// <summary>
///
/// </summary>
/// <param name="input">
///
/// </param>
/// <returns></returns>
TMapTo MapFrom(TMapFrom input);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Dfe.Data.SearchPrototype.Common.CleanArchitecture.Application.UseCase;
using Moq;

namespace Dfe.Data.SearchPrototype.Common.Tests.CleanArchitecture.Application.UseCase.TestDoubles;

public static class UseCaseRequestTestDoubles
{
public static Mock<IUseCaseRequest<TUseCaseReponse>> UseCaseRequestMock<
TUseCaseReponse>() => new();

public static IUseCaseRequest<TUseCaseReponse> MockUseCaseRequest<
TUseCaseReponse>() =>
UseCaseRequestMock<TUseCaseReponse>().Object;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using Dfe.Data.SearchPrototype.Common.CleanArchitecture.Application.UseCase;
using Moq;
using System.Dynamic;
using Bogus;

namespace Dfe.Data.SearchPrototype.Common.Tests.CleanArchitecture.Application.UseCase.TestDoubles;

public static class UseCaseTestDoubles
{
public static Mock<IUseCase<TUseCaseReponseMock>> UseCaseMock<
TUseCaseReponseMock>() => new();

public static Mock<IUseCase<IUseCaseRequest<
TUseCaseReponse>, TUseCaseReponse>> UseCaseMockWithArg<TUseCaseReponse>() => new();

public static IUseCase<TUseCaseReponseMock> MockUseCaseFor<
TUseCaseReponseMock>(TUseCaseReponseMock useCaseReponse)
{
var useCaseMock = UseCaseMock<TUseCaseReponseMock>();

useCaseMock
.Setup(useCase =>
useCase.HandleRequest())
.Returns(MockUseCaseResponseFor(useCaseReponse));

return useCaseMock.Object;
}

public static IUseCase<IUseCaseRequest<TUseCaseReponse>, TUseCaseReponse> MockUseCaseWithArgFor<
TUseCaseReponse>(IUseCaseRequest<TUseCaseReponse> useCaseRequest, TUseCaseReponse useCaseReponse)
{
var useCaseWithArgMock = UseCaseMockWithArg<TUseCaseReponse>();

useCaseWithArgMock
.Setup(useCase =>
useCase.HandleRequest(useCaseRequest))
.Returns(MockUseCaseResponseFor(useCaseReponse));

return useCaseWithArgMock.Object;
}

public static Task<TUseCaseResponse> MockUseCaseResponseFor<
TUseCaseResponse>(TUseCaseResponse useCaseResponseFake) =>
Task.FromResult(useCaseResponseFake);

public static ExpandoObject DefaultUseCaseResponseFake<TUseCaseResponse>(
TUseCaseResponse useCaseResponse)
{
dynamic useCaseResponsefake = new ExpandoObject();
useCaseResponsefake.UseCaseReponse = useCaseResponse;
return useCaseResponsefake;
}

public static string DefaultUseCaseResponseMessage => new Faker().Random.Words(count: 10);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@

using Dfe.Data.SearchPrototype.Common.CleanArchitecture.Application.UseCase;
using Dfe.Data.SearchPrototype.Common.Tests.CleanArchitecture.Application.UseCase.TestDoubles;
using System.Dynamic;
using Xunit;

namespace Dfe.Data.SearchPrototype.Common.Tests.CleanArchitecture.Application.UseCase;

public class UseCaseTests
{
[Fact]
public void UseCase_HandleRequest_No_Args_ReturnsExpectedUseCaseReponse()
{
// arrange
var useCaseResponseMessage =
UseCaseTestDoubles.DefaultUseCaseResponseMessage;
var useCaseResponsefake =
UseCaseTestDoubles.DefaultUseCaseResponseFake(useCaseResponseMessage);
IUseCase<ExpandoObject> useCaseMock =
UseCaseTestDoubles.MockUseCaseFor(useCaseResponsefake);
spanersoraferty marked this conversation as resolved.
Show resolved Hide resolved

// act
var useCaseResponse = useCaseMock.HandleRequest();

// assert
Assert.Same(
useCaseResponseMessage,
useCaseResponse.Result.FirstOrDefault<string>("UseCaseReponse"));
}

[Fact]
public void UseCase_HandleRequest_With_Args_ReturnsExpectedUseCaseReponse()
{
// arrange
var useCaseResponseMessage =
UseCaseTestDoubles.DefaultUseCaseResponseMessage;
var useCaseResponsefake =
UseCaseTestDoubles.DefaultUseCaseResponseFake(useCaseResponseMessage);
var useCaseRequestMock =
UseCaseRequestTestDoubles.MockUseCaseRequest<ExpandoObject>();
IUseCase<IUseCaseRequest<ExpandoObject>, ExpandoObject> useCaseWithArgMock =
UseCaseTestDoubles.MockUseCaseWithArgFor(useCaseRequestMock, useCaseResponsefake);

// act
var useCaseResponse =
useCaseWithArgMock.HandleRequest(useCaseRequestMock);

// assert
Assert.Same(
useCaseResponseMessage,
useCaseResponse.Result.FirstOrDefault<string>("UseCaseReponse"));
}
}

public static class ExpandoObjectExtensions
{
public static TUnderlyingType? FirstOrDefault<TUnderlyingType>(
this ExpandoObject expandoObject, string key)
{
object? underlyingObject =
expandoObject.FirstOrDefault(kvp =>
kvp.Key.Trim() == key.Trim()).Value;

return (underlyingObject is TUnderlyingType type) ?
type : default;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Dfe.Data.SearchPrototype.Common.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Bogus" Version="34.0.2" />
<PackageReference Include="FluentAssertions" Version="6.11.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="6.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
<PackageReference Include="Moq" Version="4.18.4" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.1.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>

</Project>
7 changes: 7 additions & 0 deletions Dfe.Data.SearchPrototype/Dfe.Data.SearchPrototype.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,19 @@

<ItemGroup>
<Compile Remove="Application\**" />
<Compile Remove="Common\**" />
<Compile Remove="Dfe.Data.SearchPrototype\**" />
<Compile Remove="Infrastructure\**" />
<Compile Remove="Tests\**" />
<Compile Remove="Web\**" />
<EmbeddedResource Remove="Application\**" />
<EmbeddedResource Remove="Common\**" />
<EmbeddedResource Remove="Dfe.Data.SearchPrototype\**" />
<EmbeddedResource Remove="Infrastructure\**" />
<EmbeddedResource Remove="Tests\**" />
<EmbeddedResource Remove="Web\**" />
<None Remove="Application\**" />
<None Remove="Common\**" />
<None Remove="Dfe.Data.SearchPrototype\**" />
<None Remove="Infrastructure\**" />
<None Remove="Tests\**" />
Expand All @@ -32,5 +35,9 @@
<PackageReference Include="DfE.Data.ComponentLibrary.CleanArchitecture" Version="2.0.37-beta-CI-20240610-104522" />
<PackageReference Include="DfE.Data.ComponentLibrary.CrossCuttingConcerns.Mapping" Version="2.0.37-beta-CI-20240610-104522" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="Common\Dfe.Data.SearchPrototype.Common.csproj" />
</ItemGroup>

</Project>
Loading