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 Marten Application Layer and Optimistic Concurrency Tests #242

Merged
merged 10 commits into from
May 1, 2024
Merged
43 changes: 34 additions & 9 deletions Core/Validation/ValidationExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,42 +1,67 @@
using System.Runtime.CompilerServices;
using System.Diagnostics.CodeAnalysis;
using System.Numerics;
using System.Runtime.CompilerServices;

namespace Core.Validation;

public static class ValidationExtensions
{
public static T AssertNotNull<T>(this T? value, [CallerArgumentExpression("value")] string? paramName = null)
public static T NotNull<T>(this T? value, [CallerArgumentExpression("value")] string? paramName = null)
where T : struct
{
if (value == null)
throw new ArgumentNullException(paramName);

return (T)value;
return value.Value;
}

public static string AssertNotEmpty(this string? value, [CallerArgumentExpression("value")] string? paramName = null)
public static T NotNull<T>(this T? value, [CallerArgumentExpression("value")] string? paramName = null)
where T : class
{
if (value == null)
throw new ArgumentNullException(paramName);

return value;
}

public static string NotEmpty(this string? value, [CallerArgumentExpression("value")] string? paramName = null)
=> !string.IsNullOrWhiteSpace(value) ? value : throw new ArgumentOutOfRangeException(paramName);

public static T AssertNotEmpty<T>(this T value, [CallerArgumentExpression("value")] string? paramName = null)
public static Guid NotEmpty(this Guid? value, [CallerArgumentExpression("value")] string? paramName = null)
=> value!= null && value != Guid.Empty ? value.Value : throw new ArgumentOutOfRangeException(paramName);


public static T NotEmpty<T>(this T value, [CallerArgumentExpression("value")] string? paramName = null)
where T : struct
=> AssertNotEmpty((T?)value, paramName);
=> NotEmpty((T?)value, paramName);

public static T AssertNotEmpty<T>(this T? value, [CallerArgumentExpression("value")] string? paramName = null)
public static T NotEmpty<T>(this T? value, [CallerArgumentExpression("value")] string? paramName = null)
where T : struct
{
var notNullValue = value.AssertNotNull(paramName);
var notNullValue = value.NotNull(paramName);

if (Equals(notNullValue, default(T)))
throw new ArgumentOutOfRangeException(paramName);

return notNullValue;
}

public static T AssertGreaterOrEqualThan<T>(this T value, T valueToCompare, [CallerArgumentExpression("value")] string? paramName = null)
public static T GreaterOrEqualThan<T>(this T value, T valueToCompare, [CallerArgumentExpression("value")] string? paramName = null)
where T : IComparable<T>
{
if (value.CompareTo(valueToCompare) < 0)
throw new ArgumentOutOfRangeException(paramName);

return value;

}

public static T Positive<T>(this T value, [CallerArgumentExpression("value")] string? paramName = null)
where T : INumber<T>
{
if (value == null || value.CompareTo(Convert.ChangeType(0, typeof(T))) <= 0)
throw new ArgumentOutOfRangeException(paramName);

return value;
}
}
56 changes: 42 additions & 14 deletions EventSourcing.NetCore.sln
Original file line number Diff line number Diff line change
Expand Up @@ -293,10 +293,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "07-BusinessLogic", "Worksho
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "07-BusinessLogic", "Workshops\IntroductionToEventSourcing\Solved\07-BusinessLogic\07-BusinessLogic.csproj", "{2B630D31-F68C-447E-82C4-48E2B17322B5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "08-BusinessLogic.Marten", "Workshops\IntroductionToEventSourcing\08-BusinessLogic.Marten\08-BusinessLogic.Marten.csproj", "{AC4CF160-35DB-4C8D-9E01-AFACCBFAEE6D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "08-BusinessLogic.Marten", "Workshops\IntroductionToEventSourcing\Solved\08-BusinessLogic.Marten\08-BusinessLogic.Marten.csproj", "{2BD5BA1A-FD45-4582-88D2-8B92989DC71B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "09-BusinessLogic.EventStoreDB", "Workshops\IntroductionToEventSourcing\09-BusinessLogic.EventStoreDB\09-BusinessLogic.EventStoreDB.csproj", "{6A93A4C6-7C50-453A-B3DA-48812DEC1C0F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "09-BusinessLogic.EventStoreDB", "Workshops\IntroductionToEventSourcing\Solved\09-BusinessLogic.EventStoreDB\09-BusinessLogic.EventStoreDB.csproj", "{26E884E8-4E81-4C87-A643-FE7327F218CE}"
Expand Down Expand Up @@ -451,6 +447,18 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PointOfSales.Api", "Sample\
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PointOfSales.Api.Tests", "Sample\ClosingTheBooks\PointOfSales.Api.Tests\PointOfSales.Api.Tests.csproj", "{42510FFD-04F5-4580-9B02-9CBA260718DC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "08-ApplicationLogic.Marten", "Workshops\IntroductionToEventSourcing\08-ApplicationLogic.Marten\08-ApplicationLogic.Marten.csproj", "{419234A7-EDE2-4861-A89B-4E29A579BF12}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "08-ApplicationLogic.Marten.Tests", "Workshops\IntroductionToEventSourcing\08-ApplicationLogic.Marten.Tests\08-ApplicationLogic.Marten.Tests.csproj", "{F71C0ED1-0BC5-4B51-A979-605ADF9104AB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "08-ApplicationLogic.Marten", "Workshops\IntroductionToEventSourcing\Solved\08-ApplicationLogic.Marten\08-ApplicationLogic.Marten.csproj", "{62FE6E28-7226-4CFB-A9F7-1B532FDEE6F9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "08-ApplicationLogic.Marten.Tests", "Workshops\IntroductionToEventSourcing\Solved\08-ApplicationLogic.Marten.Tests\08-ApplicationLogic.Marten.Tests.csproj", "{9A07F586-2D4B-46F4-AF8F-AF8290AE4A73}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "10-OptimisticConcurrency.Marten.Tests", "Workshops\IntroductionToEventSourcing\10-OptimisticConcurrency.Marten.Tests\10-OptimisticConcurrency.Marten.Tests.csproj", "{E03F9212-A1C3-4E3A-8DB6-C2FE05130C03}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "10-OptimisticConcurrency.Marten.Tests", "Workshops\IntroductionToEventSourcing\Solved\10-OptimisticConcurrency.Marten.Tests\10-OptimisticConcurrency.Marten.Tests.csproj", "{44BE49F6-8F70-48B7-8AC0-4B99DF855341}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -797,14 +805,6 @@ Global
{2B630D31-F68C-447E-82C4-48E2B17322B5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2B630D31-F68C-447E-82C4-48E2B17322B5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2B630D31-F68C-447E-82C4-48E2B17322B5}.Release|Any CPU.Build.0 = Release|Any CPU
{AC4CF160-35DB-4C8D-9E01-AFACCBFAEE6D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AC4CF160-35DB-4C8D-9E01-AFACCBFAEE6D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AC4CF160-35DB-4C8D-9E01-AFACCBFAEE6D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AC4CF160-35DB-4C8D-9E01-AFACCBFAEE6D}.Release|Any CPU.Build.0 = Release|Any CPU
{2BD5BA1A-FD45-4582-88D2-8B92989DC71B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2BD5BA1A-FD45-4582-88D2-8B92989DC71B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2BD5BA1A-FD45-4582-88D2-8B92989DC71B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2BD5BA1A-FD45-4582-88D2-8B92989DC71B}.Release|Any CPU.Build.0 = Release|Any CPU
{6A93A4C6-7C50-453A-B3DA-48812DEC1C0F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6A93A4C6-7C50-453A-B3DA-48812DEC1C0F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6A93A4C6-7C50-453A-B3DA-48812DEC1C0F}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down Expand Up @@ -997,6 +997,30 @@ Global
{42510FFD-04F5-4580-9B02-9CBA260718DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{42510FFD-04F5-4580-9B02-9CBA260718DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{42510FFD-04F5-4580-9B02-9CBA260718DC}.Release|Any CPU.Build.0 = Release|Any CPU
{419234A7-EDE2-4861-A89B-4E29A579BF12}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{419234A7-EDE2-4861-A89B-4E29A579BF12}.Debug|Any CPU.Build.0 = Debug|Any CPU
{419234A7-EDE2-4861-A89B-4E29A579BF12}.Release|Any CPU.ActiveCfg = Release|Any CPU
{419234A7-EDE2-4861-A89B-4E29A579BF12}.Release|Any CPU.Build.0 = Release|Any CPU
{F71C0ED1-0BC5-4B51-A979-605ADF9104AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F71C0ED1-0BC5-4B51-A979-605ADF9104AB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F71C0ED1-0BC5-4B51-A979-605ADF9104AB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F71C0ED1-0BC5-4B51-A979-605ADF9104AB}.Release|Any CPU.Build.0 = Release|Any CPU
{62FE6E28-7226-4CFB-A9F7-1B532FDEE6F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{62FE6E28-7226-4CFB-A9F7-1B532FDEE6F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{62FE6E28-7226-4CFB-A9F7-1B532FDEE6F9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{62FE6E28-7226-4CFB-A9F7-1B532FDEE6F9}.Release|Any CPU.Build.0 = Release|Any CPU
{9A07F586-2D4B-46F4-AF8F-AF8290AE4A73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9A07F586-2D4B-46F4-AF8F-AF8290AE4A73}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9A07F586-2D4B-46F4-AF8F-AF8290AE4A73}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9A07F586-2D4B-46F4-AF8F-AF8290AE4A73}.Release|Any CPU.Build.0 = Release|Any CPU
{E03F9212-A1C3-4E3A-8DB6-C2FE05130C03}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E03F9212-A1C3-4E3A-8DB6-C2FE05130C03}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E03F9212-A1C3-4E3A-8DB6-C2FE05130C03}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E03F9212-A1C3-4E3A-8DB6-C2FE05130C03}.Release|Any CPU.Build.0 = Release|Any CPU
{44BE49F6-8F70-48B7-8AC0-4B99DF855341}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{44BE49F6-8F70-48B7-8AC0-4B99DF855341}.Debug|Any CPU.Build.0 = Debug|Any CPU
{44BE49F6-8F70-48B7-8AC0-4B99DF855341}.Release|Any CPU.ActiveCfg = Release|Any CPU
{44BE49F6-8F70-48B7-8AC0-4B99DF855341}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -1110,8 +1134,6 @@ Global
{0FE43045-AC67-4DB9-9820-82C9D55EA09A} = {14C7B928-9D6C-441A-8A1F-0C49173E73EB}
{2FFCF9EC-9C75-497B-BC94-B0051367780F} = {14C7B928-9D6C-441A-8A1F-0C49173E73EB}
{2B630D31-F68C-447E-82C4-48E2B17322B5} = {65F6E2BE-B2D4-4E56-B0CB-3062C4882B9E}
{AC4CF160-35DB-4C8D-9E01-AFACCBFAEE6D} = {14C7B928-9D6C-441A-8A1F-0C49173E73EB}
{2BD5BA1A-FD45-4582-88D2-8B92989DC71B} = {65F6E2BE-B2D4-4E56-B0CB-3062C4882B9E}
{6A93A4C6-7C50-453A-B3DA-48812DEC1C0F} = {14C7B928-9D6C-441A-8A1F-0C49173E73EB}
{26E884E8-4E81-4C87-A643-FE7327F218CE} = {65F6E2BE-B2D4-4E56-B0CB-3062C4882B9E}
{DFC5BD15-8718-469A-B0FE-D21E473F1133} = {14C7B928-9D6C-441A-8A1F-0C49173E73EB}
Expand Down Expand Up @@ -1180,6 +1202,12 @@ Global
{F40DF11A-354C-4438-B674-B95B9A401C33} = {C8F02DB9-5FEA-46C8-95E3-BB4255CB0667}
{CE15C7EC-85CA-44B8-B13B-206E308E8EF8} = {C8F02DB9-5FEA-46C8-95E3-BB4255CB0667}
{42510FFD-04F5-4580-9B02-9CBA260718DC} = {C8F02DB9-5FEA-46C8-95E3-BB4255CB0667}
{419234A7-EDE2-4861-A89B-4E29A579BF12} = {14C7B928-9D6C-441A-8A1F-0C49173E73EB}
{F71C0ED1-0BC5-4B51-A979-605ADF9104AB} = {14C7B928-9D6C-441A-8A1F-0C49173E73EB}
{62FE6E28-7226-4CFB-A9F7-1B532FDEE6F9} = {65F6E2BE-B2D4-4E56-B0CB-3062C4882B9E}
{9A07F586-2D4B-46F4-AF8F-AF8290AE4A73} = {65F6E2BE-B2D4-4E56-B0CB-3062C4882B9E}
{E03F9212-A1C3-4E3A-8DB6-C2FE05130C03} = {14C7B928-9D6C-441A-8A1F-0C49173E73EB}
{44BE49F6-8F70-48B7-8AC0-4B99DF855341} = {65F6E2BE-B2D4-4E56-B0CB-3062C4882B9E}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A5F55604-2FF3-43B7-B657-4F18E6E95D3B}
Expand Down
6 changes: 0 additions & 6 deletions Sample/Helpdesk.Wolverine/Helpdesk.Api/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,11 @@
using Helpdesk.Api.Core.Kafka;
using Helpdesk.Api.Core.SignalR;
using Helpdesk.Api.Incidents;
using Helpdesk.Api.Incidents.GettingCustomerIncidents;
using Helpdesk.Api.Incidents.GettingCustomerIncidentsSummary;
using Helpdesk.Api.Incidents.GettingDetails;
using Helpdesk.Api.Incidents.GettingHistory;
using Helpdesk.Api.Incidents.ResolutionBatch;
using JasperFx.CodeGeneration;
using Marten;
using Marten.Events.Daemon.Resiliency;
using Marten.Events.Projections;
using Marten.Exceptions;
using Marten.Services.Json;
using Microsoft.AspNetCore.Http.Json;
using Microsoft.AspNetCore.SignalR;
using Oakton;
Expand Down
2 changes: 1 addition & 1 deletion Sample/HotelManagement/Reservations/Guests/Guest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Reservations.Guests;
public record GuestExternalId(string Value)
{
public static GuestExternalId FromPrefix(string prefix, string externalId) =>
new($"{prefix.AssertNotEmpty()}/{externalId.AssertNotEmpty()}");
new($"{prefix.NotEmpty()}/{externalId.NotEmpty()}");
}

public record GuestId(string Value);
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ public static GetRoomTypeAvailabilityForPeriod Of(
DateOnly to
) =>
new(
roomType.AssertNotEmpty(),
from.AssertNotEmpty(),
to.AssertNotEmpty().AssertGreaterOrEqualThan(from)
roomType.NotEmpty(),
from.NotEmpty(),
to.NotEmpty().GreaterOrEqualThan(from)
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,13 @@ public static ReserveRoom FromApi(
IReadOnlyList<DailyRoomTypeAvailability> dailyAvailability
) =>
new(
id.AssertNotEmpty(),
roomType.AssertNotEmpty(),
from.AssertNotEmpty(),
to.AssertNotEmpty().AssertGreaterOrEqualThan(from),
guestId.AssertNotEmpty(),
numberOfPeople.AssertNotEmpty(),
now.AssertNotEmpty(),
id.NotEmpty(),
roomType.NotEmpty(),
from.NotEmpty(),
to.NotEmpty().GreaterOrEqualThan(from),
guestId.NotEmpty(),
numberOfPeople.NotEmpty(),
now.NotEmpty(),
ReservationSource.Api,
dailyAvailability,
null
Expand All @@ -73,15 +73,15 @@ public static ReserveRoom FromExternal(
DateTimeOffset now
) =>
new(
id.AssertNotEmpty(),
roomType.AssertNotEmpty(),
from.AssertNotEmpty(),
to.AssertNotEmpty().AssertGreaterOrEqualThan(from),
guestId.AssertNotEmpty(),
numberOfPeople.AssertNotEmpty(),
now.AssertNotEmpty(),
id.NotEmpty(),
roomType.NotEmpty(),
from.NotEmpty(),
to.NotEmpty().GreaterOrEqualThan(from),
guestId.NotEmpty(),
numberOfPeople.NotEmpty(),
now.NotEmpty(),
ReservationSource.External,
Array.Empty<DailyRoomTypeAvailability>(),
externalId.AssertNotEmpty()
externalId.NotEmpty()
);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Net;
using System.Net;
using Ogooreck.API;
using static Ogooreck.API.ApiSpecification;
using Warehouse.Products.RegisteringProduct;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ protected MartenTest()
var options = new StoreOptions();
options.Connection(
"PORT = 5432; HOST = localhost; TIMEOUT = 15; POOLING = True; DATABASE = 'postgres'; PASSWORD = 'Password12!'; USER ID = 'postgres'");
options.UseDefaultSerialization(nonPublicMembersStorage: NonPublicMembersStorage.All);
options.UseNewtonsoftForSerialization(nonPublicMembersStorage: NonPublicMembersStorage.All);
options.DatabaseSchemaName = options.Events.DatabaseSchemaName = "IntroductionToEventSourcing";

documentStore = new DocumentStore(options);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>ApplicationLogic.Marten.Tests</RootNamespace>
<AssemblyName>ApplicationLogic.Marten.Tests</AssemblyName>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="NSubstitute" Version="5.1.0" />
<PackageReference Include="xunit" Version="2.7.1" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="8.0.4" />
<PackageReference Include="Ogooreck" Version="0.8.0" />
<PackageReference Include="Bogus" Version="35.5.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\08-ApplicationLogic.Marten\08-ApplicationLogic.Marten.csproj" />
</ItemGroup>

<Import Project="..\..\..\Tests.Build.props" />

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using Oakton;
using Xunit;
using Xunit.Abstractions;
using Xunit.Sdk;

[assembly: TestFramework("ApplicationLogic.Marten.Tests.AssemblyFixture", "ApplicationLogic.Marten.Tests")]
[assembly: CollectionBehavior(DisableTestParallelization = true)]

namespace ApplicationLogic.Marten.Tests;

public sealed class AssemblyFixture : XunitTestFramework
{
public AssemblyFixture(IMessageSink messageSink)
:base(messageSink)
{
OaktonEnvironment.AutoStartHost = true;
}
}
Loading
Loading