Skip to content
Closed
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
28 changes: 21 additions & 7 deletions Equinox.sln
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "6 - Tests", "6 - Tests", "{
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Equinox.Tests.Architecture", "tests\Equinox.Tests.Architecture\Equinox.Tests.Architecture.csproj", "{C4036D98-A669-440C-9970-05DB45815949}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Equinox.Tests.Unit", "tests\Equinox.Tests.Unit\Equinox.Tests.Unit.csproj", "{3DD828A1-824E-4301-9EC4-3EADBDA78CA9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Equinox.Tests.Integration", "tests\Equinox.Tests.Integration\Equinox.Tests.Integration.csproj", "{ABDB7393-5855-432D-9382-274BEC12D90F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -82,10 +86,18 @@ Global
{B3000AD2-5EAA-49A2-8FC4-10DF329B15B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B3000AD2-5EAA-49A2-8FC4-10DF329B15B0}.Release|Any CPU.Build.0 = Release|Any CPU
{C4036D98-A669-440C-9970-05DB45815949}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C4036D98-A669-440C-9970-05DB45815949}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C4036D98-A669-440C-9970-05DB45815949}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C4036D98-A669-440C-9970-05DB45815949}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
{C4036D98-A669-440C-9970-05DB45815949}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C4036D98-A669-440C-9970-05DB45815949}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C4036D98-A669-440C-9970-05DB45815949}.Release|Any CPU.Build.0 = Release|Any CPU
{3DD828A1-824E-4301-9EC4-3EADBDA78CA9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3DD828A1-824E-4301-9EC4-3EADBDA78CA9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3DD828A1-824E-4301-9EC4-3EADBDA78CA9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3DD828A1-824E-4301-9EC4-3EADBDA78CA9}.Release|Any CPU.Build.0 = Release|Any CPU
{ABDB7393-5855-432D-9382-274BEC12D90F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ABDB7393-5855-432D-9382-274BEC12D90F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ABDB7393-5855-432D-9382-274BEC12D90F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ABDB7393-5855-432D-9382-274BEC12D90F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
Expand All @@ -100,9 +112,11 @@ Global
{F0DDF87D-98A4-4237-91C9-FD865ED78ABB} = {0CA11832-7E0F-4038-9DB6-FF1E6D14D0DF}
{91F0D76D-2BEA-43D2-B123-DC29F2E87792} = {DF6C4BDE-F3C5-4E53-A5D5-9D27B2D3E38F}
{788030D0-561B-4136-96B4-D5ABFB5CFD07} = {DF6C4BDE-F3C5-4E53-A5D5-9D27B2D3E38F}
{B3000AD2-5EAA-49A2-8FC4-10DF329B15B0} = {DF6C4BDE-F3C5-4E53-A5D5-9D27B2D3E38F}
{C4036D98-A669-440C-9970-05DB45815949} = {7426F6BA-3DAD-411E-9956-3980D42DC36F}
EndGlobalSection
{B3000AD2-5EAA-49A2-8FC4-10DF329B15B0} = {DF6C4BDE-F3C5-4E53-A5D5-9D27B2D3E38F}
{C4036D98-A669-440C-9970-05DB45815949} = {7426F6BA-3DAD-411E-9956-3980D42DC36F}
{3DD828A1-824E-4301-9EC4-3EADBDA78CA9} = {7426F6BA-3DAD-411E-9956-3980D42DC36F}
{ABDB7393-5855-432D-9382-274BEC12D90F} = {7426F6BA-3DAD-411E-9956-3980D42DC36F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4050E145-6791-440A-A2E5-75B05ACD4AFE}
EndGlobalSection
Expand Down
4 changes: 3 additions & 1 deletion src/Equinox.Services.Api/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,6 @@
app.MapIdentityApi<IdentityUser>();

app.UseSwaggerSetup();
app.Run();
app.Run();

public partial class Program { }
55 changes: 55 additions & 0 deletions tests/Equinox.Tests.Integration/CustomerApiTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using Bogus;
using Equinox.Application.ViewModels;
using Equinox.Tests.Integration.Support;
using System.Net.Http.Json;
using Xunit;

namespace Equinox.Tests.Integration;

public class CustomerApiTests : IClassFixture<CustomWebApplicationFactory>
{
private readonly HttpClient _client;
private readonly Faker _faker = new();

public CustomerApiTests(CustomWebApplicationFactory factory)
{
_client = factory.CreateClient();
}

[Fact(DisplayName = "Posting a new customer should succeed")]
public async Task PostCustomer_ShouldReturnSuccess()
{
// Arrange
var model = new CustomerViewModel
{
Name = _faker.Person.FullName,
Email = _faker.Internet.Email(),
BirthDate = _faker.Person.DateOfBirth.AddYears(-20)
};

// Act
var response = await _client.PostAsJsonAsync("customer", model);

// Assert
Assert.True(response.IsSuccessStatusCode);
}

[Fact(DisplayName = "New customer should appear in get list")]
public async Task CreatedCustomer_ShouldAppear_OnGetAll()
{
// Arrange
var model = new CustomerViewModel
{
Name = _faker.Person.FullName,
Email = _faker.Internet.Email(),
BirthDate = _faker.Person.DateOfBirth.AddYears(-25)
};
await _client.PostAsJsonAsync("customer", model);

// Act
var customers = await _client.GetFromJsonAsync<List<CustomerViewModel>>("customer");

// Assert
Assert.Contains(customers!, c => c.Email == model.Email);
}
}
28 changes: 28 additions & 0 deletions tests/Equinox.Tests.Integration/Equinox.Tests.Integration.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.5" />
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="Bogus" Version="35.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Equinox.Services.Api\Equinox.Services.Api.csproj" />
<ProjectReference Include="..\..\src\Equinox.Application\Equinox.Application.csproj" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using Equinox.Infra.CrossCutting.Identity.Data;
using Equinox.Infra.Data.Context;
using Equinox.Services.Api;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;

namespace Equinox.Tests.Integration.Support;

public class CustomWebApplicationFactory : WebApplicationFactory<Program>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.UseEnvironment("Testing");

builder.ConfigureServices(services =>
{
// Remove existing DbContexts
var dbContextDescriptor = services.SingleOrDefault(d =>
d.ServiceType == typeof(DbContextOptions<EquinoxContext>));
if (dbContextDescriptor != null) services.Remove(dbContextDescriptor);

var eventStoreDescriptor = services.SingleOrDefault(d =>
d.ServiceType == typeof(DbContextOptions<EventStoreSqlContext>));
if (eventStoreDescriptor != null) services.Remove(eventStoreDescriptor);

var identityDescriptor = services.SingleOrDefault(d =>
d.ServiceType == typeof(DbContextOptions<EquinoxIdentityContext>));
if (identityDescriptor != null) services.Remove(identityDescriptor);

// Add in-memory databases for testing
services.AddDbContext<EquinoxContext>(options =>
options.UseInMemoryDatabase("EquinoxTest"));

Check failure on line 35 in tests/Equinox.Tests.Integration/Support/CustomWebApplicationFactory.cs

View workflow job for this annotation

GitHub Actions / build

'DbContextOptionsBuilder' does not contain a definition for 'UseInMemoryDatabase' and no accessible extension method 'UseInMemoryDatabase' accepting a first argument of type 'DbContextOptionsBuilder' could be found (are you missing a using directive or an assembly reference?)

Check failure on line 35 in tests/Equinox.Tests.Integration/Support/CustomWebApplicationFactory.cs

View workflow job for this annotation

GitHub Actions / build

'DbContextOptionsBuilder' does not contain a definition for 'UseInMemoryDatabase' and no accessible extension method 'UseInMemoryDatabase' accepting a first argument of type 'DbContextOptionsBuilder' could be found (are you missing a using directive or an assembly reference?)
services.AddDbContext<EventStoreSqlContext>(options =>
options.UseInMemoryDatabase("EquinoxTestStore"));

Check failure on line 37 in tests/Equinox.Tests.Integration/Support/CustomWebApplicationFactory.cs

View workflow job for this annotation

GitHub Actions / build

'DbContextOptionsBuilder' does not contain a definition for 'UseInMemoryDatabase' and no accessible extension method 'UseInMemoryDatabase' accepting a first argument of type 'DbContextOptionsBuilder' could be found (are you missing a using directive or an assembly reference?)

Check failure on line 37 in tests/Equinox.Tests.Integration/Support/CustomWebApplicationFactory.cs

View workflow job for this annotation

GitHub Actions / build

'DbContextOptionsBuilder' does not contain a definition for 'UseInMemoryDatabase' and no accessible extension method 'UseInMemoryDatabase' accepting a first argument of type 'DbContextOptionsBuilder' could be found (are you missing a using directive or an assembly reference?)
services.AddDbContext<EquinoxIdentityContext>(options =>
options.UseInMemoryDatabase("EquinoxTestIdentity"));

Check failure on line 39 in tests/Equinox.Tests.Integration/Support/CustomWebApplicationFactory.cs

View workflow job for this annotation

GitHub Actions / build

'DbContextOptionsBuilder' does not contain a definition for 'UseInMemoryDatabase' and no accessible extension method 'UseInMemoryDatabase' accepting a first argument of type 'DbContextOptionsBuilder' could be found (are you missing a using directive or an assembly reference?)

Check failure on line 39 in tests/Equinox.Tests.Integration/Support/CustomWebApplicationFactory.cs

View workflow job for this annotation

GitHub Actions / build

'DbContextOptionsBuilder' does not contain a definition for 'UseInMemoryDatabase' and no accessible extension method 'UseInMemoryDatabase' accepting a first argument of type 'DbContextOptionsBuilder' could be found (are you missing a using directive or an assembly reference?)

// Add test authentication
services.AddAuthentication("Test")
.AddScheme<AuthenticationSchemeOptions, TestAuthHandler>("Test", options => { });

var sp = services.BuildServiceProvider();
using var scope = sp.CreateScope();
scope.ServiceProvider.GetRequiredService<EquinoxContext>().Database.EnsureCreated();
scope.ServiceProvider.GetRequiredService<EventStoreSqlContext>().Database.EnsureCreated();
scope.ServiceProvider.GetRequiredService<EquinoxIdentityContext>().Database.EnsureCreated();
});
}
}
32 changes: 32 additions & 0 deletions tests/Equinox.Tests.Integration/Support/TestAuthHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System.Security.Claims;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace Equinox.Tests.Integration.Support;

public class TestAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
public TestAuthHandler(
IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
TimeProvider timeProvider)
: base(options, logger, encoder, timeProvider)

Check failure on line 16 in tests/Equinox.Tests.Integration/Support/TestAuthHandler.cs

View workflow job for this annotation

GitHub Actions / build

Argument 4: cannot convert from 'System.TimeProvider' to 'Microsoft.AspNetCore.Authentication.ISystemClock'

Check failure on line 16 in tests/Equinox.Tests.Integration/Support/TestAuthHandler.cs

View workflow job for this annotation

GitHub Actions / build

Argument 4: cannot convert from 'System.TimeProvider' to 'Microsoft.AspNetCore.Authentication.ISystemClock'
{
}

protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
var claims = new[]
{
new Claim(ClaimTypes.Name, "integration"),
new Claim("Customers", "Write,Remove")
};
var identity = new ClaimsIdentity(claims, Scheme.Name);
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, Scheme.Name);
return Task.FromResult(AuthenticateResult.Success(ticket));
}
}
72 changes: 72 additions & 0 deletions tests/Equinox.Tests.Unit/CustomerCommandValidationTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using Bogus;
using Equinox.Domain.Commands;
using Xunit;

namespace Equinox.Tests.Unit;

public class CustomerCommandValidationTests
{
private readonly Faker _faker = new();

[Fact(DisplayName = "Register command with valid data should be valid")]
public void RegisterNewCustomerCommand_WithValidData_ShouldBeValid()
{
// Arrange
var command = new RegisterNewCustomerCommand(
_faker.Person.FullName,
_faker.Internet.Email(),
_faker.Person.DateOfBirth.AddYears(-18));

// Act
var result = command.IsValid();

// Assert
Assert.True(result);
}

[Fact(DisplayName = "Register command with under age customer should be invalid")]
public void RegisterNewCustomerCommand_UnderAge_ShouldBeInvalid()
{
// Arrange
var command = new RegisterNewCustomerCommand(
_faker.Person.FullName,
_faker.Internet.Email(),
DateTime.Now.AddYears(-17));

// Act
var result = command.IsValid();

// Assert
Assert.False(result);
}

[Fact(DisplayName = "Update command with empty name should be invalid")]
public void UpdateCustomerCommand_EmptyName_ShouldBeInvalid()
{
// Arrange
var command = new UpdateCustomerCommand(
Guid.NewGuid(),
string.Empty,
_faker.Internet.Email(),
_faker.Person.DateOfBirth.AddYears(-20));

// Act
var result = command.IsValid();

// Assert
Assert.False(result);
}

[Fact(DisplayName = "Remove command with empty id should be invalid")]
public void RemoveCustomerCommand_EmptyId_ShouldBeInvalid()
{
// Arrange
var command = new RemoveCustomerCommand(Guid.Empty);

// Act
var result = command.IsValid();

// Assert
Assert.False(result);
}
}
26 changes: 26 additions & 0 deletions tests/Equinox.Tests.Unit/Equinox.Tests.Unit.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="Bogus" Version="35.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Equinox.Domain\Equinox.Domain.csproj" />
</ItemGroup>
</Project>
Loading