Remove unused fat

This commit is contained in:
2024-10-20 14:10:36 -04:00
parent 28d74503df
commit 905d3747d2
48 changed files with 20 additions and 792 deletions

View File

@@ -4,7 +4,6 @@
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Ardalis.GuardClauses" Version="4.2.0" />
<PackageVersion Include="AutoMapper" Version="13.0.1" />
<PackageVersion Include="Azure.Extensions.AspNetCore.Configuration.Secrets" Version="1.3.0" />
<PackageVersion Include="Azure.Identity" Version="1.12.0" />
@@ -16,7 +15,6 @@
<PackageVersion Include="FluentValidation.DependencyInjectionExtensions" Version="11.8.1" />
<PackageVersion Include="Google.Apis.Oauth2.v2" Version="1.67.0.1869" />
<PackageVersion Include="JetBrains.Annotations" Version="2024.2.0" />
<PackageVersion Include="MediatR" Version="12.2.0" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.Facebook" Version="8.0.4" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.Google" Version="8.0.4" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.6" />

View File

@@ -3,18 +3,10 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Domain", "src\Domain\Domain.csproj", "{C7E89A3E-A631-4760-8D61-BD1EAB1C4E69}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Application", "src\Application\Application.csproj", "{34C0FACD-F3D9-400C-8945-554DD6B0819A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Infrastructure", "src\Infrastructure\Infrastructure.csproj", "{117DA02F-5274-4565-ACC6-DA9B6E568B09}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{6ED356A7-8B47-4613-AD01-C85CF28491BD}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{664D406C-2F83-48F0-BFC3-408D5CB53C65}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Domain.UnitTests", "tests\Domain.UnitTests\Domain.UnitTests.csproj", "{DC37FD87-552C-4613-9F16-1537CA522898}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E2DA20AA-28D1-455C-BF50-C49A8F831633}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
@@ -30,56 +22,27 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Web", "src\Web\Web.csproj", "{4E4EE20C-F06A-4A1B-851F-C5577796941C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Application.FunctionalTests", "tests\Application.FunctionalTests\Application.FunctionalTests.csproj", "{EA6127A5-94C9-4C31-AD11-E6811B92B520}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Infrastructure.IntegrationTests", "tests\Infrastructure.IntegrationTests\Infrastructure.IntegrationTests.csproj", "{01FA6786-921D-4CE8-8C50-4FDA66C9477D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C7E89A3E-A631-4760-8D61-BD1EAB1C4E69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C7E89A3E-A631-4760-8D61-BD1EAB1C4E69}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C7E89A3E-A631-4760-8D61-BD1EAB1C4E69}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C7E89A3E-A631-4760-8D61-BD1EAB1C4E69}.Release|Any CPU.Build.0 = Release|Any CPU
{34C0FACD-F3D9-400C-8945-554DD6B0819A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{34C0FACD-F3D9-400C-8945-554DD6B0819A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{34C0FACD-F3D9-400C-8945-554DD6B0819A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{34C0FACD-F3D9-400C-8945-554DD6B0819A}.Release|Any CPU.Build.0 = Release|Any CPU
{117DA02F-5274-4565-ACC6-DA9B6E568B09}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{117DA02F-5274-4565-ACC6-DA9B6E568B09}.Debug|Any CPU.Build.0 = Debug|Any CPU
{117DA02F-5274-4565-ACC6-DA9B6E568B09}.Release|Any CPU.ActiveCfg = Release|Any CPU
{117DA02F-5274-4565-ACC6-DA9B6E568B09}.Release|Any CPU.Build.0 = Release|Any CPU
{DC37FD87-552C-4613-9F16-1537CA522898}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DC37FD87-552C-4613-9F16-1537CA522898}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DC37FD87-552C-4613-9F16-1537CA522898}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DC37FD87-552C-4613-9F16-1537CA522898}.Release|Any CPU.Build.0 = Release|Any CPU
{4E4EE20C-F06A-4A1B-851F-C5577796941C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4E4EE20C-F06A-4A1B-851F-C5577796941C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4E4EE20C-F06A-4A1B-851F-C5577796941C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4E4EE20C-F06A-4A1B-851F-C5577796941C}.Release|Any CPU.Build.0 = Release|Any CPU
{EA6127A5-94C9-4C31-AD11-E6811B92B520}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EA6127A5-94C9-4C31-AD11-E6811B92B520}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EA6127A5-94C9-4C31-AD11-E6811B92B520}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EA6127A5-94C9-4C31-AD11-E6811B92B520}.Release|Any CPU.Build.0 = Release|Any CPU
{01FA6786-921D-4CE8-8C50-4FDA66C9477D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{01FA6786-921D-4CE8-8C50-4FDA66C9477D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{01FA6786-921D-4CE8-8C50-4FDA66C9477D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{01FA6786-921D-4CE8-8C50-4FDA66C9477D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{C7E89A3E-A631-4760-8D61-BD1EAB1C4E69} = {6ED356A7-8B47-4613-AD01-C85CF28491BD}
{34C0FACD-F3D9-400C-8945-554DD6B0819A} = {6ED356A7-8B47-4613-AD01-C85CF28491BD}
{117DA02F-5274-4565-ACC6-DA9B6E568B09} = {6ED356A7-8B47-4613-AD01-C85CF28491BD}
{DC37FD87-552C-4613-9F16-1537CA522898} = {664D406C-2F83-48F0-BFC3-408D5CB53C65}
{4E4EE20C-F06A-4A1B-851F-C5577796941C} = {6ED356A7-8B47-4613-AD01-C85CF28491BD}
{EA6127A5-94C9-4C31-AD11-E6811B92B520} = {664D406C-2F83-48F0-BFC3-408D5CB53C65}
{01FA6786-921D-4CE8-8C50-4FDA66C9477D} = {664D406C-2F83-48F0-BFC3-408D5CB53C65}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3CB609D9-5D54-4C11-A371-DAAC8B74E430}

View File

@@ -3,7 +3,6 @@
## Patterns / strategy used
- Clean Architecture ( with Infrastructure, Domain, Application and Web layers )
- Minimal API endpoints.
- Guards ( Fail fast ) : https://github.com/ardalis/GuardClauses
## Tools
- Install Docker : https://www.docker.com/get-started/

View File

@@ -1,21 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RootNamespace>Hutopy.Application</RootNamespace>
<AssemblyName>Hutopy.Application</AssemblyName>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Ardalis.GuardClauses" />
<PackageReference Include="AutoMapper" />
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" />
<PackageReference Include="Google.Apis.Oauth2.v2" />
<PackageReference Include="Microsoft.EntityFrameworkCore" />
<PackageReference Include="MinimalApis.Extensions" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Domain\Domain.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,12 +0,0 @@
namespace Hutopy.Domain.Common;
public abstract class BaseAuditableEntity : BaseEntity
{
public DateTimeOffset CreatedAt { get; set; }
public Guid? CreatedBy { get; set; }
public DateTimeOffset LastModifiedAt { get; set; }
public Guid? LastModifiedBy { get; set; }
}

View File

@@ -1,30 +0,0 @@
using System.ComponentModel.DataAnnotations.Schema;
namespace Hutopy.Domain.Common;
public abstract class BaseEntity
{
// This can easily be modified to be BaseEntity<T> and public T Id to support different key types.
// Using non-generic integer types for simplicity
public Guid Id { get; set; }
private readonly List<BaseEvent> _domainEvents = [];
[NotMapped]
public IEnumerable<BaseEvent> DomainEvents => _domainEvents.AsReadOnly();
public void AddDomainEvent(BaseEvent domainEvent)
{
_domainEvents.Add(domainEvent);
}
public void RemoveDomainEvent(BaseEvent domainEvent)
{
_domainEvents.Remove(domainEvent);
}
public void ClearDomainEvents()
{
_domainEvents.Clear();
}
}

View File

@@ -1,7 +0,0 @@
using MediatR;
namespace Hutopy.Domain.Common;
public abstract class BaseEvent : INotification
{
}

View File

@@ -1,45 +0,0 @@
namespace Hutopy.Domain.Common;
// Learn more: https://docs.microsoft.com/en-us/dotnet/standard/microservices-architecture/microservice-ddd-cqrs-patterns/implement-value-objects
public abstract class ValueObject
{
private static bool EqualOperator(ValueObject left, ValueObject right)
{
if (left is null ^ right is null)
{
return false;
}
return left?.Equals(right!) != false;
}
protected static bool NotEqualOperator(ValueObject left, ValueObject right)
{
return !(EqualOperator(left, right));
}
protected abstract IEnumerable<object> GetEqualityComponents();
public override bool Equals(object? obj)
{
if (obj == null || obj.GetType() != GetType())
{
return false;
}
var other = (ValueObject)obj;
return GetEqualityComponents().SequenceEqual(other.GetEqualityComponents());
}
public override int GetHashCode()
{
var hash = new HashCode();
foreach (var component in GetEqualityComponents())
{
hash.Add(component);
}
return hash.ToHashCode();
}
}

View File

@@ -1,7 +0,0 @@
namespace Hutopy.Domain.Constants;
public abstract class Policies
{
public const string CanPurge = nameof(CanPurge);
public const string CanDelete = nameof(CanDelete);
}

View File

@@ -1,13 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RootNamespace>Hutopy.Domain</RootNamespace>
<AssemblyName>Hutopy.Domain</AssemblyName>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Google.Apis.Oauth2.v2" />
<PackageReference Include="MediatR" />
</ItemGroup>
</Project>

View File

@@ -1,9 +0,0 @@
namespace Hutopy.Domain.Enums;
public enum PriorityLevel
{
None = 0,
Low = 1,
Medium = 2,
High = 3
}

View File

@@ -1,4 +0,0 @@
namespace Hutopy.Domain.Exceptions;
public class UnsupportedColourException(
string code) : Exception($"Colour \"{code}\" is unsupported.");

View File

@@ -1,2 +0,0 @@
global using Hutopy.Domain.Common;
global using Hutopy.Domain.Exceptions;

View File

@@ -1,69 +0,0 @@
namespace Hutopy.Domain.ValueObjects;
public class Colour(string code) : ValueObject
{
public static Colour From(string code)
{
var colour = new Colour(code);
if (!SupportedColours.Contains(colour))
{
throw new UnsupportedColourException(code);
}
return colour;
}
public static Colour White => new("#FFFFFF");
public static Colour Red => new("#FF5733");
public static Colour Orange => new("#FFC300");
public static Colour Yellow => new("#FFFF66");
public static Colour Green => new("#CCFF99");
public static Colour Blue => new("#6666FF");
public static Colour Purple => new("#9966CC");
public static Colour Grey => new("#999999");
public string Code { get; private set; } = string.IsNullOrWhiteSpace(code)?"#000000":code;
public static implicit operator string(Colour colour)
{
return colour.ToString();
}
public static explicit operator Colour(string code)
{
return From(code);
}
public override string ToString()
{
return Code;
}
private static IEnumerable<Colour> SupportedColours
{
get
{
yield return White;
yield return Red;
yield return Orange;
yield return Yellow;
yield return Green;
yield return Blue;
yield return Purple;
yield return Grey;
}
}
protected override IEnumerable<object> GetEqualityComponents()
{
yield return Code;
}
}

View File

@@ -4,7 +4,7 @@ using Azure.Storage.Blobs.Models;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace Hutopy.Infrastructure.AzureBlob;
namespace Hutopy.Infrastructure.BlobStorage;
public class AzureBlobStorage
{

View File

@@ -1,4 +1,4 @@
namespace Hutopy.Application.AzureBlobStorage.Constants;
namespace Hutopy.Infrastructure.BlobStorage;
public static class CommonFileNames
{

View File

@@ -1,4 +1,4 @@
namespace Hutopy.Application.AzureBlobStorage.Constants;
namespace Hutopy.Infrastructure.BlobStorage;
public static class ContainerNames
{

View File

@@ -1,4 +1,4 @@
namespace Hutopy.Infrastructure.AzureBlob;
namespace Hutopy.Infrastructure.BlobStorage;
public static class ContentTypes
{

View File

@@ -1,4 +1,4 @@
namespace Hutopy.Application.AzureBlobStorage.Constants;
namespace Hutopy.Infrastructure.BlobStorage;
public static class SubDirectoryNames
{

View File

@@ -1,5 +1,4 @@
using Hutopy.Domain.Constants;
using Hutopy.Infrastructure.Identity;
using Hutopy.Infrastructure.Identity;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
@@ -55,13 +54,13 @@ public class ApplicationDbContextInitializer(
private async Task TrySeedAsync()
{
var administratorRole = new ApplicationRole(Roles.Administrator);
var administratorRole = new ApplicationRole(KnownRoles.Administrator);
if (roleManager.Roles.All(r => r.Name != administratorRole.Name))
{
await roleManager.CreateAsync(administratorRole);
}
var roleCreator = new ApplicationRole(Roles.Creator);
var roleCreator = new ApplicationRole(KnownRoles.Creator);
if (roleManager.Roles.All(r => r.Name != roleCreator.Name))
{
await roleManager.CreateAsync(roleCreator);

View File

@@ -1,5 +1,4 @@
using Hutopy.Domain.Constants;
using Hutopy.Infrastructure.AzureBlob;
using Hutopy.Infrastructure.BlobStorage;
using Hutopy.Infrastructure.Data;
using Hutopy.Infrastructure.Identity;
using Microsoft.AspNetCore.Identity;
@@ -49,9 +48,6 @@ public static class DependencyInjection
// Scoped services
services.AddScoped<IdentityService>();
services.AddAuthorization(options =>
options.AddPolicy(Policies.CanPurge, policy => policy.RequireRole(Roles.Administrator)));
return services;
}
}

View File

@@ -1,6 +1,6 @@
namespace Hutopy.Domain.Constants;
namespace Hutopy.Infrastructure.Identity;
public abstract class Roles
public static class KnownRoles
{
public const string Administrator = nameof(Administrator);
public const string Creator = nameof(Creator);

View File

@@ -18,9 +18,6 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools"/>
<PackageReference Include="Stripe.net"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Application\Application.csproj"/>
</ItemGroup>
<ItemGroup>
<Folder Include="Migrations\"/>
</ItemGroup>

View File

@@ -1,5 +1,4 @@
using Hutopy.Application.AzureBlobStorage.Constants;
using Hutopy.Infrastructure.AzureBlob;
using Hutopy.Infrastructure.BlobStorage;
using Hutopy.Web.Features.Contents.Data;
namespace Hutopy.Web.Features.Contents.Handlers;

View File

@@ -1,5 +1,4 @@
using Hutopy.Application.AzureBlobStorage.Constants;
using Hutopy.Infrastructure.AzureBlob;
using Hutopy.Infrastructure.BlobStorage;
using Hutopy.Web.Features.Contents.Data;
namespace Hutopy.Web.Features.Contents.Handlers;

View File

@@ -1,6 +1,5 @@
using System.Collections.Concurrent;
using Hutopy.Application.AzureBlobStorage.Constants;
using Hutopy.Infrastructure.AzureBlob;
using Hutopy.Infrastructure.BlobStorage;
using Hutopy.Web.Common;
using Hutopy.Web.Features.Contents.Data;
using Hutopy.Web.Features.Contents.Handlers.Models;

View File

@@ -1,5 +1,4 @@
using Hutopy.Application.AzureBlobStorage.Constants;
using Hutopy.Infrastructure.AzureBlob;
using Hutopy.Infrastructure.BlobStorage;
using Hutopy.Infrastructure.Identity;
using Hutopy.Web.Common;

View File

@@ -1,5 +1,4 @@
using Hutopy.Application.AzureBlobStorage.Constants;
using Hutopy.Infrastructure.AzureBlob;
using Hutopy.Infrastructure.BlobStorage;
using Hutopy.Infrastructure.Identity;
namespace Hutopy.Web.Features.Users.Handlers;

View File

@@ -30,8 +30,7 @@ public class GoogleUserInfo
[PublicAPI]
public record LoginWithGoogleRequest(
string Token)
: IRequest<LoginWithGoogleResponse>;
string Token);
public record LoginWithGoogleResponse(
string AccessToken,

View File

@@ -1,6 +1,4 @@
global using Ardalis.GuardClauses;
global using FastEndpoints;
global using FluentValidation;
global using JetBrains.Annotations;
global using MediatR;
global using Microsoft.EntityFrameworkCore;

View File

@@ -1,5 +1,4 @@
using Azure.Identity;
using Hutopy.Application;
using Hutopy.Infrastructure;
using Hutopy.Infrastructure.Data;
using Hutopy.Infrastructure.Identity;

View File

@@ -1,5 +1,4 @@
using Hutopy.Domain.Constants;
using Hutopy.Infrastructure.Identity;
using Hutopy.Infrastructure.Identity;
using Hutopy.Web.Common;
using Hutopy.Web.Features.Contents.Data;
using Hutopy.Web.Features.Messages.Data;
@@ -34,7 +33,7 @@ internal class TestDataSeeder(
{
if (contentContext.Contents.Any()) return;
_users.Add(await CreateUserAsync("admin", null, Roles.Administrator));
_users.Add(await CreateUserAsync("admin", null, KnownRoles.Administrator));
var userA = await CreateUserAsync("userA", null);
_users.Add(userA);
_users.Add(await CreateUserAsync("userB", null));
@@ -44,7 +43,7 @@ internal class TestDataSeeder(
var creatorUser = await CreateUserAsync(
creator.Name,
creator.Images.Logo,
Roles.Creator);
KnownRoles.Creator);
creator.Id = creatorUser.Id;
creator.CreatedBy = creator.Id;

View File

@@ -7,7 +7,6 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Application\Application.csproj" />
<ProjectReference Include="..\Infrastructure\Infrastructure.csproj" />
</ItemGroup>

View File

@@ -1,40 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RootNamespace>Hutopy.Application.FunctionalTests</RootNamespace>
<AssemblyName>Hutopy.Application.FunctionalTests</AssemblyName>
</PropertyGroup>
<ItemGroup>
<None Remove="appsettings.json" />
</ItemGroup>
<ItemGroup>
<Content Include="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="nunit" />
<PackageReference Include="NUnit.Analyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="NUnit3TestAdapter" />
<PackageReference Include="coverlet.collector" />
<PackageReference Include="FluentAssertions" />
<PackageReference Include="Moq" />
<PackageReference Include="Respawn" />
<PackageReference Include="Testcontainers.MsSql" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Web\Web.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,13 +0,0 @@
namespace Hutopy.Application.FunctionalTests;
using static Testing;
[TestFixture]
public abstract class BaseTestFixture
{
[SetUp]
public async Task TestSetUp()
{
await ResetState();
}
}

View File

@@ -1,33 +0,0 @@
using System.Data.Common;
using Hutopy.Infrastructure.Data;
using Hutopy.Web;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.AspNetCore.TestHost;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace Hutopy.Application.FunctionalTests;
using static Testing;
public class CustomWebApplicationFactory(
DbConnection connection)
: WebApplicationFactory<Program>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureTestServices(services =>
{
services
.RemoveAll<DbContextOptions<ApplicationDbContext>>()
.AddDbContext<ApplicationDbContext>((sp, options) =>
{
options.AddInterceptors(sp.GetServices<ISaveChangesInterceptor>());
options.UseSqlServer(connection);
});
});
}
}

View File

@@ -1,4 +0,0 @@
global using Ardalis.GuardClauses;
global using FluentAssertions;
global using Moq;
global using NUnit.Framework;

View File

@@ -1,14 +0,0 @@
using System.Data.Common;
namespace Hutopy.Application.FunctionalTests;
public interface ITestDatabase
{
Task InitialiseAsync();
DbConnection GetConnection();
Task ResetAsync();
Task DisposeAsync();
}

View File

@@ -1,62 +0,0 @@
using System.Data.Common;
using Hutopy.Infrastructure.Data;
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Respawn;
namespace Hutopy.Application.FunctionalTests;
public class SqlServerTestDatabase : ITestDatabase
{
private readonly string _connectionString = null!;
private SqlConnection _connection = null!;
private Respawner _respawner = null!;
public SqlServerTestDatabase()
{
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddEnvironmentVariables()
.Build();
var connectionString = configuration.GetConnectionString("MssqlConnection");
Guard.Against.Null(connectionString);
_connectionString = connectionString;
}
public async Task InitialiseAsync()
{
_connection = new SqlConnection(_connectionString);
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseSqlServer(_connectionString)
.Options;
var context = new ApplicationDbContext(options);
context.Database.Migrate();
_respawner = await Respawner.CreateAsync(_connectionString, new RespawnerOptions
{
TablesToIgnore = new Respawn.Graph.Table[] { "__EFMigrationsHistory" }
});
}
public DbConnection GetConnection()
{
return _connection;
}
public async Task ResetAsync()
{
await _respawner.ResetAsync(_connectionString);
}
public async Task DisposeAsync()
{
await _connection.DisposeAsync();
}
}

View File

@@ -1,13 +0,0 @@
namespace Hutopy.Application.FunctionalTests;
public static class TestDatabaseFactory
{
public static async Task<ITestDatabase> CreateAsync()
{
var database = new TestcontainersTestDatabase();
await database.InitialiseAsync();
return database;
}
}

View File

@@ -1,61 +0,0 @@
using System.Data.Common;
using Hutopy.Infrastructure.Data;
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;
using Respawn;
using Testcontainers.MsSql;
namespace Hutopy.Application.FunctionalTests;
public class TestcontainersTestDatabase : ITestDatabase
{
private readonly MsSqlContainer _container;
private DbConnection _connection = null!;
private string _connectionString = null!;
private Respawner _respawner = null!;
public TestcontainersTestDatabase()
{
_container = new MsSqlBuilder()
.WithAutoRemove(true)
.Build();
}
public async Task InitialiseAsync()
{
await _container.StartAsync();
_connectionString = _container.GetConnectionString();
_connection = new SqlConnection(_connectionString);
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseSqlServer(_connectionString)
.Options;
var context = new ApplicationDbContext(options);
await context.Database.MigrateAsync();
_respawner = await Respawner.CreateAsync(_connectionString, new RespawnerOptions
{
TablesToIgnore = ["__EFMigrationsHistory"]
});
}
public DbConnection GetConnection()
{
return _connection;
}
public async Task ResetAsync()
{
await _respawner.ResetAsync(_connectionString);
}
public async Task DisposeAsync()
{
await _connection.DisposeAsync();
await _container.DisposeAsync();
}
}

View File

@@ -1,155 +0,0 @@
using Hutopy.Domain.Constants;
using Hutopy.Infrastructure.Data;
using Hutopy.Infrastructure.Identity;
using MediatR;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
namespace Hutopy.Application.FunctionalTests;
[SetUpFixture]
public partial class Testing
{
private static ITestDatabase _database;
private static CustomWebApplicationFactory _factory = null!;
private static IServiceScopeFactory _scopeFactory = null!;
private static Guid _userId;
[OneTimeSetUp]
public async Task RunBeforeAnyTests()
{
_database = await TestDatabaseFactory.CreateAsync();
_factory = new CustomWebApplicationFactory(_database.GetConnection());
_scopeFactory = _factory.Services.GetRequiredService<IServiceScopeFactory>();
}
public static async Task<TResponse> SendAsync<TResponse>(IRequest<TResponse> request)
{
using var scope = _scopeFactory.CreateScope();
var mediator = scope.ServiceProvider.GetRequiredService<ISender>();
return await mediator.Send(request);
}
public static async Task SendAsync(IBaseRequest request)
{
using var scope = _scopeFactory.CreateScope();
var mediator = scope.ServiceProvider.GetRequiredService<ISender>();
await mediator.Send(request);
}
public static Guid GetUserId()
{
return _userId;
}
public static async Task<Guid> RunAsDefaultUserAsync()
{
return await RunAsUserAsync(
"test@local",
"Testing1234!",
[]);
}
public static async Task<Guid> RunAsAdministratorAsync()
{
return await RunAsUserAsync(
"administrator@local",
"Administrator1234!",
[Roles.Administrator]);
}
public static async Task<Guid> RunAsUserAsync(string userName, string password, string[] roles)
{
using var scope = _scopeFactory.CreateScope();
var userManager = scope.ServiceProvider.GetRequiredService<ApplicationUserManager>();
var user = new ApplicationUser
{
UserName = userName, Email = userName, Firstname = "FirstName", Lastname = "LastName"
};
var result = await userManager.CreateAsync(user, password);
if (roles.Any())
{
var roleManager = scope.ServiceProvider.GetRequiredService<RoleManager<IdentityRole>>();
foreach (var role in roles)
{
await roleManager.CreateAsync(new IdentityRole(role));
}
await userManager.AddToRolesAsync(user, roles);
}
if (result.Succeeded)
{
_userId = user.Id;
return _userId;
}
var errors = string.Join(Environment.NewLine, result.ToApplicationResult().Errors);
throw new Exception($"Unable to create {userName}.{Environment.NewLine}{errors}");
}
public static async Task ResetState()
{
try
{
await _database.ResetAsync();
}
catch (Exception)
{
}
_userId = Guid.Empty;
}
public static async Task<TEntity?> FindAsync<TEntity>(params object[] keyValues)
where TEntity : class
{
using var scope = _scopeFactory.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
return await context.FindAsync<TEntity>(keyValues);
}
public static async Task AddAsync<TEntity>(TEntity entity)
where TEntity : class
{
using var scope = _scopeFactory.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
context.Add(entity);
await context.SaveChangesAsync();
}
public static async Task<int> CountAsync<TEntity>() where TEntity : class
{
using var scope = _scopeFactory.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
return await context.Set<TEntity>().CountAsync();
}
[OneTimeTearDown]
public async Task RunAfterAnyTests()
{
await _database.DisposeAsync();
await _factory.DisposeAsync();
}
}

View File

@@ -1,5 +0,0 @@
{
"ConnectionStrings": {
"MssqlConnection": "Server=(localdb)\\mssqllocaldb;Database=HutopyTestDb;Trusted_Connection=True;MultipleActiveResultSets=true"
}
}

View File

@@ -1,24 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RootNamespace>Hutopy.Domain.UnitTests</RootNamespace>
<AssemblyName>Hutopy.Domain.UnitTests</AssemblyName>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="nunit" />
<PackageReference Include="NUnit.Analyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="NUnit3TestAdapter" />
<PackageReference Include="coverlet.collector" />
<PackageReference Include="FluentAssertions" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Domain\Domain.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,50 +0,0 @@
using Hutopy.Domain.Exceptions;
using Hutopy.Domain.ValueObjects;
using FluentAssertions;
using NUnit.Framework;
namespace Hutopy.Domain.UnitTests.ValueObjects;
public class ColourTests
{
[Test]
public void ShouldReturnCorrectColourCode()
{
var code = "#FFFFFF";
var colour = Colour.From(code);
colour.Code.Should().Be(code);
}
[Test]
public void ToStringReturnsCode()
{
var colour = Colour.White;
colour.ToString().Should().Be(colour.Code);
}
[Test]
public void ShouldPerformImplicitConversionToColourCodeString()
{
string code = Colour.White;
code.Should().Be("#FFFFFF");
}
[Test]
public void ShouldPerformExplicitConversionGivenSupportedColourCode()
{
var colour = (Colour)"#FFFFFF";
colour.Should().Be(Colour.White);
}
[Test]
public void ShouldThrowUnsupportedColourExceptionGivenNotSupportedColourCode()
{
FluentActions.Invoking(() => Colour.From("##FF33CC"))
.Should().Throw<UnsupportedColourException>();
}
}

View File

@@ -1 +0,0 @@
global using NUnit.Framework;

View File

@@ -1,19 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RootNamespace>Hutopy.Infrastructure.IntegrationTests</RootNamespace>
<AssemblyName>Hutopy.Infrastructure.IntegrationTests</AssemblyName>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="NUnit" />
<PackageReference Include="NUnit3TestAdapter" />
<PackageReference Include="NUnit.Analyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" />
</ItemGroup>
</Project>