Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 1 addition & 1 deletion .github/workflows/createrelease.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
dotnet test "TransactionProcessor.BusinessLogic.Tests\TransactionProcessor.BusinessLogic.Tests.csproj"
dotnet test "TransactionProcessor.ProjectionEngine.Tests\TransactionProcessor.ProjectionEngine.Tests.csproj"
dotnet test "TransactionProcessor.Aggregates.Tests\TransactionProcessor.Aggregates.Tests.csproj"
dotnet test "TransactionProcessor.Tests\TransactionProcessor.Tests.csproj"
dotnet test "TransactionProcessor.Tests\TransactionProcessor.Tests.csproj"

- name: Publish Images to Docker Hub - Pre Release
if: ${{ github.event.release.prerelease == true }}
Expand Down
5 changes: 3 additions & 2 deletions .github/workflows/nightlybuild.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,13 @@ jobs:
dotnet test "TransactionProcessor.ProjectionEngine.Tests\TransactionProcessor.ProjectionEngine.Tests.csproj" /p:CollectCoverage=true /p:Exclude="[xunit*]*" /p:ExcludeByAttribute="Obsolete" /p:ExcludeByAttribute="GeneratedCodeAttribute" /p:ExcludeByAttribute="CompilerGeneratedAttribute" /p:ExcludeByAttribute="ExcludeFromCodeCoverageAttribute" /p:CoverletOutput="../lcov2.info" /maxcpucount:1 /p:CoverletOutputFormat="lcov"
dotnet test "TransactionProcessor.Aggregates.Tests\TransactionProcessor.Aggregates.Tests.csproj" /p:CollectCoverage=true /p:Exclude="[xunit*]*" /p:ExcludeByAttribute="Obsolete" /p:ExcludeByAttribute="GeneratedCodeAttribute" /p:ExcludeByAttribute="CompilerGeneratedAttribute" /p:ExcludeByAttribute="ExcludeFromCodeCoverageAttribute" /p:CoverletOutput="../lcov3.info" /maxcpucount:1 /p:CoverletOutputFormat="lcov"
dotnet test "TransactionProcessor.Tests\TransactionProcessor.Tests.csproj" /p:CollectCoverage=true /p:Exclude="[xunit*]*" /p:ExcludeByAttribute="Obsolete" /p:ExcludeByAttribute="GeneratedCodeAttribute" /p:ExcludeByAttribute="CompilerGeneratedAttribute" /p:ExcludeByAttribute="ExcludeFromCodeCoverageAttribute" /p:CoverletOutput="../lcov4.info" /maxcpucount:1 /p:CoverletOutputFormat="lcov"

dotnet test "TransactionProcessor.DatabaseTests\TransactionProcessor.DatabaseTests.csproj" /p:CollectCoverage=true /p:Exclude="[xunit*]*" /p:ExcludeByAttribute="Obsolete" /p:ExcludeByAttribute="GeneratedCodeAttribute" /p:ExcludeByAttribute="CompilerGeneratedAttribute" /p:ExcludeByAttribute="ExcludeFromCodeCoverageAttribute" /p:CoverletOutput="../lcov5.info" /maxcpucount:1 /p:CoverletOutputFormat="lcov"

- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./lcov1.info,./lcov2.info,./lcov3.info,./lcov4.info
files: ./lcov1.info,./lcov2.info,./lcov3.info,./lcov4.info,./lcov5.info

- name: Build Docker Image
run: docker build . --file TransactionProcessor/Dockerfile --tag transactionprocessor:latest
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/pullrequest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ jobs:
dotnet test "TransactionProcessor.ProjectionEngine.Tests\TransactionProcessor.ProjectionEngine.Tests.csproj"
dotnet test "TransactionProcessor.Aggregates.Tests\TransactionProcessor.Aggregates.Tests.csproj"
dotnet test "TransactionProcessor.Tests\TransactionProcessor.Tests.csproj"
dotnet test "TransactionProcessor.DatabaseTests\TransactionProcessor.DatabaseTests.csproj"

- name: Build Docker Image
run: docker build . --file TransactionProcessor/Dockerfile --tag transactionprocessor:latest
Expand Down
128 changes: 128 additions & 0 deletions TransactionProcessor.DatabaseTests/BaseTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
using Ductus.FluentDocker.Services;
using Ductus.FluentDocker.Services.Extensions;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using NLog;
using Shared.EntityFramework;
using Shared.IntegrationTesting;
using Shared.Logger;
using Shouldly;
using SimpleResults;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using TransactionProcessor.Database.Contexts;
using TransactionProcessor.Repository;
using Xunit.Abstractions;
using Logger = Shared.Logger.Logger;
using NullLogger = Microsoft.Extensions.Logging.Abstractions.NullLogger;

namespace TransactionProcessor.DatabaseTests
{
public abstract class BaseTest : IAsyncLifetime
{
protected ITransactionProcessorReadModelRepository Repository;
protected ITestOutputHelper TestOutputHelper;
public virtual async Task InitializeAsync()
{
Logger.Initialise(new Shared.Logger.NullLogger());

this.TestId = Guid.NewGuid();

await this.StartSqlContainer();
await this.GetRepository();
EstateManagementContext context = this.GetContext();
await context.Database.EnsureCreatedAsync(CancellationToken.None);
}

public EstateManagementContext GetContext()
{
return new EstateManagementContext(GetLocalConnectionString($"TransactionProcessorReadModel-{this.TestId}"));
}

public async Task GetRepository()
{
String dbConnString = GetLocalConnectionString($"TransactionProcessorReadModel-{this.TestId}");

Mock<IDbContextResolver<EstateManagementContext>> resolver = new Mock<IDbContextResolver<EstateManagementContext>>();
resolver.Setup(r => r.Resolve(It.IsAny<string>(), It.IsAny<string>()))
.Returns(() =>
{
Mock<IServiceScope> innerScope = new Mock<IServiceScope>();
EstateManagementContext context = new EstateManagementContext(dbConnString);

innerScope.Setup(s => s.ServiceProvider.GetService(typeof(EstateManagementContext)))
.Returns(context);

return new ResolvedDbContext<EstateManagementContext>(innerScope.Object);
});

this.Repository = new TransactionProcessorReadModelRepository(resolver.Object);
}

public virtual async Task DisposeAsync()
{
}

//protected abstract Task ClearStandingData();

Check notice on line 73 in TransactionProcessor.DatabaseTests/BaseTest.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

TransactionProcessor.DatabaseTests/BaseTest.cs#L73

Remove this commented out code.
//protected abstract Task SetupStandingData();

protected Guid TestId;

public static IContainerService DatabaseServerContainer;

Check warning on line 78 in TransactionProcessor.DatabaseTests/BaseTest.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

TransactionProcessor.DatabaseTests/BaseTest.cs#L78

Change the visibility of 'DatabaseServerContainer' or make it 'const' or 'readonly'.

Check warning on line 78 in TransactionProcessor.DatabaseTests/BaseTest.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

TransactionProcessor.DatabaseTests/BaseTest.cs#L78

Make this field 'private' and encapsulate it in a 'public' property.
public static INetworkService DatabaseServerNetwork;

Check warning on line 79 in TransactionProcessor.DatabaseTests/BaseTest.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

TransactionProcessor.DatabaseTests/BaseTest.cs#L79

Change the visibility of 'DatabaseServerNetwork' or make it 'const' or 'readonly'.

Check warning on line 79 in TransactionProcessor.DatabaseTests/BaseTest.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

TransactionProcessor.DatabaseTests/BaseTest.cs#L79

Make this field 'private' and encapsulate it in a 'public' property.
public static (String usename, String password) SqlCredentials = ("sa", "thisisalongpassword123!");

Check warning on line 80 in TransactionProcessor.DatabaseTests/BaseTest.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

TransactionProcessor.DatabaseTests/BaseTest.cs#L80

Change the visibility of 'SqlCredentials' or make it 'const' or 'readonly'.

Check warning on line 80 in TransactionProcessor.DatabaseTests/BaseTest.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

TransactionProcessor.DatabaseTests/BaseTest.cs#L80

Make this field 'private' and encapsulate it in a 'public' property.

public static String GetLocalConnectionString(String databaseName)
{
Int32 databaseHostPort = DatabaseServerContainer.ToHostExposedEndpoint("1433/tcp").Port;

return $"server=localhost,{databaseHostPort};database={databaseName};user id={SqlCredentials.usename};password={SqlCredentials.password};Encrypt=false";
}

internal async Task StartSqlContainer()
{
DockerHelper dockerHelper = new TestDockerHelper();

NlogLogger logger = new NlogLogger();
logger.Initialise(LogManager.GetLogger("Specflow"), "Specflow");
LogManager.AddHiddenAssembly(typeof(NlogLogger).Assembly);
dockerHelper.Logger = logger;
dockerHelper.SqlCredentials = SqlCredentials;
dockerHelper.SqlServerContainerName = "sharedsqlserver_repotests";
dockerHelper.RequiredDockerServices = DockerServices.SqlServer;

DatabaseServerNetwork = dockerHelper.SetupTestNetwork("sharednetwork", true);

Check warning on line 101 in TransactionProcessor.DatabaseTests/BaseTest.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

TransactionProcessor.DatabaseTests/BaseTest.cs#L101

Make the enclosing instance method 'static' or remove this set on the 'static' field.
await Retry.For(async () => {
DatabaseServerContainer = await dockerHelper.SetupSqlServerContainer(DatabaseServerNetwork);

Check warning on line 103 in TransactionProcessor.DatabaseTests/BaseTest.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

TransactionProcessor.DatabaseTests/BaseTest.cs#L103

Make the enclosing instance method 'static' or remove this set on the 'static' field.
});
}

public void Dispose()
{
EstateManagementContext context = new EstateManagementContext(BaseTest.GetLocalConnectionString($"EstateReportingReadModel{this.TestId.ToString()}"));

Console.WriteLine($"About to delete database EstateReportingReadModel{this.TestId.ToString()}");
Boolean result = context.Database.EnsureDeleted();
Console.WriteLine($"Delete result is {result}");
result.ShouldBeTrue();
}
}



public class TestDockerHelper : DockerHelper
{
public override async Task CreateSubscriptions()
{
// Nothing here
}
}

}
36 changes: 36 additions & 0 deletions TransactionProcessor.DatabaseTests/ContractEventTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using Shared.EntityFramework;
using Shouldly;
using SimpleResults;
using System.ComponentModel.Design;
using TransactionProcessor.Database.Contexts;
using TransactionProcessor.Database.Entities;
using TransactionProcessor.Repository;
using TransactionProcessor.Testing;

namespace TransactionProcessor.DatabaseTests
{
public class ContractEventTests : BaseTest
{
[Fact]
public async Task AddContract_ContractIsAdded() {
Result result = await this.Repository.AddContract(TestData.DomainEvents.ContractCreatedEvent, CancellationToken.None);
result.IsSuccess.ShouldBeTrue();
EstateManagementContext context = this.GetContext();
Contract? contract = await context.Contracts.SingleOrDefaultAsync(c => c.ContractId == TestData.DomainEvents.ContractCreatedEvent.ContractId);
contract.ShouldNotBeNull();
}

[Fact]
public async Task AddContract_ContractIsAdded_EventReplayHandled() {
Result result = await this.Repository.AddContract(TestData.DomainEvents.ContractCreatedEvent, CancellationToken.None);
result.IsSuccess.ShouldBeTrue();

result = await this.Repository.AddContract(TestData.DomainEvents.ContractCreatedEvent, CancellationToken.None);
result.IsSuccess.ShouldBeTrue();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.2" />
<PackageReference Include="Ductus.FluentDocker" Version="2.10.59" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
<PackageReference Include="Ductus.FluentDocker" Version="2.10.59" />
<PackageReference Include="Shared.IntegrationTesting" Version="2025.7.13" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\TransactionProcessor.Database\TransactionProcessor.Database.csproj" />
<ProjectReference Include="..\TransactionProcessor.Repository\TransactionProcessor.Repository.csproj" />
<ProjectReference Include="..\TransactionProcessor.Testing\TransactionProcessor.Testing.csproj" />
</ItemGroup>

<ItemGroup>
<Using Include="Xunit" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -1659,7 +1659,7 @@ public async Task<Result> AddContract(ContractDomainEvents.ContractCreatedEvent

await context.Contracts.AddAsync(contract, cancellationToken);

return await context.SaveChangesAsync(cancellationToken);
return await context.SaveChangesWithDuplicateHandling(cancellationToken);
}

public async Task<Result> AddContractProduct(ContractDomainEvents.VariableValueProductAddedToContractEvent domainEvent,
Expand Down
7 changes: 7 additions & 0 deletions TransactionProcessor.sln
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TransactionProcessor.Reposi
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TransactionProcessor.DomainEvents", "TransactionProcessor.DomainEvents\TransactionProcessor.DomainEvents.csproj", "{45712D2F-07CB-4C28-9DC3-6D3FD686D01F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TransactionProcessor.DatabaseTests", "TransactionProcessor.DatabaseTests\TransactionProcessor.DatabaseTests.csproj", "{33C3AABB-B6A9-40CD-A8E0-3EF8DD38646E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -115,6 +117,10 @@ Global
{45712D2F-07CB-4C28-9DC3-6D3FD686D01F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{45712D2F-07CB-4C28-9DC3-6D3FD686D01F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{45712D2F-07CB-4C28-9DC3-6D3FD686D01F}.Release|Any CPU.Build.0 = Release|Any CPU
{33C3AABB-B6A9-40CD-A8E0-3EF8DD38646E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{33C3AABB-B6A9-40CD-A8E0-3EF8DD38646E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{33C3AABB-B6A9-40CD-A8E0-3EF8DD38646E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{33C3AABB-B6A9-40CD-A8E0-3EF8DD38646E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -137,6 +143,7 @@ Global
{812BE19F-0494-43C3-95CE-4684ECAC3CB3} = {749ADE74-A6F0-4469-A638-8FD7E82A8667}
{0696B63D-2807-40F9-BEEA-87D0415EC929} = {749ADE74-A6F0-4469-A638-8FD7E82A8667}
{45712D2F-07CB-4C28-9DC3-6D3FD686D01F} = {749ADE74-A6F0-4469-A638-8FD7E82A8667}
{33C3AABB-B6A9-40CD-A8E0-3EF8DD38646E} = {71B30DC4-AB27-4D30-8481-B4C326D074CB}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {193D13DE-424B-4D50-B674-01F9E4CC2CA9}
Expand Down
Loading