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
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace InvoiceReminder.API.Endpoints;

public class LoginEndpoint : IEndpointDefinition
public class LoginEndpoints : IEndpointDefinition
{
public void RegisterEndpoints(IEndpointRouteBuilder endpoints)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@
namespace InvoiceReminder.Application.UnitTests.AppServices;

[TestClass]
public class AppServiceBaseTests
public class BaseAppServiceTests
{
private readonly AppServiceBase<TestEntity, TestEntityViewModel> _appService;
private readonly IRepositoryBase<TestEntity> _repository;
private readonly BaseAppService<TestEntity, TestEntityViewModel> _appService;
private readonly IBaseRepository<TestEntity> _repository;
private readonly IUnitOfWork _unitOfWork;

public AppServiceBaseTests()
public BaseAppServiceTests()
{
_repository = Substitute.For<IRepositoryBase<TestEntity>>();
_repository = Substitute.For<IBaseRepository<TestEntity>>();
_unitOfWork = Substitute.For<IUnitOfWork>();
_appService = new AppServiceBase<TestEntity, TestEntityViewModel>(_repository, _unitOfWork);
_appService = new BaseAppService<TestEntity, TestEntityViewModel>(_repository, _unitOfWork);
}

[TestMethod]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@

namespace InvoiceReminder.Application.AppServices;

public class AppServiceBase<TEntity, TEntityViewModel> : IAppServiceBase<TEntity, TEntityViewModel>
public class BaseAppService<TEntity, TEntityViewModel> : IBaseAppService<TEntity, TEntityViewModel>
where TEntity : class where TEntityViewModel : class
{
private readonly IRepositoryBase<TEntity> _repository;
private readonly IBaseRepository<TEntity> _repository;
private readonly IUnitOfWork _unitOfWork;

public AppServiceBase(IRepositoryBase<TEntity> repository, IUnitOfWork unitOfWork)
public BaseAppService(IBaseRepository<TEntity> repository, IUnitOfWork unitOfWork)
{
_repository = repository;
_unitOfWork = unitOfWork;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace InvoiceReminder.Application.AppServices;

public class InvoiceAppService : AppServiceBase<Invoice, InvoiceViewModel>, IInvoiceAppService
public class InvoiceAppService : BaseAppService<Invoice, InvoiceViewModel>, IInvoiceAppService
{
private readonly IInvoiceRepository _repository;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

namespace InvoiceReminder.Application.AppServices;

public class JobScheduleAppService : AppServiceBase<JobSchedule, JobScheduleViewModel>, IJobScheduleAppService
public class JobScheduleAppService : BaseAppService<JobSchedule, JobScheduleViewModel>, IJobScheduleAppService
{
private readonly QuartzHostedService _quartz;
private readonly IJobScheduleRepository _repository;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace InvoiceReminder.Application.AppServices;

public class ScanEmailDefinitionAppService : AppServiceBase<ScanEmailDefinition, ScanEmailDefinitionViewModel>,
public class ScanEmailDefinitionAppService : BaseAppService<ScanEmailDefinition, ScanEmailDefinitionViewModel>,
IScanEmailDefinitionAppService
{
private readonly IScanEmailDefinitionRepository _repository;
Expand Down
2 changes: 1 addition & 1 deletion InvoiceReminder.Application/AppServices/UserAppService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace InvoiceReminder.Application.AppServices;

public sealed class UserAppService : AppServiceBase<User, UserViewModel>, IUserAppService
public sealed class UserAppService : BaseAppService<User, UserViewModel>, IUserAppService
{
private readonly IUserRepository _repository;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace InvoiceReminder.Application.Interfaces;

public interface IAppServiceBase<TEntity, TEntityViewModel> where TEntity : class where TEntityViewModel : class
public interface IBaseAppService<TEntity, TEntityViewModel> where TEntity : class where TEntityViewModel : class
{
Task<Result<TEntityViewModel>> AddAsync(TEntityViewModel viewModel);
Task<Result<int>> BulkInsertAsync(ICollection<TEntityViewModel> viewModels);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
using InvoiceReminder.Domain.Entities;

namespace InvoiceReminder.Application.Interfaces;
public interface IInvoiceAppService : IAppServiceBase<Invoice, InvoiceViewModel>
public interface IInvoiceAppService : IBaseAppService<Invoice, InvoiceViewModel>
{
Task<Result<InvoiceViewModel>> GetByBarcodeAsync(string value);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
using InvoiceReminder.Domain.Entities;

namespace InvoiceReminder.Application.Interfaces;
public interface IJobScheduleAppService : IAppServiceBase<JobSchedule, JobScheduleViewModel>
public interface IJobScheduleAppService : IBaseAppService<JobSchedule, JobScheduleViewModel>
{
Task<Result<JobScheduleViewModel>> AddNewJobAsync(JobScheduleViewModel viewModel);
Task<Result<IEnumerable<JobScheduleViewModel>>> GetByUserIdAsync(Guid id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
using InvoiceReminder.Domain.Entities;

namespace InvoiceReminder.Application.Interfaces;
public interface IScanEmailDefinitionAppService : IAppServiceBase<ScanEmailDefinition, ScanEmailDefinitionViewModel>
public interface IScanEmailDefinitionAppService : IBaseAppService<ScanEmailDefinition, ScanEmailDefinitionViewModel>
{
Task<Result<ScanEmailDefinitionViewModel>> GetBySenderBeneficiaryAsync(string value, Guid id);
Task<Result<ScanEmailDefinitionViewModel>> GetBySenderEmailAddressAsync(string value, Guid id);
Expand Down
2 changes: 1 addition & 1 deletion InvoiceReminder.Application/Interfaces/IUserAppService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace InvoiceReminder.Application.Interfaces;

public interface IUserAppService : IAppServiceBase<User, UserViewModel>
public interface IUserAppService : IBaseAppService<User, UserViewModel>
{
Task<Result<UserViewModel>> GetByEmailAsync(string value);
}
43 changes: 43 additions & 0 deletions InvoiceReminder.ArchitectureTests/Domain/EntitiesTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using InvoiceReminder.Domain.Entities;
using NetArchTest.Rules;
using Shouldly;

namespace InvoiceReminder.ArchitectureTests.Domain;

[TestClass]
public sealed class EntitiesTests
{
[TestMethod]
public void GivenDomainLayer_ThenShouldNotHaveAnyDependencies()
{
// Arrange && Act
var result = Types
.InAssembly(typeof(EntityDefaults).Assembly)
.ShouldNot()
.HaveDependencyOnAny("Application", "CrossCutting", "Data", "ExternalServices")
.GetResult();

// Assert
result.IsSuccessful.ShouldBeTrue();
}

[TestMethod]
public void GivenDomainLayer_WhenEntitiesAreAccessedFromOtherProjects_ThenTheyShouldBeVisible()
{
// Arrange && Act
var result = Types
.InAssembly(typeof(EntityDefaults).Assembly)
.That()
.ResideInNamespace("InvoiceReminder.Domain.Entities").And()
.DoNotHaveName("EntityDefaults")
.Should()
.BePublic().And()
.BeClasses().And()
.NotBeStatic().And()
.Inherit(typeof(EntityDefaults))
.GetResult();

// Assert
result.IsSuccessful.ShouldBeTrue();
}
}
28 changes: 28 additions & 0 deletions InvoiceReminder.ArchitectureTests/Domain/ExtensionsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using InvoiceReminder.Domain.Entities;
using NetArchTest.Rules;
using Shouldly;

namespace InvoiceReminder.ArchitectureTests.Domain;

[TestClass]
public sealed class ExtensionsTests
{
[TestMethod]
public void GivenDomainLayer_WhenExtensionsAreAccessedFromRepositoryClasses_ThenTheyShouldBeVisible()
{
// Arrange && Act
var result = Types
.InAssembly(typeof(EntityDefaults).Assembly)
.That()
.ResideInNamespace("InvoiceReminder.Domain.Extensions").And()
.ArePublic().And()
.AreStatic().And()
.AreClasses()
.Should()
.HaveNameEndingWith("Extensions")
.GetResult();

// Assert
result.IsSuccessful.ShouldBeTrue();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using InvoiceReminder.Data.Persistence;
using Microsoft.EntityFrameworkCore;
using NetArchTest.Rules;
using Shouldly;

namespace InvoiceReminder.ArchitectureTests.Infrastructure;

[TestClass]
public sealed class EntitiesConfigTest
{
[TestMethod]
public void GivenDataLayer_EntitiesConfigMustdBeImplementedAsFollow()
{
// Arrange && Act
var result = Types
.InAssembly(typeof(CoreDbContext).Assembly)
.That()
.ResideInNamespace("InvoiceReminder.Data.Persistence.EntitiesConfig").And()
.AreNotStatic().And()
.AreClasses()
.Should()
.NotBePublic().And()
.ImplementInterface(typeof(IEntityTypeConfiguration<>)).And()
.HaveNameEndingWith("Config")
.GetResult();

// Assert
result.IsSuccessful.ShouldBeTrue();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using InvoiceReminder.Data.Interfaces;
using InvoiceReminder.Data.Repository;
using Mono.Cecil;
using NetArchTest.Rules;
using Shouldly;

namespace InvoiceReminder.ArchitectureTests.Infrastructure;

[TestClass]
public sealed class RepositoriesTests
{
[TestMethod]
public void GivenDataLayer_ThenRepositoriesShouldHaveDependencieOnlyOnDomainLayer()
{
// Arrange && Act
var result = Types
.InAssembly(typeof(BaseRepository<,>).Assembly)
.That()
.Inherit(typeof(BaseRepository<,>)).And()
.DoNotImplementInterface(typeof(IBaseRepository<>)).And()
.DoNotImplementInterface(typeof(IUnitOfWork))
.Should()
.HaveDependencyOn("Domain")
.GetResult();

// Assert
result.IsSuccessful.ShouldBeTrue();
}

[TestMethod]
public void GivenDataLayer_RepositoriesMustEndWithRepository()
{
// Arrange && Act
var result = Types
.InAssembly(typeof(BaseRepository<,>).Assembly)
.That()
.ResideInNamespace("InvoiceReminder.Data.Repository").And()
.DoNotImplementInterface(typeof(IUnitOfWork)).And()
.DoNotHaveNameStartingWith("Base")
.Should()
.HaveNameEndingWith("Repository").And()
.Inherit(typeof(BaseRepository<,>)).And()
.ImplementInterface(typeof(IBaseRepository<>))
.GetResult();

// Assert
result.IsSuccessful.ShouldBeTrue();
}

[TestMethod]
public void GivenRepositoryInterfaces_ThenShouldBePublicAndBeInterfacesAndStartWithIAndEndWithRepository()
{
// Arrange
var interfaceRule = new InterfaceRepositoryRule();

// Act
var result = Types
.InAssembly(typeof(IBaseRepository<>).Assembly)
.That()
.ResideInNamespace("InvoiceReminder.Data.Interfaces").And()
.DoNotHaveNameStartingWith("IBase").And()
.DoNotHaveName("IUnitOfWork")
.Should()
.MeetCustomRule(interfaceRule)
.GetResult();

// Assert
result.IsSuccessful.ShouldBeTrue();
}
}

public class InterfaceRepositoryRule : ICustomRule
{
public bool MeetsRule(TypeDefinition type)
{
return type.IsInterface &&
type.IsPublic &&
type.Name.StartsWith('I') &&
type.Name.EndsWith("Repository");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<Project Sdk="MSTest.Sdk/3.6.4">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<LangVersion>latest</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<!--
Displays error on console in addition to the log file. Note that this feature comes with a performance impact.
For more information, visit https://learn.microsoft.com/dotnet/core/testing/unit-testing-platform-integration-dotnet-test#show-failure-per-test
-->
<TestingPlatformShowTestsFailure>true</TestingPlatformShowTestsFailure>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="NetArchTest.Rules" Version="1.3.2" />
<PackageReference Include="Shouldly" Version="4.3.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\InvoiceReminder.API\InvoiceReminder.API.csproj" />
<ProjectReference Include="..\InvoiceReminder.Application\InvoiceReminder.Application.csproj" />
<ProjectReference Include="..\InvoiceReminder.Data\InvoiceReminder.Data.csproj" />
<ProjectReference Include="..\InvoiceReminder.Domain\InvoiceReminder.Domain.csproj" />
</ItemGroup>

</Project>
1 change: 1 addition & 0 deletions InvoiceReminder.ArchitectureTests/MSTestSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)]
43 changes: 43 additions & 0 deletions InvoiceReminder.ArchitectureTests/Presentation/EndpointsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using InvoiceReminder.API.Endpoints;
using NetArchTest.Rules;
using Shouldly;

namespace InvoiceReminder.ArchitectureTests.Presentation;

[TestClass]
public sealed class EndpointsTests
{
[TestMethod]
public void GivenPresentationLayer_ThenShouldNotHaveAnyDependencies()
{
// Arrange && Act
var result = Types
.InAssembly(typeof(IEndpointDefinition).Assembly)
.ShouldNot()
.HaveDependencyOnAny("Data", "Domain", "JobScheduler")
.GetResult();

// Assert
result.IsSuccessful.ShouldBeTrue();
}

[TestMethod]
public void GivenPresentationLayer_EndpointsMustdBeImplementedAsFollow()
{
// Arrange && Act
var result = Types
.InAssembly(typeof(IEndpointDefinition).Assembly)
.That()
.ResideInNamespace("InvoiceReminder.API.Endpoints").And()
.ArePublic().And()
.AreNotStatic().And()
.AreClasses()
.Should()
.HaveNameEndingWith("Endpoints").And()
.ImplementInterface(typeof(IEndpointDefinition))
.GetResult();

// Assert
result.IsSuccessful.ShouldBeTrue();
}
}
Loading