diff --git a/InvoiceReminder.API/Endpoints/LoginEndpoint.cs b/InvoiceReminder.API/Endpoints/LoginEndpoints.cs similarity index 96% rename from InvoiceReminder.API/Endpoints/LoginEndpoint.cs rename to InvoiceReminder.API/Endpoints/LoginEndpoints.cs index 522543e..0359e95 100644 --- a/InvoiceReminder.API/Endpoints/LoginEndpoint.cs +++ b/InvoiceReminder.API/Endpoints/LoginEndpoints.cs @@ -6,7 +6,7 @@ namespace InvoiceReminder.API.Endpoints; -public class LoginEndpoint : IEndpointDefinition +public class LoginEndpoints : IEndpointDefinition { public void RegisterEndpoints(IEndpointRouteBuilder endpoints) { diff --git a/InvoiceReminder.Application.UnitTests/AppServices/AppServiceBaseTests.cs b/InvoiceReminder.Application.UnitTests/AppServices/BaseAppServiceTests.cs similarity index 95% rename from InvoiceReminder.Application.UnitTests/AppServices/AppServiceBaseTests.cs rename to InvoiceReminder.Application.UnitTests/AppServices/BaseAppServiceTests.cs index 4cd9277..66a7b63 100644 --- a/InvoiceReminder.Application.UnitTests/AppServices/AppServiceBaseTests.cs +++ b/InvoiceReminder.Application.UnitTests/AppServices/BaseAppServiceTests.cs @@ -7,17 +7,17 @@ namespace InvoiceReminder.Application.UnitTests.AppServices; [TestClass] -public class AppServiceBaseTests +public class BaseAppServiceTests { - private readonly AppServiceBase _appService; - private readonly IRepositoryBase _repository; + private readonly BaseAppService _appService; + private readonly IBaseRepository _repository; private readonly IUnitOfWork _unitOfWork; - public AppServiceBaseTests() + public BaseAppServiceTests() { - _repository = Substitute.For>(); + _repository = Substitute.For>(); _unitOfWork = Substitute.For(); - _appService = new AppServiceBase(_repository, _unitOfWork); + _appService = new BaseAppService(_repository, _unitOfWork); } [TestMethod] diff --git a/InvoiceReminder.Application/AppServices/AppServiceBase.cs b/InvoiceReminder.Application/AppServices/BaseAppService.cs similarity index 92% rename from InvoiceReminder.Application/AppServices/AppServiceBase.cs rename to InvoiceReminder.Application/AppServices/BaseAppService.cs index acf77e1..9e0add7 100644 --- a/InvoiceReminder.Application/AppServices/AppServiceBase.cs +++ b/InvoiceReminder.Application/AppServices/BaseAppService.cs @@ -5,13 +5,13 @@ namespace InvoiceReminder.Application.AppServices; -public class AppServiceBase : IAppServiceBase +public class BaseAppService : IBaseAppService where TEntity : class where TEntityViewModel : class { - private readonly IRepositoryBase _repository; + private readonly IBaseRepository _repository; private readonly IUnitOfWork _unitOfWork; - public AppServiceBase(IRepositoryBase repository, IUnitOfWork unitOfWork) + public BaseAppService(IBaseRepository repository, IUnitOfWork unitOfWork) { _repository = repository; _unitOfWork = unitOfWork; diff --git a/InvoiceReminder.Application/AppServices/InvoiceAppService.cs b/InvoiceReminder.Application/AppServices/InvoiceAppService.cs index b48c034..d6e8fde 100644 --- a/InvoiceReminder.Application/AppServices/InvoiceAppService.cs +++ b/InvoiceReminder.Application/AppServices/InvoiceAppService.cs @@ -7,7 +7,7 @@ namespace InvoiceReminder.Application.AppServices; -public class InvoiceAppService : AppServiceBase, IInvoiceAppService +public class InvoiceAppService : BaseAppService, IInvoiceAppService { private readonly IInvoiceRepository _repository; diff --git a/InvoiceReminder.Application/AppServices/JobScheduleAppService.cs b/InvoiceReminder.Application/AppServices/JobScheduleAppService.cs index f1355fe..7a28191 100644 --- a/InvoiceReminder.Application/AppServices/JobScheduleAppService.cs +++ b/InvoiceReminder.Application/AppServices/JobScheduleAppService.cs @@ -10,7 +10,7 @@ namespace InvoiceReminder.Application.AppServices; -public class JobScheduleAppService : AppServiceBase, IJobScheduleAppService +public class JobScheduleAppService : BaseAppService, IJobScheduleAppService { private readonly QuartzHostedService _quartz; private readonly IJobScheduleRepository _repository; diff --git a/InvoiceReminder.Application/AppServices/ScanEmailDefinitionAppService.cs b/InvoiceReminder.Application/AppServices/ScanEmailDefinitionAppService.cs index 466da07..4359b20 100644 --- a/InvoiceReminder.Application/AppServices/ScanEmailDefinitionAppService.cs +++ b/InvoiceReminder.Application/AppServices/ScanEmailDefinitionAppService.cs @@ -7,7 +7,7 @@ namespace InvoiceReminder.Application.AppServices; -public class ScanEmailDefinitionAppService : AppServiceBase, +public class ScanEmailDefinitionAppService : BaseAppService, IScanEmailDefinitionAppService { private readonly IScanEmailDefinitionRepository _repository; diff --git a/InvoiceReminder.Application/AppServices/UserAppService.cs b/InvoiceReminder.Application/AppServices/UserAppService.cs index ff7d813..de864f3 100644 --- a/InvoiceReminder.Application/AppServices/UserAppService.cs +++ b/InvoiceReminder.Application/AppServices/UserAppService.cs @@ -7,7 +7,7 @@ namespace InvoiceReminder.Application.AppServices; -public sealed class UserAppService : AppServiceBase, IUserAppService +public sealed class UserAppService : BaseAppService, IUserAppService { private readonly IUserRepository _repository; diff --git a/InvoiceReminder.Application/Interfaces/IAppServiceBase.cs b/InvoiceReminder.Application/Interfaces/IBaseAppService.cs similarity index 89% rename from InvoiceReminder.Application/Interfaces/IAppServiceBase.cs rename to InvoiceReminder.Application/Interfaces/IBaseAppService.cs index 669df0c..1ed0391 100644 --- a/InvoiceReminder.Application/Interfaces/IAppServiceBase.cs +++ b/InvoiceReminder.Application/Interfaces/IBaseAppService.cs @@ -2,7 +2,7 @@ namespace InvoiceReminder.Application.Interfaces; -public interface IAppServiceBase where TEntity : class where TEntityViewModel : class +public interface IBaseAppService where TEntity : class where TEntityViewModel : class { Task> AddAsync(TEntityViewModel viewModel); Task> BulkInsertAsync(ICollection viewModels); diff --git a/InvoiceReminder.Application/Interfaces/IInvoiceAppService.cs b/InvoiceReminder.Application/Interfaces/IInvoiceAppService.cs index 0e7388f..e178d5d 100644 --- a/InvoiceReminder.Application/Interfaces/IInvoiceAppService.cs +++ b/InvoiceReminder.Application/Interfaces/IInvoiceAppService.cs @@ -3,7 +3,7 @@ using InvoiceReminder.Domain.Entities; namespace InvoiceReminder.Application.Interfaces; -public interface IInvoiceAppService : IAppServiceBase +public interface IInvoiceAppService : IBaseAppService { Task> GetByBarcodeAsync(string value); } diff --git a/InvoiceReminder.Application/Interfaces/IJobScheduleAppService.cs b/InvoiceReminder.Application/Interfaces/IJobScheduleAppService.cs index bd5b8af..e798e29 100644 --- a/InvoiceReminder.Application/Interfaces/IJobScheduleAppService.cs +++ b/InvoiceReminder.Application/Interfaces/IJobScheduleAppService.cs @@ -3,7 +3,7 @@ using InvoiceReminder.Domain.Entities; namespace InvoiceReminder.Application.Interfaces; -public interface IJobScheduleAppService : IAppServiceBase +public interface IJobScheduleAppService : IBaseAppService { Task> AddNewJobAsync(JobScheduleViewModel viewModel); Task>> GetByUserIdAsync(Guid id); diff --git a/InvoiceReminder.Application/Interfaces/IScanEmailDefinitionAppService.cs b/InvoiceReminder.Application/Interfaces/IScanEmailDefinitionAppService.cs index 9692135..ec93035 100644 --- a/InvoiceReminder.Application/Interfaces/IScanEmailDefinitionAppService.cs +++ b/InvoiceReminder.Application/Interfaces/IScanEmailDefinitionAppService.cs @@ -3,7 +3,7 @@ using InvoiceReminder.Domain.Entities; namespace InvoiceReminder.Application.Interfaces; -public interface IScanEmailDefinitionAppService : IAppServiceBase +public interface IScanEmailDefinitionAppService : IBaseAppService { Task> GetBySenderBeneficiaryAsync(string value, Guid id); Task> GetBySenderEmailAddressAsync(string value, Guid id); diff --git a/InvoiceReminder.Application/Interfaces/IUserAppService.cs b/InvoiceReminder.Application/Interfaces/IUserAppService.cs index e98006d..6efcd25 100644 --- a/InvoiceReminder.Application/Interfaces/IUserAppService.cs +++ b/InvoiceReminder.Application/Interfaces/IUserAppService.cs @@ -4,7 +4,7 @@ namespace InvoiceReminder.Application.Interfaces; -public interface IUserAppService : IAppServiceBase +public interface IUserAppService : IBaseAppService { Task> GetByEmailAsync(string value); } diff --git a/InvoiceReminder.ArchitectureTests/Domain/EntitiesTests.cs b/InvoiceReminder.ArchitectureTests/Domain/EntitiesTests.cs new file mode 100644 index 0000000..85b4df6 --- /dev/null +++ b/InvoiceReminder.ArchitectureTests/Domain/EntitiesTests.cs @@ -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(); + } +} diff --git a/InvoiceReminder.ArchitectureTests/Domain/ExtensionsTests.cs b/InvoiceReminder.ArchitectureTests/Domain/ExtensionsTests.cs new file mode 100644 index 0000000..d9a33ca --- /dev/null +++ b/InvoiceReminder.ArchitectureTests/Domain/ExtensionsTests.cs @@ -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(); + } +} diff --git a/InvoiceReminder.ArchitectureTests/Infrastructure/EntitiesConfigTest.cs b/InvoiceReminder.ArchitectureTests/Infrastructure/EntitiesConfigTest.cs new file mode 100644 index 0000000..0d0b1b0 --- /dev/null +++ b/InvoiceReminder.ArchitectureTests/Infrastructure/EntitiesConfigTest.cs @@ -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(); + } +} diff --git a/InvoiceReminder.ArchitectureTests/Infrastructure/RepositoriesTests.cs b/InvoiceReminder.ArchitectureTests/Infrastructure/RepositoriesTests.cs new file mode 100644 index 0000000..78c0758 --- /dev/null +++ b/InvoiceReminder.ArchitectureTests/Infrastructure/RepositoriesTests.cs @@ -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"); + } +} diff --git a/InvoiceReminder.ArchitectureTests/InvoiceReminder.ArchitectureTests.csproj b/InvoiceReminder.ArchitectureTests/InvoiceReminder.ArchitectureTests.csproj new file mode 100644 index 0000000..59a8f7c --- /dev/null +++ b/InvoiceReminder.ArchitectureTests/InvoiceReminder.ArchitectureTests.csproj @@ -0,0 +1,27 @@ + + + + net9.0 + latest + enable + enable + + true + + + + + + + + + + + + + + + diff --git a/InvoiceReminder.ArchitectureTests/MSTestSettings.cs b/InvoiceReminder.ArchitectureTests/MSTestSettings.cs new file mode 100644 index 0000000..aaf278c --- /dev/null +++ b/InvoiceReminder.ArchitectureTests/MSTestSettings.cs @@ -0,0 +1 @@ +[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)] diff --git a/InvoiceReminder.ArchitectureTests/Presentation/EndpointsTests.cs b/InvoiceReminder.ArchitectureTests/Presentation/EndpointsTests.cs new file mode 100644 index 0000000..176f9c4 --- /dev/null +++ b/InvoiceReminder.ArchitectureTests/Presentation/EndpointsTests.cs @@ -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(); + } +} diff --git a/InvoiceReminder.ArchitectureTests/Services/AppServicesTests.cs b/InvoiceReminder.ArchitectureTests/Services/AppServicesTests.cs new file mode 100644 index 0000000..78236c5 --- /dev/null +++ b/InvoiceReminder.ArchitectureTests/Services/AppServicesTests.cs @@ -0,0 +1,78 @@ +using InvoiceReminder.Application.AppServices; +using InvoiceReminder.Application.Interfaces; +using Mono.Cecil; +using NetArchTest.Rules; +using Shouldly; + +namespace InvoiceReminder.ArchitectureTests.Services; + +[TestClass] +public sealed class AppServicesTests +{ + [TestMethod] + public void GivenApplicationLayer_ThenAppServicesShouldHaveDependencieOnlyOnDataAndDomainLayerAndJobScheduler() + { + // Arrange && Act + var result = Types + .InAssembly(typeof(BaseAppService<,>).Assembly) + .That() + .Inherit(typeof(BaseAppService<,>)).And() + .DoNotImplementInterface(typeof(IBaseAppService<,>)) + .Should() + .HaveDependencyOnAll("Data", "Domain", "JobScheduler") + .GetResult(); + + // Assert + result.IsSuccessful.ShouldBeTrue(); + } + + [TestMethod] + public void GivenApplicationLayer_AppServicesMustEndWithAppService() + { + // Arrange && Act + var result = Types + .InAssembly(typeof(BaseAppService<,>).Assembly) + .That() + .ResideInNamespace("InvoiceReminder.Application.AppServices").And() + .DoNotHaveNameStartingWith("Base") + .Should() + .HaveNameEndingWith("AppService").And() + .Inherit(typeof(BaseAppService<,>)).And() + .ImplementInterface(typeof(IBaseAppService<,>)) + .GetResult(); + + // Assert + result.IsSuccessful.ShouldBeTrue(); + } + + [TestMethod] + public void GivenAppServiceInterfaces_ThenShouldBePublicAndBeInterfacesAndStartWithIAndEndWithAppService() + { + // Arrange + var interfaceRule = new InterfaceAppServiceRule(); + + // Act + var result = Types + .InAssembly(typeof(IBaseAppService<,>).Assembly) + .That() + .ResideInNamespace("InvoiceReminder.Application.Interfaces").And() + .DoNotHaveNameStartingWith("IBase") + .Should() + .MeetCustomRule(interfaceRule) + .GetResult(); + + // Assert + result.IsSuccessful.ShouldBeTrue(); + } +} + +public class InterfaceAppServiceRule : ICustomRule +{ + public bool MeetsRule(TypeDefinition type) + { + return type.IsInterface && + type.IsPublic && + type.Name.StartsWith('I') && + type.Name.EndsWith("AppService"); + } +} diff --git a/InvoiceReminder.ArchitectureTests/Services/ViewModelsTests.cs b/InvoiceReminder.ArchitectureTests/Services/ViewModelsTests.cs new file mode 100644 index 0000000..0443b72 --- /dev/null +++ b/InvoiceReminder.ArchitectureTests/Services/ViewModelsTests.cs @@ -0,0 +1,29 @@ +using InvoiceReminder.Application.ViewModels; +using NetArchTest.Rules; +using Shouldly; + +namespace InvoiceReminder.ArchitectureTests.Services; + +[TestClass] +public sealed class ViewModelsTests +{ + [TestMethod] + public void GivenApplicationLayer_WhenViewModelsAreAccessedFromPresentation_ThenTheyShouldBeVisible() + { + // Arrange && Act + var result = Types + .InAssembly(typeof(ViewModelDefaults).Assembly) + .That() + .ResideInNamespace("InvoiceReminder.Application.ViewModels").And() + .DoNotHaveName("ViewModelDefaults") + .Should() + .BePublic().And() + .BeClasses().And() + .NotBeStatic().And() + .Inherit(typeof(ViewModelDefaults)) + .GetResult(); + + // Assert + result.IsSuccessful.ShouldBeTrue(); + } +} diff --git a/InvoiceReminder.CrossCutting.IoC/DependencyInjectionConfig.cs b/InvoiceReminder.CrossCutting.IoC/DependencyInjectionConfig.cs index 2ae7f4e..44837ff 100644 --- a/InvoiceReminder.CrossCutting.IoC/DependencyInjectionConfig.cs +++ b/InvoiceReminder.CrossCutting.IoC/DependencyInjectionConfig.cs @@ -48,7 +48,7 @@ public static IServiceCollection AddInfrastructure(this IServiceCollection servi private static IServiceCollection AddAppServices(this IServiceCollection services) { _ = services.Scan(scan => - scan.FromAssembliesOf(typeof(IAppServiceBase<,>)) + scan.FromAssembliesOf(typeof(IBaseAppService<,>)) .AddClasses(classes => classes.InNamespaces("InvoiceReminder.Application.AppServices")) .UsingRegistrationStrategy(RegistrationStrategy.Skip) .AsImplementedInterfaces() @@ -99,7 +99,7 @@ private static IServiceCollection AddQuartzJobService(this IServiceCollection se private static IServiceCollection AddRepositories(this IServiceCollection services) { _ = services.Scan(scan => - scan.FromAssembliesOf(typeof(IRepositoryBase<>)) + scan.FromAssembliesOf(typeof(IBaseRepository<>)) .AddClasses(classes => classes.InNamespaces("InvoiceReminder.Data.Repository")) .UsingRegistrationStrategy(RegistrationStrategy.Skip) .AsImplementedInterfaces() diff --git a/InvoiceReminder.Data/Interfaces/IRepositoryBase.cs b/InvoiceReminder.Data/Interfaces/IBaseRepository.cs similarity index 86% rename from InvoiceReminder.Data/Interfaces/IRepositoryBase.cs rename to InvoiceReminder.Data/Interfaces/IBaseRepository.cs index 3a26e1d..297266c 100644 --- a/InvoiceReminder.Data/Interfaces/IRepositoryBase.cs +++ b/InvoiceReminder.Data/Interfaces/IBaseRepository.cs @@ -2,7 +2,7 @@ namespace InvoiceReminder.Data.Interfaces; -public interface IRepositoryBase : IDisposable where TEntity : class +public interface IBaseRepository : IDisposable where TEntity : class { Task AddAsync(TEntity entity); Task BulkInsertAsync(ICollection entities); diff --git a/InvoiceReminder.Data/Interfaces/IInvoiceRepository.cs b/InvoiceReminder.Data/Interfaces/IInvoiceRepository.cs index c3ef01b..2988f6e 100644 --- a/InvoiceReminder.Data/Interfaces/IInvoiceRepository.cs +++ b/InvoiceReminder.Data/Interfaces/IInvoiceRepository.cs @@ -2,7 +2,7 @@ namespace InvoiceReminder.Data.Interfaces; -public interface IInvoiceRepository : IRepositoryBase +public interface IInvoiceRepository : IBaseRepository { Task GetByBarCodeAsync(string value); } diff --git a/InvoiceReminder.Data/Interfaces/IJobScheduleRepository.cs b/InvoiceReminder.Data/Interfaces/IJobScheduleRepository.cs index 4377c32..04668f6 100644 --- a/InvoiceReminder.Data/Interfaces/IJobScheduleRepository.cs +++ b/InvoiceReminder.Data/Interfaces/IJobScheduleRepository.cs @@ -2,7 +2,7 @@ namespace InvoiceReminder.Data.Interfaces; -public interface IJobScheduleRepository : IRepositoryBase +public interface IJobScheduleRepository : IBaseRepository { Task> GetByUserIdAsync(Guid id); } diff --git a/InvoiceReminder.Data/Interfaces/IScanEmailDefinitionRepository.cs b/InvoiceReminder.Data/Interfaces/IScanEmailDefinitionRepository.cs index 969c35d..9e787d3 100644 --- a/InvoiceReminder.Data/Interfaces/IScanEmailDefinitionRepository.cs +++ b/InvoiceReminder.Data/Interfaces/IScanEmailDefinitionRepository.cs @@ -2,7 +2,7 @@ namespace InvoiceReminder.Data.Interfaces; -public interface IScanEmailDefinitionRepository : IRepositoryBase +public interface IScanEmailDefinitionRepository : IBaseRepository { Task GetBySenderBeneficiaryAsync(string value, Guid id); Task GetBySenderEmailAddressAsync(string value, Guid id); diff --git a/InvoiceReminder.Data/Interfaces/IUserRepository.cs b/InvoiceReminder.Data/Interfaces/IUserRepository.cs index 96501e8..3523f36 100644 --- a/InvoiceReminder.Data/Interfaces/IUserRepository.cs +++ b/InvoiceReminder.Data/Interfaces/IUserRepository.cs @@ -2,7 +2,7 @@ namespace InvoiceReminder.Data.Interfaces; -public interface IUserRepository : IRepositoryBase +public interface IUserRepository : IBaseRepository { Task GetByEmailAsync(string value); new Task GetByIdAsync(Guid id); diff --git a/InvoiceReminder.Data/Repository/RepositoryBase.cs b/InvoiceReminder.Data/Repository/BaseRepository.cs similarity index 94% rename from InvoiceReminder.Data/Repository/RepositoryBase.cs rename to InvoiceReminder.Data/Repository/BaseRepository.cs index e9e612b..6e2a774 100644 --- a/InvoiceReminder.Data/Repository/RepositoryBase.cs +++ b/InvoiceReminder.Data/Repository/BaseRepository.cs @@ -1,18 +1,18 @@ -using InvoiceReminder.Data.Interfaces; using EFCore.BulkExtensions; +using InvoiceReminder.Data.Interfaces; using Microsoft.EntityFrameworkCore; using System.Linq.Expressions; namespace InvoiceReminder.Data.Repository; -public class RepositoryBase : IRepositoryBase +public class BaseRepository : IBaseRepository where TDbContext : DbContext where TEntity : class { private readonly TDbContext _dbContext; private readonly DbSet _dbSet; private bool disposed = false; - public RepositoryBase(TDbContext dbContext) + public BaseRepository(TDbContext dbContext) { _dbContext = dbContext; _dbSet = _dbContext.Set(); diff --git a/InvoiceReminder.Data/Repository/InvoiceRepository.cs b/InvoiceReminder.Data/Repository/InvoiceRepository.cs index e443cd2..7701aeb 100644 --- a/InvoiceReminder.Data/Repository/InvoiceRepository.cs +++ b/InvoiceReminder.Data/Repository/InvoiceRepository.cs @@ -8,7 +8,7 @@ namespace InvoiceReminder.Data.Repository; -public class InvoiceRepository : RepositoryBase, IInvoiceRepository +public class InvoiceRepository : BaseRepository, IInvoiceRepository { private readonly IDbConnection _dbConnection; private readonly ILogger _logger; diff --git a/InvoiceReminder.Data/Repository/JobScheduleRepository.cs b/InvoiceReminder.Data/Repository/JobScheduleRepository.cs index 3210c47..de866a2 100644 --- a/InvoiceReminder.Data/Repository/JobScheduleRepository.cs +++ b/InvoiceReminder.Data/Repository/JobScheduleRepository.cs @@ -8,7 +8,7 @@ namespace InvoiceReminder.Data.Repository; -public class JobScheduleRepository : RepositoryBase, IJobScheduleRepository +public class JobScheduleRepository : BaseRepository, IJobScheduleRepository { private readonly IDbConnection _dbConnection; private readonly ILogger _logger; diff --git a/InvoiceReminder.Data/Repository/ScanEmailDefinitionRepository.cs b/InvoiceReminder.Data/Repository/ScanEmailDefinitionRepository.cs index 9fe94b3..6c7a0b9 100644 --- a/InvoiceReminder.Data/Repository/ScanEmailDefinitionRepository.cs +++ b/InvoiceReminder.Data/Repository/ScanEmailDefinitionRepository.cs @@ -8,7 +8,7 @@ namespace InvoiceReminder.Data.Repository; -public class ScanEmailDefinitionRepository : RepositoryBase, IScanEmailDefinitionRepository +public class ScanEmailDefinitionRepository : BaseRepository, IScanEmailDefinitionRepository { private readonly IDbConnection _dbConnection; private readonly ILogger _logger; diff --git a/InvoiceReminder.Data/Repository/UserRepository.cs b/InvoiceReminder.Data/Repository/UserRepository.cs index cd9578e..badcfd0 100644 --- a/InvoiceReminder.Data/Repository/UserRepository.cs +++ b/InvoiceReminder.Data/Repository/UserRepository.cs @@ -9,7 +9,7 @@ namespace InvoiceReminder.Data.Repository; -public class UserRepository : RepositoryBase, IUserRepository +public class UserRepository : BaseRepository, IUserRepository { private readonly IDbConnection _dbConnection; private readonly ILogger _logger; diff --git a/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/RepositoryBaseTests.cs b/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/BaseRepositoryTests.cs similarity index 92% rename from InvoiceReminder.Infrastructure.UnitTests/Data/Repository/RepositoryBaseTests.cs rename to InvoiceReminder.Infrastructure.UnitTests/Data/Repository/BaseRepositoryTests.cs index 351eb8d..a91e985 100644 --- a/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/RepositoryBaseTests.cs +++ b/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/BaseRepositoryTests.cs @@ -8,12 +8,12 @@ namespace InvoiceReminder.Infrastructure.UnitTests.Data.Repository; [TestClass] -public class RepositoryBaseTests +public class BaseRepositoryTests { private readonly SqliteConnection _connection; private readonly DbContextOptions _contextOptions; - public RepositoryBaseTests() + public BaseRepositoryTests() { _connection = new SqliteConnection("Filename=:memory:"); _connection.Open(); @@ -47,7 +47,7 @@ public async Task AddAsync_Should_AddEntityToDatabase() // Arrange var entity = new TestEntity { Id = Guid.NewGuid(), Name = "Test" }; using var context = CreateContext(); - var repository = new RepositoryBase(context); + var repository = new BaseRepository(context); // Act var addedEntity = await repository.AddAsync(entity); @@ -69,7 +69,7 @@ public async Task BulkInsertAsync_Should_AddMultipleEntitiesToDatabaseWithTimest }; using var context = CreateContext(); - var repository = new RepositoryBase(context); + var repository = new BaseRepository(context); // Act var count = await repository.BulkInsertAsync(entities); @@ -92,7 +92,7 @@ public async Task Remove_Should_RemoveExistingEntityFromDatabase() // Arrange var entity = new TestEntity { Id = Guid.NewGuid(), Name = "Test" }; using var context = CreateContext(); - var repository = new RepositoryBase(context); + var repository = new BaseRepository(context); _ = await repository.AddAsync(entity); _ = await context.SaveChangesAsync(); @@ -111,7 +111,7 @@ public async Task Remove_Should_AttachAndRemoveDetachedEntity() // Arrange var entity = new TestEntity { Id = Guid.NewGuid(), Name = "Test" }; using var context = CreateContext(); - var repository = new RepositoryBase(context); + var repository = new BaseRepository(context); _ = await repository.AddAsync(entity); _ = await context.SaveChangesAsync(); @@ -133,7 +133,7 @@ public async Task GetByIdAsync_Should_ReturnEntityById() // Arrange var entity = new TestEntity { Id = Guid.NewGuid(), Name = "Test" }; using var context = CreateContext(); - var repository = new RepositoryBase(context); + var repository = new BaseRepository(context); _ = await repository.AddAsync(entity); _ = await context.SaveChangesAsync(); @@ -156,7 +156,7 @@ public async Task GetByIdAsync_Should_ReturnNullWhenEntityNotFound() // Arrange var nonExistingId = Guid.NewGuid(); using var context = CreateContext(); - var repository = new RepositoryBase(context); + var repository = new BaseRepository(context); // Act var retrievedEntity = await repository.GetByIdAsync(nonExistingId); @@ -176,7 +176,7 @@ public void GetAll_Should_ReturnAllEntities() }; using var context = CreateContext(); - var repository = new RepositoryBase(context); + var repository = new BaseRepository(context); repository.BulkInsertAsync(entities).Wait(); _ = context.SaveChanges(); @@ -199,7 +199,7 @@ public async Task Update_Should_UpdateExistingEntity() // Arrange var entity = new TestEntity { Id = Guid.NewGuid(), Name = "Original Name" }; using var context = CreateContext(); - var repository = new RepositoryBase(context); + var repository = new BaseRepository(context); _ = await repository.AddAsync(entity); _ = await context.SaveChangesAsync(); @@ -227,7 +227,7 @@ public async Task Update_Should_AttachAndUpdateDetachedEntity() // Arrange var entity = new TestEntity { Id = Guid.NewGuid(), Name = "Original Name" }; using var context = CreateContext(); - var repository = new RepositoryBase(context); + var repository = new BaseRepository(context); _ = await repository.AddAsync(entity); _ = await context.SaveChangesAsync(); @@ -266,7 +266,7 @@ public async Task Where_Should_ReturnEntitiesMatchingPredicate() using var context = CreateContext(); context.TestEntities.AddRange(entities); _ = await context.SaveChangesAsync(); - var repository = new RepositoryBase(context); + var repository = new BaseRepository(context); // Act Expression> predicate = e => e.Name.StartsWith("Test"); @@ -291,7 +291,7 @@ public void Dispose_Should_DisposeDbContext() .UseSqlite(mockConnection) .Options; var mockDbContext = Substitute.For(options); - var repository = new RepositoryBase(mockDbContext); + var repository = new BaseRepository(mockDbContext); // Act repository.Dispose(); @@ -309,7 +309,7 @@ public void Dispose_CalledMultipleTimes_Should_DisposeDbContextOnlyOnce() .UseSqlite(mockConnection) .Options; var mockDbContext = Substitute.For(options); - var repository = new RepositoryBase(mockDbContext); + var repository = new BaseRepository(mockDbContext); // Act repository.Dispose(); diff --git a/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/InvoiceRepositoryTests.cs b/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/InvoiceRepositoryTests.cs index 5935f94..a80d517 100644 --- a/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/InvoiceRepositoryTests.cs +++ b/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/InvoiceRepositoryTests.cs @@ -37,8 +37,8 @@ public void InvoiceRepository_ShouldBeAssignableToItsInterface_And_GenericInterf repository.ShouldSatisfyAllConditions(() => { _ = repository.ShouldBeAssignableTo(); - _ = repository.ShouldBeAssignableTo>(); - _ = repository.ShouldBeAssignableTo>(); + _ = repository.ShouldBeAssignableTo>(); + _ = repository.ShouldBeAssignableTo>(); _ = repository.ShouldNotBeNull(); _ = repository.ShouldBeOfType(); diff --git a/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/JobScheduleRepositoryTests.cs b/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/JobScheduleRepositoryTests.cs index fa7c274..d65ad22 100644 --- a/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/JobScheduleRepositoryTests.cs +++ b/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/JobScheduleRepositoryTests.cs @@ -37,8 +37,8 @@ public void JobScheduleRepository_ShouldBeAssignableToItsInterface_And_GenericIn repository.ShouldSatisfyAllConditions(() => { _ = repository.ShouldBeAssignableTo(); - _ = repository.ShouldBeAssignableTo>(); - _ = repository.ShouldBeAssignableTo>(); + _ = repository.ShouldBeAssignableTo>(); + _ = repository.ShouldBeAssignableTo>(); _ = repository.ShouldNotBeNull(); _ = repository.ShouldBeOfType(); diff --git a/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/ScanEmailDefinitionRepositoryTests.cs b/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/ScanEmailDefinitionRepositoryTests.cs index 3bae19a..d4c857e 100644 --- a/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/ScanEmailDefinitionRepositoryTests.cs +++ b/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/ScanEmailDefinitionRepositoryTests.cs @@ -37,8 +37,8 @@ public void ScanEmailDefinitionRepository_ShouldBeAssignableToItsInterface_And_G repository.ShouldSatisfyAllConditions(() => { _ = repository.ShouldBeAssignableTo(); - _ = repository.ShouldBeAssignableTo>(); - _ = repository.ShouldBeAssignableTo>(); + _ = repository.ShouldBeAssignableTo>(); + _ = repository.ShouldBeAssignableTo>(); _ = repository.ShouldNotBeNull(); _ = repository.ShouldBeOfType(); diff --git a/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/UserRepositoryTests.cs b/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/UserRepositoryTests.cs index 6cb9b19..cd039aa 100644 --- a/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/UserRepositoryTests.cs +++ b/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/UserRepositoryTests.cs @@ -37,8 +37,8 @@ public void UserRepository_ShouldBeAssignableToItsInterface_And_GenericInterface repository.ShouldSatisfyAllConditions(() => { _ = repository.ShouldBeAssignableTo(); - _ = repository.ShouldBeAssignableTo>(); - _ = repository.ShouldBeAssignableTo>(); + _ = repository.ShouldBeAssignableTo>(); + _ = repository.ShouldBeAssignableTo>(); _ = repository.ShouldNotBeNull(); _ = repository.ShouldBeOfType(); diff --git a/InvoiceReminder.sln b/InvoiceReminder.sln index 1e221d8..b55f07a 100644 --- a/InvoiceReminder.sln +++ b/InvoiceReminder.sln @@ -61,6 +61,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InvoiceReminder.ExternalSer EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InvoiceReminder.UnitTests.Assets", "InvoiceReminder.UnitTests.Assets\InvoiceReminder.UnitTests.Assets.csproj", "{0D3F963F-37A2-40E6-865D-0FD732FC5376}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InvoiceReminder.ArchitectureTests", "InvoiceReminder.ArchitectureTests\InvoiceReminder.ArchitectureTests.csproj", "{F8FE30DD-52E6-4981-96A7-D4D487522A26}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -127,6 +129,10 @@ Global {0D3F963F-37A2-40E6-865D-0FD732FC5376}.Debug|Any CPU.Build.0 = Debug|Any CPU {0D3F963F-37A2-40E6-865D-0FD732FC5376}.Release|Any CPU.ActiveCfg = Release|Any CPU {0D3F963F-37A2-40E6-865D-0FD732FC5376}.Release|Any CPU.Build.0 = Release|Any CPU + {F8FE30DD-52E6-4981-96A7-D4D487522A26}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F8FE30DD-52E6-4981-96A7-D4D487522A26}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F8FE30DD-52E6-4981-96A7-D4D487522A26}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F8FE30DD-52E6-4981-96A7-D4D487522A26}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -153,6 +159,7 @@ Global {1CC0FC52-71DE-4FEB-8A8F-3B8F879D33C1} = {67471F47-0B78-4F8A-95CD-145F78F644C3} {45590E22-E032-476B-848F-D43669E82DA2} = {67471F47-0B78-4F8A-95CD-145F78F644C3} {0D3F963F-37A2-40E6-865D-0FD732FC5376} = {67471F47-0B78-4F8A-95CD-145F78F644C3} + {F8FE30DD-52E6-4981-96A7-D4D487522A26} = {67471F47-0B78-4F8A-95CD-145F78F644C3} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {6165C451-34A2-4F1A-88D1-21F3EB4B587B}