diff --git a/.github/workflows/workflow-ci.yml b/.github/workflows/workflow-ci.yml index 8fc162f..48b1940 100644 --- a/.github/workflows/workflow-ci.yml +++ b/.github/workflows/workflow-ci.yml @@ -22,6 +22,14 @@ jobs: with: dotnet-version: 10.0.x + - name: Set UTF-8 locale + run: | + sudo apt-get update + sudo apt-get install -y locales + sudo locale-gen en_US.UTF-8 + echo "LANG=en_US.UTF-8" >> $GITHUB_ENV + echo "LC_ALL=en_US.UTF-8" >> $GITHUB_ENV + - name: Restore dependencies run: dotnet restore diff --git a/CodeCoverage.runsettings b/CodeCoverage.runsettings index 2e8682f..bfcbd04 100644 --- a/CodeCoverage.runsettings +++ b/CodeCoverage.runsettings @@ -1,8 +1,5 @@ - - 1 - diff --git a/Directory.Packages.props b/Directory.Packages.props index 31eb18a..4b525de 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -3,56 +3,61 @@ true true + + + + + + + + + + + + + + + + + + + + + + + - + + + - - - - - - - - - - - - - - - - - - - - - + - + - - + - + + - + diff --git a/InvoiceReminder.API.UnitTests/Endpoints/InvoiceEndpointsTests.cs b/InvoiceReminder.API.UnitTests/Endpoints/InvoiceEndpointsTests.cs index 505d4ed..d258d3c 100644 --- a/InvoiceReminder.API.UnitTests/Endpoints/InvoiceEndpointsTests.cs +++ b/InvoiceReminder.API.UnitTests/Endpoints/InvoiceEndpointsTests.cs @@ -1,3 +1,4 @@ +using Bogus; using InvoiceReminder.API.UnitTests.Factories; using InvoiceReminder.Application.Interfaces; using InvoiceReminder.Application.ViewModels; @@ -22,6 +23,8 @@ public sealed class InvoiceEndpointsTests private readonly HttpClient _client; private readonly IAuthorizationService _authorizationService; private readonly IInvoiceAppService _invoiceAppService; + private readonly Faker _invoiceViewModelFaker; + private readonly Faker _faker; private const string basepath = "/api/invoice"; public TestContext TestContext { get; set; } @@ -34,6 +37,26 @@ public InvoiceEndpointsTests() _client = factory.CreateClient(); _authorizationService = serviceProvider.GetRequiredService(); _invoiceAppService = serviceProvider.GetRequiredService(); + _faker = new Faker(); + + _invoiceViewModelFaker = new Faker() + .RuleFor(i => i.Id, faker => faker.Random.Guid()) + .RuleFor(i => i.UserId, faker => faker.Random.Guid()) + .RuleFor(i => i.Bank, faker => faker.PickRandom( + "Banco do Brasil", + "Bradesco", + "Itaú", + "Caixa Econômica Federal", + "Santander", + "Safra", + "Citibank", + "BTG Pactual")) + .RuleFor(i => i.Beneficiary, faker => faker.Person.FullName) + .RuleFor(i => i.Amount, faker => faker.Finance.Amount(10, 10000)) + .RuleFor(i => i.Barcode, faker => faker.Random.AlphaNumeric(44)) + .RuleFor(i => i.DueDate, faker => faker.Date.Future().ToUniversalTime()) + .RuleFor(i => i.CreatedAt, faker => faker.Date.Past().ToUniversalTime()) + .RuleFor(i => i.UpdatedAt, faker => faker.Date.Recent().ToUniversalTime()); } #region MapGet Tests @@ -44,29 +67,8 @@ public async Task GetAllInvoices_WhenUserIsAuthenticated_ShouldReturnOk() var request = new HttpRequestMessage(HttpMethod.Get, basepath); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "test_token"); - var expectedResult = Result>.Success( - [ - new() { - Id = Guid.NewGuid(), - Bank = "Banco do Brasil", - Beneficiary = "João da Silva", - Amount = 100.00m, - Barcode = "12345678901234567890123456789012345678901234", - DueDate = DateTime.UtcNow.AddDays(30), - CreatedAt = DateTime.UtcNow, - UpdatedAt = DateTime.UtcNow - }, - new() { - Id = Guid.NewGuid(), - Bank = "Banco do Brasil", - Beneficiary = "José da Silva", - Amount = 100.00m, - Barcode = "12345678901234567890123456789012345678901234", - DueDate = DateTime.UtcNow.AddDays(30), - CreatedAt = DateTime.UtcNow, - UpdatedAt = DateTime.UtcNow - } - ]); + var invoices = _invoiceViewModelFaker.Generate(2); + var expectedResult = Result>.Success(invoices); _ = _invoiceAppService.GetAll().Returns(expectedResult); @@ -140,17 +142,7 @@ public async Task GetInvoiceById_WhenUserIsAuthenticated_ShouldReturnOk() request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "test_token"); var expectedResult = Result.Success( - new() - { - Id = id, - Bank = "Banco do Brasil", - Beneficiary = "João da Silva", - Amount = 100.00m, - Barcode = "12345678901234567890123456789012345678901234", - DueDate = DateTime.UtcNow.AddDays(30), - CreatedAt = DateTime.UtcNow, - UpdatedAt = DateTime.UtcNow - }); + _invoiceViewModelFaker.Clone().RuleFor(i => i.Id, id).Generate()); _ = _invoiceAppService.GetByIdAsync(Arg.Any(), Arg.Any()).Returns(expectedResult); @@ -274,21 +266,12 @@ public async Task GetInvoiceById_WhenUserIsAuthenticatedButNotExists_ShouldRetur public async Task GetInvoiceByBarcode_WhenUserIsAuthenticated_ShouldReturnOk() { // Arrange - var value = "12345678901234567890123456789012345678901234"; - var request = new HttpRequestMessage(HttpMethod.Get, $"{basepath}/getby-barcode/{value}"); + var barcode = _faker.Random.AlphaNumeric(44); + var request = new HttpRequestMessage(HttpMethod.Get, $"{basepath}/getby-barcode/{barcode}"); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "test_token"); - var expectedResult = Result.Success(new() - { - Id = Guid.NewGuid(), - Bank = "Banco do Brasil", - Beneficiary = "João da Silva", - Amount = 100.00m, - Barcode = "12345678901234567890123456789012345678901234", - DueDate = DateTime.UtcNow.AddDays(30), - CreatedAt = DateTime.UtcNow, - UpdatedAt = DateTime.UtcNow - }); + var expectedResult = Result.Success( + _invoiceViewModelFaker.Clone().RuleFor(i => i.Barcode, barcode).Generate()); _ = _invoiceAppService.GetByBarcodeAsync(Arg.Any(), Arg.Any()) .Returns(expectedResult); @@ -313,8 +296,8 @@ public async Task GetInvoiceByBarcode_WhenUserIsAuthenticated_ShouldReturnOk() public async Task GetInvoiceByBarcode_WhenUserIsNotAuthenticated_ShouldReturnUnauthorized() { // Arrange - var value = "12345678901234567890123456789012345678901234"; - var request = new HttpRequestMessage(HttpMethod.Get, $"{basepath}/getby-barcode/{value}"); + var barcode = _faker.Random.AlphaNumeric(44); + var request = new HttpRequestMessage(HttpMethod.Get, $"{basepath}/getby-barcode/{barcode}"); _ = _authorizationService.AuthorizeAsync(Arg.Any(), Arg.Any(), Arg.Any>()) @@ -331,8 +314,8 @@ public async Task GetInvoiceByBarcode_WhenUserIsNotAuthenticated_ShouldReturnUna public async Task GetInvoiceByBarcode_WhenUserIsAuthenticatedButServiceFails_ShouldReturnBadRequest() { // Arrange - var value = "12345678901234567890123456789012345678901234"; - var request = new HttpRequestMessage(HttpMethod.Get, $"{basepath}/getby-barcode/{value}"); + var barcode = _faker.Random.AlphaNumeric(44); + var request = new HttpRequestMessage(HttpMethod.Get, $"{basepath}/getby-barcode/{barcode}"); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "test_token"); _ = _invoiceAppService.GetByBarcodeAsync(Arg.Any(), Arg.Any()) @@ -358,8 +341,8 @@ public async Task GetInvoiceByBarcode_WhenUserIsAuthenticatedButServiceFails_Sho public async Task GetInvoiceByBarcode_WhenUserIsAuthenticatedButServiceFails_ShouldReturnInternalServerError() { // Arrange - var value = "12345678901234567890123456789012345678901234"; - var request = new HttpRequestMessage(HttpMethod.Get, $"{basepath}/getby-barcode/{value}"); + var barcode = _faker.Random.AlphaNumeric(44); + var request = new HttpRequestMessage(HttpMethod.Get, $"{basepath}/getby-barcode/{barcode}"); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "test_token"); _ = _invoiceAppService.GetByBarcodeAsync(Arg.Any(), Arg.Any()) @@ -385,8 +368,8 @@ public async Task GetInvoiceByBarcode_WhenUserIsAuthenticatedButServiceFails_Sho public async Task GetInvoiceByBarcode_WhenUserIsAuthenticatedButNotExists_ShouldReturnNotFound() { // Arrange - var value = "12345678901234567890123456789012345678901234"; - var request = new HttpRequestMessage(HttpMethod.Get, $"{basepath}/getby-barcode/{value}"); + var barcode = _faker.Random.AlphaNumeric(44); + var request = new HttpRequestMessage(HttpMethod.Get, $"{basepath}/getby-barcode/{barcode}"); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "test_token"); var expectedResult = Result.Failure($"Invoice not Found."); @@ -421,18 +404,7 @@ public async Task CreateInvoice_WhenUserIsAuthenticated_ShouldReturnCreated() var request = new HttpRequestMessage(HttpMethod.Post, basepath); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "test_token"); - var invoiceViewModel = new InvoiceViewModel - { - Id = Guid.NewGuid(), - Bank = "Banco do Brasil", - Beneficiary = "João da Silva", - Amount = 100.00m, - Barcode = "12345678901234567890123456789012345678901234", - DueDate = DateTime.UtcNow.AddDays(30), - CreatedAt = DateTime.UtcNow, - UpdatedAt = DateTime.UtcNow - }; - + var invoiceViewModel = _invoiceViewModelFaker.Generate(); var expectedResult = Result.Success(invoiceViewModel); _ = _invoiceAppService.AddAsync(Arg.Any(), Arg.Any()) @@ -479,18 +451,7 @@ public async Task CreateInvoice_WhenUserIsAuthenticatedButServiceFails_ShouldRet var request = new HttpRequestMessage(HttpMethod.Post, basepath); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "test_token"); - var invoiceViewModel = new InvoiceViewModel - { - Id = Guid.NewGuid(), - Bank = "Banco do Brasil", - Beneficiary = "João da Silva", - Amount = 100.00m, - Barcode = "12345678901234567890123456789012345678901234", - DueDate = DateTime.UtcNow.AddDays(30), - CreatedAt = DateTime.UtcNow, - UpdatedAt = DateTime.UtcNow - }; - + var invoiceViewModel = _invoiceViewModelFaker.Generate(); var expectedResult = Result.Failure("Service error"); _ = _invoiceAppService.AddAsync(Arg.Any(), Arg.Any()) @@ -524,18 +485,7 @@ public async Task UpdateInvoice_WhenUserIsAuthenticated_ShouldReturnOk() var request = new HttpRequestMessage(HttpMethod.Put, basepath); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "test_token"); - var invoiceViewModel = new InvoiceViewModel - { - Id = Guid.NewGuid(), - Bank = "Banco do Brasil", - Beneficiary = "João da Silva", - Amount = 100.00m, - Barcode = "12345678901234567890123456789012345678901234", - DueDate = DateTime.UtcNow.AddDays(30), - CreatedAt = DateTime.UtcNow, - UpdatedAt = DateTime.UtcNow - }; - + var invoiceViewModel = _invoiceViewModelFaker.Generate(); var expectedResult = Result.Success(invoiceViewModel); _ = _invoiceAppService.UpdateAsync(Arg.Any(), Arg.Any()) @@ -582,18 +532,7 @@ public async Task UpdateInvoice_WhenUserIsAuthenticatedButServiceFails_ShouldRet var request = new HttpRequestMessage(HttpMethod.Put, basepath); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "test_token"); - var invoiceViewModel = new InvoiceViewModel - { - Id = Guid.NewGuid(), - Bank = "Banco do Brasil", - Beneficiary = "João da Silva", - Amount = 100.00m, - Barcode = "12345678901234567890123456789012345678901234", - DueDate = DateTime.UtcNow.AddDays(30), - CreatedAt = DateTime.UtcNow, - UpdatedAt = DateTime.UtcNow - }; - + var invoiceViewModel = _invoiceViewModelFaker.Generate(); var expectedResult = Result.Failure("Service error"); _ = _invoiceAppService.UpdateAsync(Arg.Any(), Arg.Any()) diff --git a/InvoiceReminder.API.UnitTests/Endpoints/JobScheduleEndPointsTests.cs b/InvoiceReminder.API.UnitTests/Endpoints/JobScheduleEndPointsTests.cs index 172d862..ee2d111 100644 --- a/InvoiceReminder.API.UnitTests/Endpoints/JobScheduleEndPointsTests.cs +++ b/InvoiceReminder.API.UnitTests/Endpoints/JobScheduleEndPointsTests.cs @@ -1,3 +1,4 @@ +using Bogus; using InvoiceReminder.API.UnitTests.Factories; using InvoiceReminder.Application.Interfaces; using InvoiceReminder.Application.ViewModels; @@ -22,6 +23,7 @@ public sealed class JobScheduleEndPointsTests private readonly HttpClient _client; private readonly IAuthorizationService _authorizationService; private readonly IJobScheduleAppService _jobScheduleAppService; + private readonly Faker _jobScheduleViewModelFaker; private const string basepath = "/api/job_schedule"; public TestContext TestContext { get; set; } @@ -34,6 +36,20 @@ public JobScheduleEndPointsTests() _client = factory.CreateClient(); _authorizationService = serviceProvider.GetRequiredService(); _jobScheduleAppService = serviceProvider.GetRequiredService(); + + _jobScheduleViewModelFaker = new Faker() + .RuleFor(j => j.Id, faker => faker.Random.Guid()) + .RuleFor(j => j.UserId, faker => faker.Random.Guid()) + .RuleFor(j => j.CronExpression, faker => faker.PickRandom( + "0 0/5 * * * ?", // Every 5 minutes + "0 0 * * * ?", // Every hour + "0 0 0 * * ?", // Every day at midnight + "0 0 0 ? * MON", // Every Monday + "0 0 0 1 * ?", // First day of month + "0 0 0 1 1 ?", // Every January 1st + "0 0 12 * * ?")) // Every noon + .RuleFor(j => j.CreatedAt, faker => faker.Date.Past().ToUniversalTime()) + .RuleFor(j => j.UpdatedAt, faker => faker.Date.Recent().ToUniversalTime()); } #region MapGet Tests @@ -44,23 +60,8 @@ public async Task GetAllJobSchedules_WhenUserIsAuthenticated_ShouldReturnOk() var request = new HttpRequestMessage(HttpMethod.Get, basepath); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "test_token"); - var expectedResult = Result>.Success( - [ - new() { - Id = Guid.NewGuid(), - UserId = Guid.NewGuid(), - CronExpression = "0 0 * * *", - CreatedAt = DateTime.UtcNow, - UpdatedAt = DateTime.UtcNow - }, - new() { - Id = Guid.NewGuid(), - UserId = Guid.NewGuid(), - CronExpression = "0 0 * 1 1", - CreatedAt = DateTime.UtcNow, - UpdatedAt = DateTime.UtcNow - } - ]); + var jobSchedules = _jobScheduleViewModelFaker.Generate(2); + var expectedResult = Result>.Success(jobSchedules); _ = _jobScheduleAppService.GetAll().Returns(expectedResult); @@ -134,14 +135,7 @@ public async Task GetJobScheduleById_WhenUserIsAuthenticated_ShouldReturnOk() request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "test_token"); var expectedResult = Result.Success( - new() - { - Id = id, - UserId = Guid.NewGuid(), - CronExpression = "0 0 * 1 2", - CreatedAt = DateTime.UtcNow, - UpdatedAt = DateTime.UtcNow - }); + _jobScheduleViewModelFaker.RuleFor(j => j.Id, id).Generate()); _ = _jobScheduleAppService.GetByIdAsync(Arg.Any(), Arg.Any()).Returns(expectedResult); @@ -269,23 +263,11 @@ public async Task GetJobScheduleByUserId_WhenUserIsAuthenticated_ShouldReturnOk( var request = new HttpRequestMessage(HttpMethod.Get, $"{basepath}/getby-userid/{id}"); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "test_token"); - var expectedResult = Result>.Success( - [ - new() { - Id = Guid.NewGuid(), - UserId = id, - CronExpression = "0 0 * * *", - CreatedAt = DateTime.UtcNow, - UpdatedAt = DateTime.UtcNow - }, - new() { - Id = Guid.NewGuid(), - UserId = id, - CronExpression = "0 0 * 1 1", - CreatedAt = DateTime.UtcNow, - UpdatedAt = DateTime.UtcNow - } - ]); + var jobSchedules = _jobScheduleViewModelFaker + .RuleFor(j => j.UserId, id) + .Generate(2); + + var expectedResult = Result>.Success(jobSchedules); _ = _jobScheduleAppService.GetByUserIdAsync(Arg.Any(), Arg.Any()) .Returns(expectedResult); @@ -417,15 +399,7 @@ public async Task CreateJobSchedule_WhenUserIsAuthenticated_ShouldReturnCreated( var request = new HttpRequestMessage(HttpMethod.Post, basepath); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "test_token"); - var jobScheduleViewModel = new JobScheduleViewModel - { - Id = Guid.NewGuid(), - UserId = Guid.NewGuid(), - CronExpression = "0 0 * * *", - CreatedAt = DateTime.UtcNow, - UpdatedAt = DateTime.UtcNow - }; - + var jobScheduleViewModel = _jobScheduleViewModelFaker.Generate(); var expectedResult = Result.Success(jobScheduleViewModel); _ = _jobScheduleAppService.AddNewJobAsync(Arg.Any(), Arg.Any()) @@ -472,15 +446,7 @@ public async Task CreateJobSchedule_WhenUserIsAuthenticatedButServiceFails_Shoul var request = new HttpRequestMessage(HttpMethod.Post, basepath); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "test_token"); - var jobScheduleViewModel = new JobScheduleViewModel - { - Id = Guid.NewGuid(), - UserId = Guid.NewGuid(), - CronExpression = "0 0 * * *", - CreatedAt = DateTime.UtcNow, - UpdatedAt = DateTime.UtcNow - }; - + var jobScheduleViewModel = _jobScheduleViewModelFaker.Generate(); var expectedResult = Result.Failure("Service error"); _ = _jobScheduleAppService.AddNewJobAsync(Arg.Any(), Arg.Any()) @@ -515,15 +481,7 @@ public async Task UpdateJobSchedule_WhenUserIsAuthenticated_ShouldReturnOk() var request = new HttpRequestMessage(HttpMethod.Put, basepath); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "test_token"); - var jobScheduleViewModel = new JobScheduleViewModel - { - Id = id, - UserId = Guid.NewGuid(), - CronExpression = "0 0 * * *", - CreatedAt = DateTime.UtcNow, - UpdatedAt = DateTime.UtcNow - }; - + var jobScheduleViewModel = _jobScheduleViewModelFaker.RuleFor(j => j.Id, id).Generate(); var expectedResult = Result.Success(jobScheduleViewModel); _ = _jobScheduleAppService.UpdateAsync(Arg.Any(), Arg.Any()) @@ -571,15 +529,7 @@ public async Task UpdateJobSchedule_WhenUserIsAuthenticatedButServiceFails_Shoul var request = new HttpRequestMessage(HttpMethod.Put, basepath); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "test_token"); - var jobScheduleViewModel = new JobScheduleViewModel - { - Id = id, - UserId = Guid.NewGuid(), - CronExpression = "0 0 * * *", - CreatedAt = DateTime.UtcNow, - UpdatedAt = DateTime.UtcNow - }; - + var jobScheduleViewModel = _jobScheduleViewModelFaker.RuleFor(j => j.Id, id).Generate(); var expectedResult = Result.Failure("Service error"); _ = _jobScheduleAppService.UpdateAsync(Arg.Any(), Arg.Any()) @@ -612,7 +562,9 @@ public async Task DeleteJobSchedule_WhenUserIsAuthenticated_ShouldReturnNoConten // Arrange var request = new HttpRequestMessage(HttpMethod.Delete, basepath); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "test_token"); - request.Content = JsonContent.Create(new JobScheduleViewModel { Id = Guid.NewGuid() }); + + var jobScheduleViewModel = _jobScheduleViewModelFaker.Generate(); + request.Content = JsonContent.Create(jobScheduleViewModel); var expectedResult = Result.Success(null); diff --git a/InvoiceReminder.API.UnitTests/InvoiceReminder.API.UnitTests.csproj b/InvoiceReminder.API.UnitTests/InvoiceReminder.API.UnitTests.csproj index cf1117e..46c3e67 100644 --- a/InvoiceReminder.API.UnitTests/InvoiceReminder.API.UnitTests.csproj +++ b/InvoiceReminder.API.UnitTests/InvoiceReminder.API.UnitTests.csproj @@ -16,6 +16,7 @@ + diff --git a/InvoiceReminder.API/Endpoints/GoogleOAuthEndpoints.cs b/InvoiceReminder.API/Endpoints/GoogleOAuthEndpoints.cs index 7ae0618..54993c1 100644 --- a/InvoiceReminder.API/Endpoints/GoogleOAuthEndpoints.cs +++ b/InvoiceReminder.API/Endpoints/GoogleOAuthEndpoints.cs @@ -4,11 +4,19 @@ namespace InvoiceReminder.API.Endpoints; public class GoogleOAuthEndpoints : IEndpointDefinition { + private const string basepath = "/api/google_oauth"; + public void RegisterEndpoints(IEndpointRouteBuilder endpoints) { - var basepath = "/api/google_oauth"; var endpoint = endpoints.MapGroup(basepath).WithName("GoogleOAuthEndpoints"); + MapGetAuthUrl(endpoint); + MapAuthorize(endpoint); + MapRevoke(endpoint); + } + + private static void MapGetAuthUrl(RouteGroupBuilder endpoint) + { _ = endpoint.MapGet("/get-auth-url/{id}", (IGoogleOAuthService oAuthService, Guid id) => { var result = oAuthService.GetAuthorizationUrl(id.ToString()); @@ -22,7 +30,10 @@ public void RegisterEndpoints(IEndpointRouteBuilder endpoints) .Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status400BadRequest) .Produces(StatusCodes.Status500InternalServerError); + } + private static void MapAuthorize(RouteGroupBuilder endpoint) + { _ = endpoint.MapGet("/authorize", async (IGoogleOAuthService oAuthService, CancellationToken ct, Guid state, string code) => { @@ -37,7 +48,10 @@ public void RegisterEndpoints(IEndpointRouteBuilder endpoints) .Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status400BadRequest) .Produces(StatusCodes.Status500InternalServerError); + } + private static void MapRevoke(RouteGroupBuilder endpoint) + { _ = endpoint.MapDelete("/revoke", async (IGoogleOAuthService oAuthService, CancellationToken ct, Guid id) => { var result = await oAuthService.RevokeAuthorizationAsync(id, ct); diff --git a/InvoiceReminder.API/Endpoints/InvoiceEndpoints.cs b/InvoiceReminder.API/Endpoints/InvoiceEndpoints.cs index b22ca2a..5ef59a6 100644 --- a/InvoiceReminder.API/Endpoints/InvoiceEndpoints.cs +++ b/InvoiceReminder.API/Endpoints/InvoiceEndpoints.cs @@ -6,11 +6,22 @@ namespace InvoiceReminder.API.Endpoints; public class InvoiceEndpoints : IEndpointDefinition { + private const string basepath = "/api/invoice"; + public void RegisterEndpoints(IEndpointRouteBuilder endpoints) { - var basepath = "/api/invoice"; var endpoint = endpoints.MapGroup(basepath).WithName("InvoiceEndpoints"); + MapGetInvoices(endpoint); + MapGetInvoice(endpoint); + MapGetInvoiceByBarcode(endpoint); + MapCreateInvoice(endpoint); + MapUpdateInvoice(endpoint); + MapDeleteInvoice(endpoint); + } + + private static void MapGetInvoices(RouteGroupBuilder endpoint) + { _ = endpoint.MapGet("/", (IInvoiceAppService invoiceAppService) => { var result = invoiceAppService.GetAll(); @@ -24,7 +35,10 @@ public void RegisterEndpoints(IEndpointRouteBuilder endpoints) .Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status400BadRequest) .Produces(StatusCodes.Status500InternalServerError); + } + private static void MapGetInvoice(RouteGroupBuilder endpoint) + { _ = endpoint.MapGet("/{id}", async (IInvoiceAppService invoiceAppService, CancellationToken ct, Guid id) => { var result = await invoiceAppService.GetByIdAsync(id, ct); @@ -37,8 +51,12 @@ public void RegisterEndpoints(IEndpointRouteBuilder endpoints) .RequireAuthorization() .Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status400BadRequest) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status500InternalServerError); + } + private static void MapGetInvoiceByBarcode(RouteGroupBuilder endpoint) + { _ = endpoint.MapGet("/getby-barcode/{value}", async (IInvoiceAppService invoiceAppService, CancellationToken ct, string value) => { @@ -52,8 +70,12 @@ public void RegisterEndpoints(IEndpointRouteBuilder endpoints) .RequireAuthorization() .Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status400BadRequest) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status500InternalServerError); + } + private static void MapCreateInvoice(RouteGroupBuilder endpoint) + { _ = endpoint.MapPost("/", async (IInvoiceAppService invoiceAppService, CancellationToken ct, [FromBody] InvoiceViewModel invoiceViewModel) => { @@ -68,7 +90,10 @@ public void RegisterEndpoints(IEndpointRouteBuilder endpoints) .Produces(StatusCodes.Status201Created) .Produces(StatusCodes.Status400BadRequest) .Produces(StatusCodes.Status500InternalServerError); + } + private static void MapUpdateInvoice(RouteGroupBuilder endpoint) + { _ = endpoint.MapPut("/", async (IInvoiceAppService invoiceAppService, CancellationToken ct, [FromBody] InvoiceViewModel invoiceViewModel) => { @@ -83,7 +108,10 @@ public void RegisterEndpoints(IEndpointRouteBuilder endpoints) .Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status400BadRequest) .Produces(StatusCodes.Status500InternalServerError); + } + private static void MapDeleteInvoice(RouteGroupBuilder endpoint) + { _ = endpoint.MapDelete("/", async (IInvoiceAppService invoiceAppService, CancellationToken ct, [FromBody] InvoiceViewModel invoiceViewModel) => { diff --git a/InvoiceReminder.API/Endpoints/JobScheduleEndpoints.cs b/InvoiceReminder.API/Endpoints/JobScheduleEndpoints.cs index 9f7e9c9..2db5d02 100644 --- a/InvoiceReminder.API/Endpoints/JobScheduleEndpoints.cs +++ b/InvoiceReminder.API/Endpoints/JobScheduleEndpoints.cs @@ -6,11 +6,22 @@ namespace InvoiceReminder.API.Endpoints; public class JobScheduleEndpoints : IEndpointDefinition { + private const string basepath = "/api/job_schedule"; + public void RegisterEndpoints(IEndpointRouteBuilder endpoints) { - var basepath = "/api/job_schedule"; var endpoint = endpoints.MapGroup(basepath).WithName("JobScheduleEndpoints"); + MapGetJobSchedules(endpoint); + MapGetJobSchedule(endpoint); + MapGetJobScheduleByUserId(endpoint); + MapCreateJobSchedule(endpoint); + MapUpdateJobSchedule(endpoint); + MapDeleteJobSchedule(endpoint); + } + + private static void MapGetJobSchedules(RouteGroupBuilder endpoint) + { _ = endpoint.MapGet("/", (IJobScheduleAppService jobScheduleAppService) => { var result = jobScheduleAppService.GetAll(); @@ -24,7 +35,10 @@ public void RegisterEndpoints(IEndpointRouteBuilder endpoints) .Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status400BadRequest) .Produces(StatusCodes.Status500InternalServerError); + } + private static void MapGetJobSchedule(RouteGroupBuilder endpoint) + { _ = endpoint.MapGet("/{id}", async (IJobScheduleAppService jobScheduleAppService, CancellationToken ct, Guid id) => { var result = await jobScheduleAppService.GetByIdAsync(id, ct); @@ -39,7 +53,10 @@ public void RegisterEndpoints(IEndpointRouteBuilder endpoints) .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status400BadRequest) .Produces(StatusCodes.Status500InternalServerError); + } + private static void MapGetJobScheduleByUserId(RouteGroupBuilder endpoint) + { _ = endpoint.MapGet("/getby-userid/{id}", async (IJobScheduleAppService jobScheduleAppService, CancellationToken ct, Guid id) => { @@ -55,10 +72,12 @@ public void RegisterEndpoints(IEndpointRouteBuilder endpoints) .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status400BadRequest) .Produces(StatusCodes.Status500InternalServerError); + } + private static void MapCreateJobSchedule(RouteGroupBuilder endpoint) + { _ = endpoint.MapPost("/", - async (IJobScheduleAppService jobScheduleAppService, - CancellationToken ct, + async (IJobScheduleAppService jobScheduleAppService, CancellationToken ct, [FromBody] JobScheduleViewModel jobScheduleViewModel) => { var result = await jobScheduleAppService.AddNewJobAsync(jobScheduleViewModel, ct); @@ -72,10 +91,12 @@ public void RegisterEndpoints(IEndpointRouteBuilder endpoints) .Produces(StatusCodes.Status201Created) .Produces(StatusCodes.Status400BadRequest) .Produces(StatusCodes.Status500InternalServerError); + } + private static void MapUpdateJobSchedule(RouteGroupBuilder endpoint) + { _ = endpoint.MapPut("/", - async (IJobScheduleAppService jobScheduleAppService, - CancellationToken ct, + async (IJobScheduleAppService jobScheduleAppService, CancellationToken ct, [FromBody] JobScheduleViewModel jobScheduleViewModel) => { var result = await jobScheduleAppService.UpdateAsync(jobScheduleViewModel, ct); @@ -89,10 +110,12 @@ public void RegisterEndpoints(IEndpointRouteBuilder endpoints) .Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status400BadRequest) .Produces(StatusCodes.Status500InternalServerError); + } + private static void MapDeleteJobSchedule(RouteGroupBuilder endpoint) + { _ = endpoint.MapDelete("/", - async (IJobScheduleAppService jobScheduleAppService, - CancellationToken ct, + async (IJobScheduleAppService jobScheduleAppService, CancellationToken ct, [FromBody] JobScheduleViewModel jobScheduleViewModel) => { var result = await jobScheduleAppService.RemoveAsync(jobScheduleViewModel, ct); diff --git a/InvoiceReminder.API/Endpoints/LoginEndpoints.cs b/InvoiceReminder.API/Endpoints/LoginEndpoints.cs index ca8c445..fe65f0b 100644 --- a/InvoiceReminder.API/Endpoints/LoginEndpoints.cs +++ b/InvoiceReminder.API/Endpoints/LoginEndpoints.cs @@ -8,9 +8,18 @@ namespace InvoiceReminder.API.Endpoints; public class LoginEndpoints : IEndpointDefinition { + private const string basepath = "/api/login"; + public void RegisterEndpoints(IEndpointRouteBuilder endpoints) { - _ = endpoints.MapPost("/api/login", + var endpoint = endpoints.MapGroup(basepath).WithName("LoginEndpoints"); + + MapLogin(endpoint); + } + + private static void MapLogin(IEndpointRouteBuilder endpoints) + { + _ = endpoints.MapPost("/", async (IJwtProvider jwtProvider, IUserAppService userAppService, CancellationToken ct, diff --git a/InvoiceReminder.API/Endpoints/ScanEmailDefinitionEndpoints.cs b/InvoiceReminder.API/Endpoints/ScanEmailDefinitionEndpoints.cs index 963265d..b84c469 100644 --- a/InvoiceReminder.API/Endpoints/ScanEmailDefinitionEndpoints.cs +++ b/InvoiceReminder.API/Endpoints/ScanEmailDefinitionEndpoints.cs @@ -6,11 +6,23 @@ namespace InvoiceReminder.API.Endpoints; public class ScanEmailDefinitionEndpoints : IEndpointDefinition { + private const string basepath = "/api/scan_email"; + public void RegisterEndpoints(IEndpointRouteBuilder endpoints) { - var basepath = "/api/scan_email"; var endpoint = endpoints.MapGroup(basepath).WithName("ScanEmailDefinitionEndpoints"); + MapGetScanEmailDefinitions(endpoint); + MapGetScanEmailDefinition(endpoint); + MapGetByUserId(endpoint); + MapGetBySenderEmailAddress(endpoint); + MapCreateScanEmailDefinition(endpoint); + MapUpdateScanEmailDefinition(endpoint); + MapDeleteScanEmailDefinition(endpoint); + } + + private static void MapGetScanEmailDefinitions(RouteGroupBuilder endpoint) + { _ = endpoint.MapGet("/", (IScanEmailDefinitionAppService scanEmailDefinitionAppService) => { var result = scanEmailDefinitionAppService.GetAll(); @@ -24,7 +36,10 @@ public void RegisterEndpoints(IEndpointRouteBuilder endpoints) .Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status400BadRequest) .Produces(StatusCodes.Status500InternalServerError); + } + private static void MapGetScanEmailDefinition(RouteGroupBuilder endpoint) + { _ = endpoint.MapGet("/{id}", async (IScanEmailDefinitionAppService scanEmailDefinitionAppService, CancellationToken ct, Guid id) => { @@ -38,8 +53,12 @@ public void RegisterEndpoints(IEndpointRouteBuilder endpoints) .RequireAuthorization() .Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status400BadRequest) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status500InternalServerError); + } + private static void MapGetByUserId(RouteGroupBuilder endpoint) + { _ = endpoint.MapGet("/getby-userid/{id}", async (IScanEmailDefinitionAppService scanEmailDefinitionAppService, CancellationToken ct, Guid id) => { @@ -53,8 +72,12 @@ public void RegisterEndpoints(IEndpointRouteBuilder endpoints) .RequireAuthorization() .Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status400BadRequest) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status500InternalServerError); + } + private static void MapGetBySenderEmailAddress(RouteGroupBuilder endpoint) + { _ = endpoint.MapGet("/{email}/{id}", async (IScanEmailDefinitionAppService scanEmailDefinitionAppService, CancellationToken ct, @@ -71,8 +94,12 @@ public void RegisterEndpoints(IEndpointRouteBuilder endpoints) .RequireAuthorization() .Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status400BadRequest) + .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status500InternalServerError); + } + private static void MapCreateScanEmailDefinition(RouteGroupBuilder endpoint) + { _ = endpoint.MapPost("/", async (IScanEmailDefinitionAppService scanEmailDefinitionAppService, CancellationToken ct, @@ -89,7 +116,10 @@ public void RegisterEndpoints(IEndpointRouteBuilder endpoints) .Produces(StatusCodes.Status201Created) .Produces(StatusCodes.Status400BadRequest) .Produces(StatusCodes.Status500InternalServerError); + } + private static void MapUpdateScanEmailDefinition(RouteGroupBuilder endpoint) + { _ = endpoint.MapPut("/", async (IScanEmailDefinitionAppService scanEmailDefinitionAppService, CancellationToken ct, @@ -106,10 +136,12 @@ public void RegisterEndpoints(IEndpointRouteBuilder endpoints) .Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status400BadRequest) .Produces(StatusCodes.Status500InternalServerError); + } + private static void MapDeleteScanEmailDefinition(RouteGroupBuilder endpoint) + { _ = endpoint.MapDelete("/", - async (IScanEmailDefinitionAppService scanEmailDefinitionAppService, - CancellationToken ct, + async (IScanEmailDefinitionAppService scanEmailDefinitionAppService, CancellationToken ct, [FromBody] ScanEmailDefinitionViewModel scanEmailDefinitionViewModel) => { var result = await scanEmailDefinitionAppService.RemoveAsync(scanEmailDefinitionViewModel, ct); diff --git a/InvoiceReminder.API/Endpoints/SendMessageEndpoints.cs b/InvoiceReminder.API/Endpoints/SendMessageEndpoints.cs index ded320c..61e8004 100644 --- a/InvoiceReminder.API/Endpoints/SendMessageEndpoints.cs +++ b/InvoiceReminder.API/Endpoints/SendMessageEndpoints.cs @@ -4,16 +4,29 @@ namespace InvoiceReminder.API.Endpoints; public class SendMessageEndpoints : IEndpointDefinition { + private const string basepath = "/api/send_message"; + public void RegisterEndpoints(IEndpointRouteBuilder endpoints) { - _ = endpoints.MapGet("/api/send_message/{id}", + var endpoint = endpoints.MapGroup(basepath).WithName("SendMessageEndpoints"); + + MapSendMessage(endpoint); + } + + private static void MapSendMessage(RouteGroupBuilder endpoint) + { + _ = endpoint.MapGet("/{id}", async (ISendMessageService messageService, CancellationToken ct, Guid id) => { var result = await messageService.SendMessage(id, ct); return !string.IsNullOrEmpty(result) ? Results.Ok(result) - : Results.Problem(result); + : Results.Problem( + statusCode: StatusCodes.Status500InternalServerError, + title: "Erro ao enviar mensagem", + detail: "O serviço não retornou um resultado válido." + ); }) .WithName("SendMessage") .RequireAuthorization() diff --git a/InvoiceReminder.API/Endpoints/UserEndpoints.cs b/InvoiceReminder.API/Endpoints/UserEndpoints.cs index 1a41bbb..eacb5b4 100644 --- a/InvoiceReminder.API/Endpoints/UserEndpoints.cs +++ b/InvoiceReminder.API/Endpoints/UserEndpoints.cs @@ -7,11 +7,23 @@ namespace InvoiceReminder.API.Endpoints; public class UserEndpoints : IEndpointDefinition { + private const string basepath = "/api/user"; + public void RegisterEndpoints(IEndpointRouteBuilder endpoints) { - var basepath = "/api/user"; var endpoint = endpoints.MapGroup(basepath).WithName("UserEndpoints"); + MapGetUsers(endpoint); + MapGetUser(endpoint); + MapGetUserByEmail(endpoint); + MapCreateUser(endpoint); + MapCreateUsers(endpoint); + MapUpdateUser(endpoint); + MapDeleteUser(endpoint); + } + + private static void MapGetUsers(RouteGroupBuilder endpoint) + { _ = endpoint.MapGet("/", (IUserAppService userAppService) => { var result = userAppService.GetAll(); @@ -25,21 +37,27 @@ public void RegisterEndpoints(IEndpointRouteBuilder endpoints) .Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status400BadRequest) .Produces(StatusCodes.Status500InternalServerError); + } + private static void MapGetUser(RouteGroupBuilder endpoint) + { _ = endpoint.MapGet("/{id}", async (IUserAppService userAppService, CancellationToken ct, Guid id) => - { - var result = await userAppService.GetByIdAsync(id, ct); + { + var result = await userAppService.GetByIdAsync(id, ct); - return result.IsSuccess - ? Results.Ok(result.Value) - : Results.NotFound(result.Error); - }) + return result.IsSuccess + ? Results.Ok(result.Value) + : Results.NotFound(result.Error); + }) .WithName("GetUser") .RequireAuthorization() .Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status500InternalServerError); + } + private static void MapGetUserByEmail(RouteGroupBuilder endpoint) + { _ = endpoint.MapGet("/getby-email/{value}", async (IUserAppService userAppService, CancellationToken ct, string value) => { @@ -54,7 +72,10 @@ public void RegisterEndpoints(IEndpointRouteBuilder endpoints) .Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status404NotFound) .Produces(StatusCodes.Status500InternalServerError); + } + private static void MapCreateUser(RouteGroupBuilder endpoint) + { _ = endpoint.MapPost("/", async (IUserAppService userAppService, CancellationToken ct, [FromBody] UserViewModel userViewModel) => { @@ -63,7 +84,7 @@ public void RegisterEndpoints(IEndpointRouteBuilder endpoints) var result = await userAppService.AddAsync(userViewModel, ct); return result.IsSuccess - ? Results.Created($"{basepath}/{result.Value.Email}", result.Value) + ? Results.Created($"{basepath}/{result.Value.Id}", result.Value) : Results.Problem(result.Error); }) .WithName("CreateUser") @@ -71,7 +92,10 @@ public void RegisterEndpoints(IEndpointRouteBuilder endpoints) .Produces(StatusCodes.Status201Created) .Produces(StatusCodes.Status400BadRequest) .Produces(StatusCodes.Status500InternalServerError); + } + private static void MapCreateUsers(RouteGroupBuilder endpoint) + { _ = endpoint.MapPost("/bulk-insert", async (IUserAppService userAppService, CancellationToken ct, [FromBody] ICollection usersViewModel) => { @@ -83,7 +107,7 @@ public void RegisterEndpoints(IEndpointRouteBuilder endpoints) var result = await userAppService.BulkInsertAsync(usersViewModel, ct); return result.IsSuccess - ? Results.Created($"{basepath}/", result.Value) + ? Results.Created($"{basepath}", result.Value) : Results.Problem(result.Error); }) .WithName("CreateUsers") @@ -91,7 +115,10 @@ public void RegisterEndpoints(IEndpointRouteBuilder endpoints) .Produces(StatusCodes.Status201Created) .Produces(StatusCodes.Status400BadRequest) .Produces(StatusCodes.Status500InternalServerError); + } + private static void MapUpdateUser(RouteGroupBuilder endpoint) + { _ = endpoint.MapPut("/", async (IUserAppService userAppService, CancellationToken ct, [FromBody] UserViewModel userViewModel) => { @@ -106,7 +133,10 @@ public void RegisterEndpoints(IEndpointRouteBuilder endpoints) .Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status400BadRequest) .Produces(StatusCodes.Status500InternalServerError); + } + private static void MapDeleteUser(RouteGroupBuilder endpoint) + { _ = endpoint.MapDelete("/", async (IUserAppService userAppService, CancellationToken ct, [FromBody] UserViewModel userViewModel) => { diff --git a/InvoiceReminder.API/Exceptions/GlobalExceptionHandler.cs b/InvoiceReminder.API/Exceptions/GlobalExceptionHandler.cs index e8b1f21..e400371 100644 --- a/InvoiceReminder.API/Exceptions/GlobalExceptionHandler.cs +++ b/InvoiceReminder.API/Exceptions/GlobalExceptionHandler.cs @@ -16,7 +16,10 @@ public GlobalExceptionHandler(IProblemDetailsService problemDetailsService, ILog public async ValueTask TryHandleAsync(HttpContext httpContext, Exception exception, CancellationToken cancellationToken) { - _logger.LogError(exception, "An Exception occurred: {Message}", exception.Message); + if (_logger.IsEnabled(LogLevel.Error)) + { + _logger.LogError(exception, "An Exception occurred: {Message}", exception.Message); + } httpContext.Response.StatusCode = exception switch { diff --git a/InvoiceReminder.Application.UnitTests/AppServices/BaseAppServiceTests.cs b/InvoiceReminder.Application.UnitTests/AppServices/BaseAppServiceTests.cs index 7e257f1..12ea633 100644 --- a/InvoiceReminder.Application.UnitTests/AppServices/BaseAppServiceTests.cs +++ b/InvoiceReminder.Application.UnitTests/AppServices/BaseAppServiceTests.cs @@ -1,3 +1,4 @@ +using Bogus; using InvoiceReminder.Application.AppServices; using InvoiceReminder.Data.Interfaces; using Mapster; @@ -13,6 +14,9 @@ public sealed class BaseAppServiceTests private readonly IBaseRepository _repository; private readonly IUnitOfWork _unitOfWork; + private readonly Faker _entityFaker; + private readonly Faker _entityViewModelFaker; + public TestContext TestContext { get; set; } public BaseAppServiceTests() @@ -20,13 +24,27 @@ public BaseAppServiceTests() _repository = Substitute.For>(); _unitOfWork = Substitute.For(); _appService = new BaseAppService(_repository, _unitOfWork); + + _entityFaker = new Faker() + .RuleFor(e => e.Id, faker => faker.Random.Guid()) + .RuleFor(e => e.Name, faker => faker.Person.FullName) + .RuleFor(e => e.CreatedAt, faker => faker.Date.Past(1).ToUniversalTime()) + .RuleFor(e => e.UpdatedAt, (faker, entity) => faker.Date.Between(entity.CreatedAt, DateTime.UtcNow) + .ToUniversalTime()); + + _entityViewModelFaker = new Faker() + .RuleFor(e => e.Id, faker => faker.Random.Guid()) + .RuleFor(e => e.Name, faker => faker.Person.FullName) + .RuleFor(e => e.CreatedAt, faker => faker.Date.Past(1).ToUniversalTime()) + .RuleFor(e => e.UpdatedAt, (faker, entity) => faker.Date.Between(entity.CreatedAt, DateTime.UtcNow) + .ToUniversalTime()); } [TestMethod] public async Task AddAsync_Should_Return_Success_When_Entity_Is_Valid() { // Arrange - var viewModel = new TestEntityViewModel { Name = "Test" }; + var viewModel = _entityViewModelFaker.Generate(); _ = _repository.AddAsync(Arg.Any(), Arg.Any()) .Returns(viewModel.Adapt()); @@ -82,11 +100,7 @@ public async Task BulkInsertAsync_Should_Return_Failure_When_ViewModels_Are_Null public async Task BulkInsertAsync_Should_Return_Success_When_ViewModels_Are_Valid() { // Arrange - var viewModels = new List - { - new() { Name = "Test1" }, - new() { Name = "Test2" } - }; + var viewModels = _entityViewModelFaker.Generate(2); _ = _repository.BulkInsertAsync(Arg.Any>(), Arg.Any()) .Returns(viewModels.Count); @@ -131,11 +145,7 @@ public void GetAll_Should_Return_Failure_When_No_Entities_Exist() public void GetAll_Should_Return_Success_When_Entities_Exist() { // Arrange - var entities = new List - { - new() { Name = "Entity1" }, - new() { Name = "Entity2" } - }; + var entities = _entityFaker.Generate(2); _ = _repository.GetAll().Returns(entities); @@ -157,13 +167,12 @@ public void GetAll_Should_Return_Success_When_Entities_Exist() public async Task GetByIdAsync_Should_Return_Success_When_Entity_Exists() { // Arrange - var id = Guid.Parse("42ab20e5-0742-4d32-a76e-7c45fd74dac3"); - var entity = new TestEntity { Name = "Test" }; + var entity = _entityFaker.Generate(); _ = _repository.GetByIdAsync(Arg.Any(), Arg.Any()).Returns(entity); // Act - var result = await _appService.GetByIdAsync(id, TestContext.CancellationToken); + var result = await _appService.GetByIdAsync(entity.Id, TestContext.CancellationToken); // Assert _ = _repository.Received(1).GetByIdAsync(Arg.Any(), Arg.Any()); @@ -180,7 +189,8 @@ public async Task GetByIdAsync_Should_Return_Success_When_Entity_Exists() public async Task GetByIdAsync_Should_Return_Failure_When_Entity_Does_Not_Exist() { // Arrange - var id = Guid.Parse("42ab20e5-0742-4d32-a76e-7c45fd74dac3"); + var id = Guid.NewGuid(); + _ = _repository.GetByIdAsync(Arg.Any(), Arg.Any()) .Returns(Task.FromResult(null)); @@ -202,7 +212,7 @@ public async Task GetByIdAsync_Should_Return_Failure_When_Entity_Does_Not_Exist( public async Task RemoveAsync_Should_Return_Success_When_Entity_Exists() { // Arrange - var entity = new TestEntityViewModel { Name = "Test" }; + var entity = _entityViewModelFaker.Generate(); _repository.Remove(Arg.Any()); _ = _unitOfWork.SaveChangesAsync(Arg.Any()).Returns(Task.CompletedTask); @@ -239,7 +249,7 @@ public async Task RemoveAsync_Should_Return_Failure_When_ViewModel_Is_Null() public async Task UpdateAsync_Should_Return_Success_When_ViewModel_Is_Valid() { // Arrange - var viewModel = new TestEntityViewModel { Name = "Updated" }; + var viewModel = _entityViewModelFaker.Generate(); _ = _repository.Update(Arg.Any()).Returns(viewModel.Adapt()); _ = _unitOfWork.SaveChangesAsync(Arg.Any()).Returns(Task.CompletedTask); @@ -274,12 +284,18 @@ public async Task UpdateAsync_Should_Return_Failure_When_ViewModel_Is_Null() } } -public class TestEntity +public sealed class TestEntity { + public Guid Id { get; set; } public string Name { get; set; } + public DateTime CreatedAt { get; set; } + public DateTime UpdatedAt { get; set; } } -public class TestEntityViewModel +public sealed class TestEntityViewModel { + public Guid Id { get; set; } public string Name { get; set; } + public DateTime CreatedAt { get; set; } + public DateTime UpdatedAt { get; set; } } diff --git a/InvoiceReminder.Application.UnitTests/AppServices/EmailAuthTokenAppServiceTests.cs b/InvoiceReminder.Application.UnitTests/AppServices/EmailAuthTokenAppServiceTests.cs index 209835a..bec2386 100644 --- a/InvoiceReminder.Application.UnitTests/AppServices/EmailAuthTokenAppServiceTests.cs +++ b/InvoiceReminder.Application.UnitTests/AppServices/EmailAuthTokenAppServiceTests.cs @@ -1,3 +1,4 @@ +using Bogus; using InvoiceReminder.Application.AppServices; using InvoiceReminder.Application.Interfaces; using InvoiceReminder.Data.Interfaces; @@ -12,6 +13,7 @@ public sealed class EmailAuthTokenAppServiceTests { private readonly IEmailAuthTokenRepository _repository; private readonly IUnitOfWork _unitOfWork; + private readonly Faker _faker; public TestContext TestContext { get; set; } @@ -19,6 +21,21 @@ public EmailAuthTokenAppServiceTests() { _repository = Substitute.For(); _unitOfWork = Substitute.For(); + _faker = new Faker(); + } + + private static Faker CreateEmailAuthTokenFaker() + { + return new Faker() + .RuleFor(e => e.Id, faker => faker.Random.Guid()) + .RuleFor(e => e.UserId, faker => faker.Random.Guid()) + .RuleFor(e => e.AccessToken, faker => faker.Random.AlphaNumeric(128)) + .RuleFor(e => e.RefreshToken, faker => faker.Random.AlphaNumeric(128)) + .RuleFor(e => e.TokenProvider, faker => faker.PickRandom("Google", "Microsoft", "GitHub")) + .RuleFor(e => e.NonceValue, faker => faker.Random.Hash()) + .RuleFor(e => e.AccessTokenExpiry, faker => faker.Date.Future().ToUniversalTime()) + .RuleFor(e => e.CreatedAt, faker => faker.Date.Past().ToUniversalTime()) + .RuleFor(e => e.UpdatedAt, faker => faker.Date.Recent().ToUniversalTime()); } [TestMethod] @@ -41,19 +58,12 @@ public async Task GetByUserIdAsync_WhenTokenExists_ShouldReturnSuccess_WithResul { // Arrange var appService = new EmailAuthTokenAppService(_repository, _unitOfWork); - var userId = Guid.NewGuid(); + var userId = _faker.Random.Guid(); var tokenProvider = "Google"; - var emailAuthToken = new EmailAuthToken - { - Id = Guid.NewGuid(), - UserId = userId, - AccessToken = "access_token", - RefreshToken = "refresh_token", - TokenProvider = "Google", - AccessTokenExpiry = DateTime.UtcNow.AddHours(1), - CreatedAt = DateTime.UtcNow, - UpdatedAt = DateTime.UtcNow - }; + var emailAuthToken = CreateEmailAuthTokenFaker() + .RuleFor(e => e.UserId, userId) + .RuleFor(e => e.TokenProvider, tokenProvider) + .Generate(); _ = _repository.GetByUserIdAsync(Arg.Any(), Arg.Any(), Arg.Any()) .Returns(emailAuthToken); @@ -78,7 +88,7 @@ public async Task GetByUserIdAsync_WhenTokenDoesNotExist_ShouldReturnFailure_Wit { // Arrange var appService = new EmailAuthTokenAppService(_repository, _unitOfWork); - var userId = Guid.NewGuid(); + var userId = _faker.Random.Guid(); var tokenProvider = "Google"; _ = _repository.GetByUserIdAsync(Arg.Any(), Arg.Any(), Arg.Any()) diff --git a/InvoiceReminder.Application.UnitTests/AppServices/InvoiceAppServiceTests.cs b/InvoiceReminder.Application.UnitTests/AppServices/InvoiceAppServiceTests.cs index 817b712..de971ac 100644 --- a/InvoiceReminder.Application.UnitTests/AppServices/InvoiceAppServiceTests.cs +++ b/InvoiceReminder.Application.UnitTests/AppServices/InvoiceAppServiceTests.cs @@ -1,3 +1,4 @@ +using Bogus; using InvoiceReminder.Application.AppServices; using InvoiceReminder.Application.Interfaces; using InvoiceReminder.Application.ViewModels; @@ -13,6 +14,7 @@ public sealed class InvoiceAppServiceTests { private readonly IInvoiceRepository _repository; private readonly IUnitOfWork _unitOfWork; + private readonly Faker _faker; public TestContext TestContext { get; set; } @@ -20,6 +22,29 @@ public InvoiceAppServiceTests() { _repository = Substitute.For(); _unitOfWork = Substitute.For(); + _faker = new Faker(); + } + + private static Faker CreateInvoiceFaker() + { + return new Faker() + .RuleFor(i => i.Id, faker => faker.Random.Guid()) + .RuleFor(i => i.UserId, faker => faker.Random.Guid()) + .RuleFor(i => i.Bank, faker => faker.PickRandom( + "Banco do Brasil", + "Bradesco", + "Itaú", + "Caixa Econômica Federal", + "Santander", + "Safra", + "Citibank", + "BTG Pactual")) + .RuleFor(i => i.Beneficiary, faker => faker.Person.FullName) + .RuleFor(i => i.Amount, faker => faker.Finance.Amount(10, 10000)) + .RuleFor(i => i.Barcode, faker => faker.Random.AlphaNumeric(44)) + .RuleFor(i => i.DueDate, faker => faker.Date.Future().ToUniversalTime()) + .RuleFor(i => i.CreatedAt, faker => faker.Date.Past().ToUniversalTime()) + .RuleFor(i => i.UpdatedAt, faker => faker.Date.Recent().ToUniversalTime()); } [TestMethod] @@ -42,16 +67,10 @@ public async Task GetByBarcodeAsync_WhenInvoiceExists_ShouldReturnSuccess_WithRe { // Arrange var appService = new InvoiceAppService(_repository, _unitOfWork); - var barcode = "12345678901234567890"; - var invoice = new Invoice - { - Id = Guid.NewGuid(), - Barcode = barcode, - Amount = 100.00m, - DueDate = DateTime.UtcNow.AddDays(30), - CreatedAt = DateTime.UtcNow, - UpdatedAt = DateTime.UtcNow - }; + var barcode = _faker.Random.AlphaNumeric(44); + var invoice = CreateInvoiceFaker() + .RuleFor(i => i.Barcode, barcode) + .Generate(); _ = _repository.GetByBarcodeAsync(Arg.Any(), Arg.Any()).Returns(invoice); @@ -76,7 +95,7 @@ public async Task GetByBarcodeAsync_WhenInvoiceDoesNotExist_ShouldReturnFailure_ { // Arrange var appService = new InvoiceAppService(_repository, _unitOfWork); - var barcode = "12345678901234567890"; + var barcode = _faker.Random.AlphaNumeric(44); _ = _repository.GetByBarcodeAsync(Arg.Any(), Arg.Any()).Returns((Invoice)null); diff --git a/InvoiceReminder.Application.UnitTests/AppServices/JobScheduleAppServiceTests.cs b/InvoiceReminder.Application.UnitTests/AppServices/JobScheduleAppServiceTests.cs index 68c9231..4c5a3cf 100644 --- a/InvoiceReminder.Application.UnitTests/AppServices/JobScheduleAppServiceTests.cs +++ b/InvoiceReminder.Application.UnitTests/AppServices/JobScheduleAppServiceTests.cs @@ -1,3 +1,4 @@ +using Bogus; using InvoiceReminder.Application.AppServices; using InvoiceReminder.Application.Interfaces; using InvoiceReminder.Application.ViewModels; @@ -17,6 +18,8 @@ public sealed class JobScheduleAppServiceTests private readonly IUnitOfWork _unitOfWork; private readonly IJobFactory _jobFactory; private readonly ISchedulerFactory _schedulerFactory; + private readonly Faker _faker; + private readonly string[] _validCronExpressions; public TestContext TestContext { get; set; } @@ -26,6 +29,36 @@ public JobScheduleAppServiceTests() _unitOfWork = Substitute.For(); _jobFactory = Substitute.For(); _schedulerFactory = Substitute.For(); + _faker = new Faker(); + _validCronExpressions = [ + "0 0/5 * * * ?",// Every 5 minutes + "0 0 * * * ?", // Every hour + "0 0 0 * * ?", // Every day at midnight + "0 0 0 ? * MON",// Every Monday + "0 0 0 1 * ?", // First day of month + "0 0 0 1 1 ?", // Every January 1st + "0 0 12 * * ?" // Every noon + ]; + } + + private Faker CreateJobScheduleFaker() + { + return new Faker() + .RuleFor(j => j.Id, faker => faker.Random.Guid()) + .RuleFor(j => j.UserId, faker => faker.Random.Guid()) + .RuleFor(j => j.CronExpression, faker => faker.PickRandom(_validCronExpressions)) + .RuleFor(j => j.CreatedAt, faker => faker.Date.Past(refDate: DateTime.UtcNow).ToUniversalTime()) + .RuleFor(j => j.UpdatedAt, (faker, j) => faker.Date.Between(j.CreatedAt, DateTime.UtcNow).ToUniversalTime()); + } + + private Faker CreateJobScheduleViewModelFaker() + { + return new Faker() + .RuleFor(j => j.Id, faker => faker.Random.Guid()) + .RuleFor(j => j.UserId, faker => faker.Random.Guid()) + .RuleFor(j => j.CronExpression, faker => faker.PickRandom(_validCronExpressions)) + .RuleFor(j => j.CreatedAt, faker => faker.Date.Past(refDate: DateTime.UtcNow).ToUniversalTime()) + .RuleFor(j => j.UpdatedAt, (faker, j) => faker.Date.Between(j.CreatedAt, DateTime.UtcNow).ToUniversalTime()); } [TestMethod] @@ -70,14 +103,7 @@ public async Task AddNewJobAsync_ShouldReturnSuccess_WhenViewModelIsValid() { // Arrange var appService = new JobScheduleAppService(_repository, _schedulerFactory, _jobFactory, _unitOfWork); - var viewModel = new JobScheduleViewModel - { - Id = Guid.NewGuid(), - UserId = Guid.NewGuid(), - CronExpression = "0 0/5 * * * ?", - CreatedAt = DateTime.UtcNow, - UpdatedAt = DateTime.UtcNow - }; + var viewModel = CreateJobScheduleViewModelFaker().Generate(); // Act var result = await appService.AddNewJobAsync(viewModel, TestContext.CancellationToken); @@ -103,17 +129,11 @@ public async Task GetByUserIdAsync_ShouldReturnSuccess_WhenUserHasJobSchedules() { // Arrange var appService = new JobScheduleAppService(_repository, _schedulerFactory, _jobFactory, _unitOfWork); - var userId = Guid.NewGuid(); - var jobSchedules = new List - { - new() { - Id = Guid.NewGuid(), - UserId = userId, - CronExpression = "0 0/5 * * * ?", - CreatedAt = DateTime.UtcNow, - UpdatedAt = DateTime.UtcNow - } - }; + var userId = _faker.Random.Guid(); + var jobSchedules = CreateJobScheduleFaker() + .RuleFor(j => j.UserId, userId) + .Generate(3) + .ToList(); _ = _repository.GetByUserIdAsync(Arg.Any(), Arg.Any()).Returns(jobSchedules); @@ -138,7 +158,7 @@ public async Task GetByUserIdAsync_ShouldReturnFailure_WhenUserHasNoJobSchedules { // Arrange var appService = new JobScheduleAppService(_repository, _schedulerFactory, _jobFactory, _unitOfWork); - var userId = Guid.NewGuid(); + var userId = _faker.Random.Guid(); _ = _repository.GetByUserIdAsync(Arg.Any(), Arg.Any()).Returns([]); diff --git a/InvoiceReminder.Application.UnitTests/AppServices/ScanEmailDefinitionAppServiceTests.cs b/InvoiceReminder.Application.UnitTests/AppServices/ScanEmailDefinitionAppServiceTests.cs index 5714ede..b391a19 100644 --- a/InvoiceReminder.Application.UnitTests/AppServices/ScanEmailDefinitionAppServiceTests.cs +++ b/InvoiceReminder.Application.UnitTests/AppServices/ScanEmailDefinitionAppServiceTests.cs @@ -1,8 +1,10 @@ +using Bogus; using InvoiceReminder.Application.AppServices; using InvoiceReminder.Application.Interfaces; using InvoiceReminder.Application.ViewModels; using InvoiceReminder.Data.Interfaces; using InvoiceReminder.Domain.Entities; +using InvoiceReminder.Domain.Enums; using Mapster; using NSubstitute; using Shouldly; @@ -14,6 +16,7 @@ public sealed class ScanEmailDefinitionAppServiceTests { private readonly IScanEmailDefinitionRepository _repository; private readonly IUnitOfWork _unitOfWork; + private readonly Faker _faker; public TestContext TestContext { get; set; } @@ -21,6 +24,21 @@ public ScanEmailDefinitionAppServiceTests() { _repository = Substitute.For(); _unitOfWork = Substitute.For(); + _faker = new Faker(); + } + + private static Faker CreateScanEmailDefinitionFaker() + { + return new Faker() + .RuleFor(s => s.Id, faker => faker.Random.Guid()) + .RuleFor(s => s.UserId, faker => faker.Random.Guid()) + .RuleFor(s => s.InvoiceType, faker => faker.PickRandom()) + .RuleFor(s => s.Beneficiary, faker => faker.Person.FullName) + .RuleFor(s => s.Description, faker => faker.Lorem.Sentence()) + .RuleFor(s => s.SenderEmailAddress, faker => faker.Internet.Email()) + .RuleFor(s => s.AttachmentFileName, faker => faker.System.FileName("pdf")) + .RuleFor(s => s.CreatedAt, faker => faker.Date.Past().ToUniversalTime()) + .RuleFor(s => s.UpdatedAt, faker => faker.Date.Recent().ToUniversalTime()); } [TestMethod] @@ -43,19 +61,12 @@ public async Task GetBySenderBeneficiaryAsync_WhenSenderBeneficiaryExists_Should { // Arrange var appService = new ScanEmailDefinitionAppService(_repository, _unitOfWork); - var beneficiary = "Test Beneficiary"; - var userId = Guid.NewGuid(); - var scanEmailDefinition = new ScanEmailDefinition - { - Id = Guid.NewGuid(), - UserId = userId, - AttachmentFileName = "test.pdf", - Beneficiary = beneficiary, - Description = "Test Description", - SenderEmailAddress = "test@mail.com", - CreatedAt = DateTime.UtcNow, - UpdatedAt = DateTime.UtcNow - }; + var userId = _faker.Random.Guid(); + var beneficiary = _faker.Person.FullName; + var scanEmailDefinition = CreateScanEmailDefinitionFaker() + .RuleFor(s => s.UserId, userId) + .RuleFor(s => s.Beneficiary, beneficiary) + .Generate(); _ = _repository.GetBySenderBeneficiaryAsync(Arg.Any(), Arg.Any(), Arg.Any()) .Returns(scanEmailDefinition); @@ -82,8 +93,8 @@ public async Task GetBySenderBeneficiaryAsync_WhenSenderBeneficiaryNotExists_Sho { // Arrange var appService = new ScanEmailDefinitionAppService(_repository, _unitOfWork); - var beneficiary = "Test Beneficiary"; - var userId = Guid.NewGuid(); + var beneficiary = _faker.Person.FullName; + var userId = _faker.Random.Guid(); _ = _repository.GetBySenderBeneficiaryAsync(Arg.Any(), Arg.Any(), Arg.Any()) .Returns((ScanEmailDefinition)null); @@ -109,25 +120,18 @@ public async Task GetBySenderEmailAddressAsync_WhenSenderEmailAddressExists_Shou { // Arrange var appService = new ScanEmailDefinitionAppService(_repository, _unitOfWork); - var beneficiary = "test@email.com"; - var userId = Guid.NewGuid(); - var scanEmailDefinition = new ScanEmailDefinition - { - Id = Guid.NewGuid(), - UserId = userId, - AttachmentFileName = "test.pdf", - Beneficiary = beneficiary, - Description = "Test Description", - SenderEmailAddress = "test@mail.com", - CreatedAt = DateTime.UtcNow, - UpdatedAt = DateTime.UtcNow - }; + var userId = _faker.Random.Guid(); + var senderEmail = _faker.Internet.Email(); + var scanEmailDefinition = CreateScanEmailDefinitionFaker() + .RuleFor(s => s.UserId, userId) + .RuleFor(s => s.SenderEmailAddress, senderEmail) + .Generate(); _ = _repository.GetBySenderEmailAddressAsync(Arg.Any(), Arg.Any(), Arg.Any()) .Returns(scanEmailDefinition); // Act - var result = await appService.GetBySenderEmailAddressAsync(beneficiary, userId, TestContext.CancellationToken); + var result = await appService.GetBySenderEmailAddressAsync(senderEmail, userId, TestContext.CancellationToken); // Assert _ = _repository.Received(1) @@ -148,14 +152,14 @@ public async Task GetBySenderEmailAddressAsync_WhenSenderEmailAddressNotExists_S { // Arrange var appService = new ScanEmailDefinitionAppService(_repository, _unitOfWork); - var beneficiary = "not_existing@email.com"; - var userId = Guid.NewGuid(); + var senderEmail = _faker.Internet.Email(); + var userId = _faker.Random.Guid(); _ = _repository.GetBySenderEmailAddressAsync(Arg.Any(), Arg.Any(), Arg.Any()) .Returns((ScanEmailDefinition)null); // Act - var result = await appService.GetBySenderEmailAddressAsync(beneficiary, userId, TestContext.CancellationToken); + var result = await appService.GetBySenderEmailAddressAsync(senderEmail, userId, TestContext.CancellationToken); // Assert _ = _repository.Received(1) @@ -175,21 +179,11 @@ public async Task GetByUserIdAsync_WhenUserIdExists_ShouldReturnSuccess_WithResu { // Arrange var appService = new ScanEmailDefinitionAppService(_repository, _unitOfWork); - var userId = Guid.NewGuid(); - var scanEmailDefinitions = new List - { - new() - { - Id = Guid.NewGuid(), - UserId = userId, - AttachmentFileName = "test.pdf", - Beneficiary = "Test Beneficiary", - Description = "Test Description", - SenderEmailAddress = "test@mail.com", - CreatedAt = DateTime.UtcNow, - UpdatedAt = DateTime.UtcNow - } - }; + var userId = _faker.Random.Guid(); + var scanEmailDefinitions = CreateScanEmailDefinitionFaker() + .RuleFor(s => s.UserId, userId) + .Generate(3) + .ToList(); _ = _repository.GetByUserIdAsync(Arg.Any(), Arg.Any()).Returns(scanEmailDefinitions); @@ -214,7 +208,7 @@ public async Task GetByUserIdAsync_WhenUserIdNotExists_ShouldReturnFailure_WithR { // Arrange var appService = new ScanEmailDefinitionAppService(_repository, _unitOfWork); - var userId = Guid.NewGuid(); + var userId = _faker.Random.Guid(); _ = _repository.GetByUserIdAsync(Arg.Any(), Arg.Any()).Returns([]); // Act diff --git a/InvoiceReminder.Application.UnitTests/AppServices/UserAppServiceTests.cs b/InvoiceReminder.Application.UnitTests/AppServices/UserAppServiceTests.cs index 3f742af..e2e420c 100644 --- a/InvoiceReminder.Application.UnitTests/AppServices/UserAppServiceTests.cs +++ b/InvoiceReminder.Application.UnitTests/AppServices/UserAppServiceTests.cs @@ -1,3 +1,4 @@ +using Bogus; using InvoiceReminder.Application.AppServices; using InvoiceReminder.Application.Interfaces; using InvoiceReminder.Application.ViewModels; @@ -14,6 +15,7 @@ public sealed class UserAppServiceTests { private readonly IUserRepository _repository; private readonly IUnitOfWork _unitOfWork; + private readonly Faker _faker; public TestContext TestContext { get; set; } @@ -21,6 +23,23 @@ public UserAppServiceTests() { _repository = Substitute.For(); _unitOfWork = Substitute.For(); + _faker = new Faker(); + } + + private static Faker CreateFaker() + { + return new Faker() + .RuleFor(u => u.Id, faker => faker.Random.Guid()) + .RuleFor(u => u.Name, faker => faker.Person.FullName) + .RuleFor(u => u.Email, faker => faker.Internet.Email()) + .RuleFor(u => u.Password, faker => faker.Internet.Password()) + .RuleFor(u => u.TelegramChatId, faker => faker.Random.Long(100000000, long.MaxValue)) + .RuleFor(u => u.CreatedAt, faker => faker.Date.Past().ToUniversalTime()) + .RuleFor(u => u.UpdatedAt, faker => faker.Date.Recent().ToUniversalTime()) + .RuleFor(u => u.Invoices, _ => []) + .RuleFor(u => u.JobSchedules, _ => []) + .RuleFor(u => u.EmailAuthTokens, _ => []) + .RuleFor(u => u.ScanEmailDefinitions, _ => []); } [TestMethod] @@ -43,18 +62,8 @@ public async Task GetByEmaildAsync_WhenUserEmailExists_ShouldReturnSuccess_Whith { // Arrange var appService = new UserAppService(_repository, _unitOfWork); - var email = "user@test.com"; - var user = new User - { - Id = Guid.NewGuid(), - Email = email, - Name = "Test User", - Password = "password", - JobSchedules = [], - ScanEmailDefinitions = [], - CreatedAt = DateTime.UtcNow, - UpdatedAt = DateTime.UtcNow - }; + var user = CreateFaker().Generate(); + var email = user.Email; _ = _repository.GetByEmailAsync(Arg.Any(), Arg.Any()).Returns(user); @@ -79,7 +88,7 @@ public async Task GetByEmaildAsync_WhenUserEmailNotExists_ShouldReturnFailure_Wh { // Arrange var appService = new UserAppService(_repository, _unitOfWork); - var email = "not_existing@test.com"; + var email = _faker.Internet.Email(); _ = _repository.GetByEmailAsync(Arg.Any(), Arg.Any()).Returns((User)null); diff --git a/InvoiceReminder.Application.UnitTests/InvoiceReminder.Application.UnitTests.csproj b/InvoiceReminder.Application.UnitTests/InvoiceReminder.Application.UnitTests.csproj index fa4d5f2..a65a181 100644 --- a/InvoiceReminder.Application.UnitTests/InvoiceReminder.Application.UnitTests.csproj +++ b/InvoiceReminder.Application.UnitTests/InvoiceReminder.Application.UnitTests.csproj @@ -16,6 +16,7 @@ + all diff --git a/InvoiceReminder.Data/InvoiceReminder.Data.csproj b/InvoiceReminder.Data/InvoiceReminder.Data.csproj index 05981c8..b35eebb 100644 --- a/InvoiceReminder.Data/InvoiceReminder.Data.csproj +++ b/InvoiceReminder.Data/InvoiceReminder.Data.csproj @@ -6,7 +6,8 @@ - + + diff --git a/InvoiceReminder.Data/Repository/BaseRepository.cs b/InvoiceReminder.Data/Repository/BaseRepository.cs index bd3c10c..236d3da 100644 --- a/InvoiceReminder.Data/Repository/BaseRepository.cs +++ b/InvoiceReminder.Data/Repository/BaseRepository.cs @@ -1,3 +1,4 @@ +using EFCore.BulkExtensions; using InvoiceReminder.Data.Interfaces; using Microsoft.EntityFrameworkCore; using System.Linq.Expressions; @@ -33,7 +34,7 @@ public virtual async Task BulkInsertAsync(ICollection entities, Ca entity.GetType().GetProperty("UpdatedAt")?.SetValue(entity, DateTime.UtcNow); } - await _dbContext.BulkInsertAsync(entities, cancellationToken); + await _dbContext.BulkInsertAsync(entities, cancellationToken: cancellationToken); return entities.Count; } diff --git a/InvoiceReminder.DomainEntities.UnitTests/Extensions/EntityExtensionsTests.cs b/InvoiceReminder.DomainEntities.UnitTests/Extensions/EntityExtensionsTests.cs index 8661c98..740e9e9 100644 --- a/InvoiceReminder.DomainEntities.UnitTests/Extensions/EntityExtensionsTests.cs +++ b/InvoiceReminder.DomainEntities.UnitTests/Extensions/EntityExtensionsTests.cs @@ -1,3 +1,4 @@ +using Bogus; using InvoiceReminder.Domain.Extensions; using Shouldly; @@ -12,6 +13,10 @@ private sealed class TestEntity public string Name { get; init; } = string.Empty; } + private readonly Faker _entityFaker = new Faker() + .RuleFor(e => e.Id, faker => faker.Random.Guid()) + .RuleFor(e => e.Name, faker => faker.Person.FullName); + [TestMethod] public void AddIfNotExists_WhenEntityIsNull_ShouldNotAddToCollection() { @@ -30,8 +35,7 @@ public void AddIfNotExists_WhenEntityIsNull_ShouldNotAddToCollection() public void AddIfNotExists_WhenCollectionIsEmpty_ShouldAddEntity() { // Arrange - var entityId = Guid.NewGuid(); - var entity = new TestEntity { Id = entityId, Name = "Test Entity" }; + var entity = _entityFaker.Generate(); var collection = new List(); // Act @@ -42,7 +46,7 @@ public void AddIfNotExists_WhenCollectionIsEmpty_ShouldAddEntity() { _ = collection.ShouldHaveSingleItem(); collection[0].ShouldBeEquivalentTo(entity); - collection[0].Id.ShouldBe(entityId); + collection[0].Id.ShouldBe(entity.Id); }); } @@ -50,9 +54,10 @@ public void AddIfNotExists_WhenCollectionIsEmpty_ShouldAddEntity() public void AddIfNotExists_WhenEntityAlreadyExists_ShouldNotAddDuplicate() { // Arrange - var entityId = Guid.NewGuid(); - var existingEntity = new TestEntity { Id = entityId, Name = "Existing Entity" }; - var newEntity = new TestEntity { Id = entityId, Name = "New Entity" }; + var faker = new Faker(); + var entityId = faker.Random.Guid(); + var existingEntity = new TestEntity { Id = entityId, Name = faker.Person.FullName }; + var newEntity = new TestEntity { Id = entityId, Name = faker.Person.FullName }; var collection = new List { existingEntity }; // Act @@ -63,7 +68,7 @@ public void AddIfNotExists_WhenEntityAlreadyExists_ShouldNotAddDuplicate() { _ = collection.ShouldHaveSingleItem(); collection[0].ShouldBeEquivalentTo(existingEntity); - collection[0].Name.ShouldBe("Existing Entity"); + collection[0].Id.ShouldBe(entityId); }); } @@ -71,10 +76,8 @@ public void AddIfNotExists_WhenEntityAlreadyExists_ShouldNotAddDuplicate() public void AddIfNotExists_WhenEntityDoesNotExist_ShouldAddToNonEmptyCollection() { // Arrange - var existingId = Guid.NewGuid(); - var newId = Guid.NewGuid(); - var existingEntity = new TestEntity { Id = existingId, Name = "Existing Entity" }; - var newEntity = new TestEntity { Id = newId, Name = "New Entity" }; + var existingEntity = _entityFaker.Generate(); + var newEntity = _entityFaker.Generate(); var collection = new List { existingEntity }; // Act @@ -93,11 +96,12 @@ public void AddIfNotExists_WhenEntityDoesNotExist_ShouldAddToNonEmptyCollection( public void AddIfNotExists_WhenMultipleEntitiesExist_ShouldFindExistingByIdOnly() { // Arrange - var targetId = Guid.NewGuid(); - var entity1 = new TestEntity { Id = Guid.NewGuid(), Name = "Entity 1" }; - var entity2 = new TestEntity { Id = targetId, Name = "Entity 2" }; - var entity3 = new TestEntity { Id = Guid.NewGuid(), Name = "Entity 3" }; - var duplicateEntity = new TestEntity { Id = targetId, Name = "Different Name" }; + var faker = new Faker(); + var targetId = faker.Random.Guid(); + var entity1 = _entityFaker.Generate(); + var entity2 = new TestEntity { Id = targetId, Name = faker.Person.FullName }; + var entity3 = _entityFaker.Generate(); + var duplicateEntity = new TestEntity { Id = targetId, Name = faker.Person.FullName }; var collection = new List { entity1, entity2, entity3 }; // Act @@ -109,20 +113,15 @@ public void AddIfNotExists_WhenMultipleEntitiesExist_ShouldFindExistingByIdOnly( collection.Count.ShouldBe(3); collection.Count(e => e.Id == targetId).ShouldBe(1); }); - } [TestMethod] public void AddIfNotExists_WhenEntityWithNewIdAdded_ShouldIncreaseCollectionCount() { // Arrange - var collection = new List - { - new() { Id = Guid.NewGuid(), Name = "Entity 1" }, - new() { Id = Guid.NewGuid(), Name = "Entity 2" } - }; + var collection = new List { _entityFaker.Generate(), _entityFaker.Generate() }; var initialCount = collection.Count; - var newEntity = new TestEntity { Id = Guid.NewGuid(), Name = "Entity 3" }; + var newEntity = _entityFaker.Generate(); // Act newEntity.AddIfNotExists(collection); @@ -139,8 +138,7 @@ public void AddIfNotExists_WhenEntityWithNewIdAdded_ShouldIncreaseCollectionCoun public void AddIfNotExists_WhenMultipleCallsWithSameEntity_ShouldOnlyAddOnce() { // Arrange - var entityId = Guid.NewGuid(); - var entity = new TestEntity { Id = entityId, Name = "Test Entity" }; + var entity = _entityFaker.Generate(); var collection = new List(); // Act @@ -160,7 +158,8 @@ public void AddIfNotExists_WhenMultipleCallsWithSameEntity_ShouldOnlyAddOnce() public void AddIfNotExists_WhenEntityIdIsEmptyGuid_ShouldAddEntity() { // Arrange - var entity = new TestEntity { Id = Guid.Empty, Name = "Entity with Empty ID" }; + var faker = new Faker(); + var entity = new TestEntity { Id = Guid.Empty, Name = faker.Person.FullName }; var collection = new List(); // Act @@ -178,8 +177,9 @@ public void AddIfNotExists_WhenEntityIdIsEmptyGuid_ShouldAddEntity() public void AddIfNotExists_WhenEntityWithEmptyIdAlreadyExists_ShouldNotAddDuplicate() { // Arrange - var existingEntity = new TestEntity { Id = Guid.Empty, Name = "Existing Entity" }; - var newEntity = new TestEntity { Id = Guid.Empty, Name = "New Entity" }; + var faker = new Faker(); + var existingEntity = new TestEntity { Id = Guid.Empty, Name = faker.Person.FullName }; + var newEntity = new TestEntity { Id = Guid.Empty, Name = faker.Person.FullName }; var collection = new List { existingEntity }; // Act @@ -189,7 +189,7 @@ public void AddIfNotExists_WhenEntityWithEmptyIdAlreadyExists_ShouldNotAddDuplic collection.ShouldSatisfyAllConditions(() => { _ = collection.ShouldHaveSingleItem(); - collection[0].Name.ShouldBe("Existing Entity"); + collection[0].Id.ShouldBe(Guid.Empty); }); } @@ -197,18 +197,18 @@ public void AddIfNotExists_WhenEntityWithEmptyIdAlreadyExists_ShouldNotAddDuplic public void AddIfNotExists_WithHashSetCollection_ShouldWorkCorrectly() { // Arrange - var entityId = Guid.NewGuid(); - var entity = new TestEntity { Id = entityId, Name = "Test Entity" }; - var collection = new HashSet([new TestEntity { Id = Guid.NewGuid(), Name = "Existing" }]); + var newEntity = _entityFaker.Generate(); + var existingEntity = _entityFaker.Generate(); + var collection = new HashSet { existingEntity }; // Act - entity.AddIfNotExists(collection); + newEntity.AddIfNotExists(collection); // Assert collection.ShouldSatisfyAllConditions(() => { collection.Count.ShouldBe(2); - collection.ShouldContain(entity); + collection.ShouldContain(newEntity); }); } @@ -216,9 +216,11 @@ public void AddIfNotExists_WithHashSetCollection_ShouldWorkCorrectly() public void AddIfNotExists_WhenPreservingExistingEntityProperties_ShouldNotReplaceExistingEntity() { // Arrange - var entityId = Guid.NewGuid(); - var existingEntity = new TestEntity { Id = entityId, Name = "Original Name" }; - var newEntity = new TestEntity { Id = entityId, Name = "Updated Name" }; + var faker = new Faker(); + var entityId = faker.Random.Guid(); + var existingName = faker.Person.FullName; + var existingEntity = new TestEntity { Id = entityId, Name = existingName }; + var newEntity = new TestEntity { Id = entityId, Name = faker.Person.FullName }; var collection = new List { existingEntity }; // Act @@ -228,9 +230,8 @@ public void AddIfNotExists_WhenPreservingExistingEntityProperties_ShouldNotRepla collection.ShouldSatisfyAllConditions(() => { _ = collection.ShouldHaveSingleItem(); - collection[0].Name.ShouldBe("Original Name"); + collection[0].Name.ShouldBe(existingName); collection[0].ShouldBe(existingEntity); }); } } - diff --git a/InvoiceReminder.DomainEntities.UnitTests/Extensions/UserExtensionsTests.cs b/InvoiceReminder.DomainEntities.UnitTests/Extensions/UserExtensionsTests.cs index 38c707a..fd37453 100644 --- a/InvoiceReminder.DomainEntities.UnitTests/Extensions/UserExtensionsTests.cs +++ b/InvoiceReminder.DomainEntities.UnitTests/Extensions/UserExtensionsTests.cs @@ -1,4 +1,6 @@ +using Bogus; using InvoiceReminder.Domain.Entities; +using InvoiceReminder.Domain.Enums; using InvoiceReminder.Domain.Extensions; using Shouldly; @@ -7,12 +9,70 @@ namespace InvoiceReminder.DomainEntities.UnitTests.Extensions; [TestClass] public sealed class UserExtensionsTests { + private readonly Faker _invoiceFaker; + private readonly Faker _jobScheduleFaker; + private readonly Faker _emailAuthTokenFaker; + private readonly Faker _scanEmailDefinitionFaker; + + public UserExtensionsTests() + { + _invoiceFaker = new Faker() + .RuleFor(e => e.Id, faker => faker.Random.Guid()) + .RuleFor(e => e.UserId, faker => faker.Random.Guid()) + .RuleFor(e => e.Bank, faker => faker.Company.CompanyName()) + .RuleFor(e => e.Beneficiary, faker => faker.Company.CompanyName()) + .RuleFor(e => e.Amount, faker => faker.Finance.Amount(100, 10000)) + .RuleFor(e => e.Barcode, faker => faker.Random.Replace("############################################")) + .RuleFor(e => e.DueDate, faker => faker.Date.Future().ToUniversalTime()); + + _jobScheduleFaker = new Faker() + .RuleFor(e => e.Id, faker => faker.Random.Guid()) + .RuleFor(e => e.UserId, faker => faker.Random.Guid()) + .RuleFor(e => e.CronExpression, faker => $"0 {faker.Random.Int(0, 23)} * * *"); + + _emailAuthTokenFaker = new Faker() + .RuleFor(e => e.Id, faker => faker.Random.Guid()) + .RuleFor(e => e.UserId, faker => faker.Random.Guid()) + .RuleFor(e => e.AccessToken, faker => faker.Random.AlphaNumeric(256)) + .RuleFor(e => e.RefreshToken, faker => faker.Random.AlphaNumeric(256)) + .RuleFor(e => e.NonceValue, faker => faker.Random.AlphaNumeric(32)) + .RuleFor(e => e.TokenProvider, faker => faker.PickRandom("Google", "Microsoft", "Yahoo")) + .RuleFor(e => e.AccessTokenExpiry, faker => faker.Date.Future().ToUniversalTime()); + + _scanEmailDefinitionFaker = new Faker() + .RuleFor(e => e.Id, faker => faker.Random.Guid()) + .RuleFor(e => e.UserId, faker => faker.Random.Guid()) + .RuleFor(e => e.InvoiceType, faker => faker.PickRandom(InvoiceType.AccountInvoice, InvoiceType.BankInvoice)) + .RuleFor(e => e.Beneficiary, faker => faker.Company.CompanyName()) + .RuleFor(e => e.Description, faker => faker.Lorem.Sentence()) + .RuleFor(e => e.SenderEmailAddress, faker => faker.Internet.Email()) + .RuleFor(e => e.AttachmentFileName, faker => faker.System.FileName()); + } + + private static Faker CreateFaker( + ICollection invoices = default, + ICollection jobSchedules = default, + ICollection emailAuthTokens = default, + ICollection scanEmailDefinitions = default) + { + return new Faker() + .RuleFor(e => e.Id, faker => faker.Random.Guid()) + .RuleFor(e => e.Name, faker => faker.Person.FullName) + .RuleFor(e => e.Email, faker => faker.Internet.Email()) + .RuleFor(e => e.Password, faker => faker.Internet.Password()) + .RuleFor(e => e.TelegramChatId, faker => faker.Random.Long(1)) + .RuleFor(u => u.Invoices, _ => invoices ?? []) + .RuleFor(u => u.JobSchedules, _ => jobSchedules ?? []) + .RuleFor(u => u.EmailAuthTokens, _ => emailAuthTokens ?? []) + .RuleFor(u => u.ScanEmailDefinitions, _ => scanEmailDefinitions ?? []); + } + [TestMethod] public void Handle_NewUser_AddsUserToResult() { // Arrange var result = new Dictionary(); - var user = new User { Id = Guid.NewGuid(), Name = "Test User" }; + var user = CreateFaker().Generate(); var parameters = new UserParameters(); // Act @@ -39,8 +99,8 @@ public void Handle_NewUserWithInvoice_AddsInvoiceToUserAndResult() { // Arrange var result = new Dictionary(); - var user = new User { Id = Guid.NewGuid(), Name = "Test User" }; - var invoice = new Invoice { Id = Guid.NewGuid() }; + var user = CreateFaker().Generate(); + var invoice = _invoiceFaker.Generate(); var parameters = new UserParameters { Invoice = invoice }; // Act @@ -67,8 +127,8 @@ public void Handle_NewUserWithJobSchedule_AddsJobScheduleToUserAndResult() { // Arrange var result = new Dictionary(); - var user = new User { Id = Guid.NewGuid(), Name = "Test User" }; - var jobSchedule = new JobSchedule { Id = Guid.NewGuid() }; + var user = CreateFaker().Generate(); + var jobSchedule = _jobScheduleFaker.Generate(); var parameters = new UserParameters { JobSchedule = jobSchedule }; // Act @@ -95,8 +155,8 @@ public void Handle_NewUserWithEmailAuthToken_AddsEmailAuthTokenToUserAndResult() { // Arrange var result = new Dictionary(); - var user = new User { Id = Guid.NewGuid(), Name = "Test User" }; - var emailAuthToken = new EmailAuthToken { Id = Guid.NewGuid() }; + var user = CreateFaker().Generate(); + var emailAuthToken = _emailAuthTokenFaker.Generate(); var parameters = new UserParameters { EmailAuthToken = emailAuthToken }; // Act @@ -123,8 +183,8 @@ public void Handle_NewUserWithScanEmailDefinition_AddsScanEmailDefinitionToUserA { // Arrange var result = new Dictionary(); - var user = new User { Id = Guid.NewGuid(), Name = "Test User" }; - var scanEmailDefinition = new ScanEmailDefinition { Id = Guid.NewGuid() }; + var user = CreateFaker().Generate(); + var scanEmailDefinition = _scanEmailDefinitionFaker.Generate(); var parameters = new UserParameters { ScanEmailDefinition = scanEmailDefinition }; // Act @@ -149,20 +209,11 @@ public void Handle_NewUserWithScanEmailDefinition_AddsScanEmailDefinitionToUserA public void Handle_ExistingUserWithNewInvoice_AddsInvoiceToExistingUserInResult() { // Arrange - var userId = Guid.NewGuid(); - var existingInvoice = new Invoice { Id = Guid.NewGuid() }; - var existingUser = new User - { - Id = userId, - Name = "Existing User", - Invoices = [existingInvoice], - JobSchedules = [], - EmailAuthTokens = [], - ScanEmailDefinitions = [] - }; - var result = new Dictionary { { userId, existingUser } }; + var existingInvoice = _invoiceFaker.Generate(); + var existingUser = CreateFaker(invoices: [existingInvoice]).Generate(); + var result = new Dictionary { { existingUser.Id, existingUser } }; var newUserReference = existingUser; - var newInvoice = new Invoice { Id = Guid.NewGuid() }; + var newInvoice = _invoiceFaker.Generate(); var parameters = new UserParameters { Invoice = newInvoice }; // Act @@ -171,9 +222,9 @@ public void Handle_ExistingUserWithNewInvoice_AddsInvoiceToExistingUserInResult( // Assert result.ShouldSatisfyAllConditions(result => { - result.ShouldContainKey(userId); - result[userId].Invoices.ShouldContain(existingInvoice); - result[userId].Invoices.ShouldContain(newInvoice); + result.ShouldContainKey(existingUser.Id); + result[existingUser.Id].Invoices.ShouldContain(existingInvoice); + result[existingUser.Id].Invoices.ShouldContain(newInvoice); }); newUserReference.ShouldSatisfyAllConditions(newUserReference => @@ -191,20 +242,11 @@ public void Handle_ExistingUserWithNewInvoice_AddsInvoiceToExistingUserInResult( public void Handle_ExistingUserWithNewJobSchedule_AddsJobScheduleToExistingUserInResult() { // Arrange - var userId = Guid.NewGuid(); - var existingJobSchedule = new JobSchedule { Id = Guid.NewGuid() }; - var existingUser = new User - { - Id = userId, - Name = "Existing User", - Invoices = [], - JobSchedules = [existingJobSchedule], - EmailAuthTokens = [], - ScanEmailDefinitions = [] - }; - var result = new Dictionary { { userId, existingUser } }; + var existingJobSchedule = _jobScheduleFaker.Generate(); + var existingUser = CreateFaker(jobSchedules: [existingJobSchedule]).Generate(); + var result = new Dictionary { { existingUser.Id, existingUser } }; var newUserReference = existingUser; - var newJobSchedule = new JobSchedule { Id = Guid.NewGuid() }; + var newJobSchedule = _jobScheduleFaker.Generate(); var parameters = new UserParameters { JobSchedule = newJobSchedule }; // Act @@ -213,9 +255,9 @@ public void Handle_ExistingUserWithNewJobSchedule_AddsJobScheduleToExistingUserI // Assert result.ShouldSatisfyAllConditions(result => { - result.ShouldContainKey(userId); - result[userId].JobSchedules.ShouldContain(existingJobSchedule); - result[userId].JobSchedules.ShouldContain(newJobSchedule); + result.ShouldContainKey(existingUser.Id); + result[existingUser.Id].JobSchedules.ShouldContain(existingJobSchedule); + result[existingUser.Id].JobSchedules.ShouldContain(newJobSchedule); }); newUserReference.ShouldSatisfyAllConditions(newUserReference => @@ -233,20 +275,11 @@ public void Handle_ExistingUserWithNewJobSchedule_AddsJobScheduleToExistingUserI public void Handle_ExistingUserWithNewEmailAuthToken_AddsEmailAuthTokenToExistingUserInResult() { // Arrange - var userId = Guid.NewGuid(); - var existingEmailAuthToken = new EmailAuthToken { Id = Guid.NewGuid() }; - var existingUser = new User - { - Id = userId, - Name = "Existing User", - Invoices = [], - JobSchedules = [], - EmailAuthTokens = [existingEmailAuthToken], - ScanEmailDefinitions = [] - }; - var result = new Dictionary { { userId, existingUser } }; + var existingEmailAuthToken = _emailAuthTokenFaker.Generate(); + var existingUser = CreateFaker(emailAuthTokens: [existingEmailAuthToken]).Generate(); + var result = new Dictionary { { existingUser.Id, existingUser } }; var newUserReference = existingUser; - var newEmailAuthToken = new EmailAuthToken { Id = Guid.NewGuid() }; + var newEmailAuthToken = _emailAuthTokenFaker.Generate(); var parameters = new UserParameters { EmailAuthToken = newEmailAuthToken }; // Act @@ -255,9 +288,9 @@ public void Handle_ExistingUserWithNewEmailAuthToken_AddsEmailAuthTokenToExistin // Assert result.ShouldSatisfyAllConditions(result => { - result.ShouldContainKey(userId); - result[userId].EmailAuthTokens.ShouldContain(existingEmailAuthToken); - result[userId].EmailAuthTokens.ShouldContain(newEmailAuthToken); + result.ShouldContainKey(existingUser.Id); + result[existingUser.Id].EmailAuthTokens.ShouldContain(existingEmailAuthToken); + result[existingUser.Id].EmailAuthTokens.ShouldContain(newEmailAuthToken); }); newUserReference.ShouldSatisfyAllConditions(newUserReference => @@ -275,20 +308,11 @@ public void Handle_ExistingUserWithNewEmailAuthToken_AddsEmailAuthTokenToExistin public void Handle_ExistingUserWithNewScanEmailDefinition_AddsScanEmailDefinitionToExistingUserInResult() { // Arrange - var userId = Guid.NewGuid(); - var existingScanEmailDefinition = new ScanEmailDefinition { Id = Guid.NewGuid() }; - var existingUser = new User - { - Id = userId, - Name = "Existing User", - Invoices = [], - JobSchedules = [], - EmailAuthTokens = [], - ScanEmailDefinitions = [existingScanEmailDefinition] - }; - var result = new Dictionary { { userId, existingUser } }; + var existingScanEmailDefinition = _scanEmailDefinitionFaker.Generate(); + var existingUser = CreateFaker(scanEmailDefinitions: [existingScanEmailDefinition]).Generate(); + var result = new Dictionary { { existingUser.Id, existingUser } }; var newUserReference = existingUser; - var newScanEmailDefinition = new ScanEmailDefinition { Id = Guid.NewGuid() }; + var newScanEmailDefinition = _scanEmailDefinitionFaker.Generate(); var parameters = new UserParameters { ScanEmailDefinition = newScanEmailDefinition }; // Act @@ -297,9 +321,9 @@ public void Handle_ExistingUserWithNewScanEmailDefinition_AddsScanEmailDefinitio // Assert result.ShouldSatisfyAllConditions(result => { - result.ShouldContainKey(userId); - result[userId].ScanEmailDefinitions.ShouldContain(existingScanEmailDefinition); - result[userId].ScanEmailDefinitions.ShouldContain(newScanEmailDefinition); + result.ShouldContainKey(existingUser.Id); + result[existingUser.Id].ScanEmailDefinitions.ShouldContain(existingScanEmailDefinition); + result[existingUser.Id].ScanEmailDefinitions.ShouldContain(newScanEmailDefinition); }); newUserReference.ShouldSatisfyAllConditions(newUserReference => @@ -317,10 +341,9 @@ public void Handle_ExistingUserWithNewScanEmailDefinition_AddsScanEmailDefinitio public void Handle_ExistingUserWithSameInvoice_DoesNotAddDuplicate() { // Arrange - var userId = Guid.NewGuid(); - var existingInvoice = new Invoice { Id = Guid.NewGuid() }; - var existingUser = new User { Id = userId, Name = "Existing User", Invoices = [existingInvoice] }; - var result = new Dictionary { { userId, existingUser } }; + var existingInvoice = _invoiceFaker.Generate(); + var existingUser = CreateFaker(invoices: [existingInvoice]).Generate(); + var result = new Dictionary { { existingUser.Id, existingUser } }; var newUserReference = existingUser; var parameters = new UserParameters { Invoice = existingInvoice }; @@ -330,9 +353,9 @@ public void Handle_ExistingUserWithSameInvoice_DoesNotAddDuplicate() // Assert result.ShouldSatisfyAllConditions(result => { - result.ShouldContainKey(userId); - result[userId].Invoices.Count.ShouldBe(1); - result[userId].Invoices.ShouldContain(existingInvoice); + result.ShouldContainKey(existingUser.Id); + result[existingUser.Id].Invoices.Count.ShouldBe(1); + result[existingUser.Id].Invoices.ShouldContain(existingInvoice); }); newUserReference.ShouldSatisfyAllConditions(newUserReference => @@ -346,10 +369,9 @@ public void Handle_ExistingUserWithSameInvoice_DoesNotAddDuplicate() public void Handle_ExistingUserWithSameJobSchedule_DoesNotAddDuplicate() { // Arrange - var userId = Guid.NewGuid(); - var existingJobSchedule = new JobSchedule { Id = Guid.NewGuid() }; - var existingUser = new User { Id = userId, Name = "Existing User", JobSchedules = [existingJobSchedule] }; - var result = new Dictionary { { userId, existingUser } }; + var existingJobSchedule = _jobScheduleFaker.Generate(); + var existingUser = CreateFaker(jobSchedules: [existingJobSchedule]).Generate(); + var result = new Dictionary { { existingUser.Id, existingUser } }; var newUserReference = existingUser; var parameters = new UserParameters { JobSchedule = existingJobSchedule }; @@ -359,9 +381,9 @@ public void Handle_ExistingUserWithSameJobSchedule_DoesNotAddDuplicate() // Assert result.ShouldSatisfyAllConditions(result => { - result.ShouldContainKey(userId); - result[userId].JobSchedules.Count.ShouldBe(1); - result[userId].JobSchedules.ShouldContain(existingJobSchedule); + result.ShouldContainKey(existingUser.Id); + result[existingUser.Id].JobSchedules.Count.ShouldBe(1); + result[existingUser.Id].JobSchedules.ShouldContain(existingJobSchedule); }); newUserReference.ShouldSatisfyAllConditions(newUserReference => @@ -375,10 +397,9 @@ public void Handle_ExistingUserWithSameJobSchedule_DoesNotAddDuplicate() public void Handle_ExistingUserWithSameEmailAuthToken_DoesNotAddDuplicate() { // Arrange - var userId = Guid.NewGuid(); - var existingEmailAuthToken = new EmailAuthToken { Id = Guid.NewGuid() }; - var existingUser = new User { Id = userId, Name = "Existing User", EmailAuthTokens = [existingEmailAuthToken] }; - var result = new Dictionary { { userId, existingUser } }; + var existingEmailAuthToken = _emailAuthTokenFaker.Generate(); + var existingUser = CreateFaker(emailAuthTokens: [existingEmailAuthToken]).Generate(); + var result = new Dictionary { { existingUser.Id, existingUser } }; var newUserReference = existingUser; var parameters = new UserParameters { EmailAuthToken = existingEmailAuthToken }; @@ -388,9 +409,9 @@ public void Handle_ExistingUserWithSameEmailAuthToken_DoesNotAddDuplicate() // Assert result.ShouldSatisfyAllConditions(result => { - result.ShouldContainKey(userId); - result[userId].EmailAuthTokens.Count.ShouldBe(1); - result[userId].EmailAuthTokens.ShouldContain(existingEmailAuthToken); + result.ShouldContainKey(existingUser.Id); + result[existingUser.Id].EmailAuthTokens.Count.ShouldBe(1); + result[existingUser.Id].EmailAuthTokens.ShouldContain(existingEmailAuthToken); }); newUserReference.ShouldSatisfyAllConditions(newUserReference => @@ -404,15 +425,9 @@ public void Handle_ExistingUserWithSameEmailAuthToken_DoesNotAddDuplicate() public void Handle_ExistingUserWithSameScanEmailDefinition_DoesNotAddDuplicate() { // Arrange - var userId = Guid.NewGuid(); - var existingScanEmailDefinition = new ScanEmailDefinition { Id = Guid.NewGuid() }; - var existingUser = new User - { - Id = userId, - Name = "Existing User", - ScanEmailDefinitions = [existingScanEmailDefinition] - }; - var result = new Dictionary { { userId, existingUser } }; + var existingScanEmailDefinition = _scanEmailDefinitionFaker.Generate(); + var existingUser = CreateFaker(scanEmailDefinitions: [existingScanEmailDefinition]).Generate(); + var result = new Dictionary { { existingUser.Id, existingUser } }; var newUserReference = existingUser; var parameters = new UserParameters { ScanEmailDefinition = existingScanEmailDefinition }; @@ -422,9 +437,9 @@ public void Handle_ExistingUserWithSameScanEmailDefinition_DoesNotAddDuplicate() // Assert result.ShouldSatisfyAllConditions(result => { - result.ShouldContainKey(userId); - result[userId].ScanEmailDefinitions.Count.ShouldBe(1); - result[userId].ScanEmailDefinitions.ShouldContain(existingScanEmailDefinition); + result.ShouldContainKey(existingUser.Id); + result[existingUser.Id].ScanEmailDefinitions.Count.ShouldBe(1); + result[existingUser.Id].ScanEmailDefinitions.ShouldContain(existingScanEmailDefinition); }); newUserReference.ShouldSatisfyAllConditions(newUserReference => @@ -439,15 +454,11 @@ public void Handle_NewUserWithEmptyCollections_InitializesCollections() { // Arrange var result = new Dictionary(); - var user = new User - { - Id = Guid.NewGuid(), - Name = "Test User" - }; - var invoice = new Invoice { Id = Guid.NewGuid() }; - var jobSchedule = new JobSchedule { Id = Guid.NewGuid() }; - var scanEmailDefinition = new ScanEmailDefinition { Id = Guid.NewGuid() }; - var emailAuthToken = new EmailAuthToken { Id = Guid.NewGuid() }; + var user = CreateFaker().Generate(); + var invoice = _invoiceFaker.Generate(); + var jobSchedule = _jobScheduleFaker.Generate(); + var scanEmailDefinition = _scanEmailDefinitionFaker.Generate(); + var emailAuthToken = _emailAuthTokenFaker.Generate(); var parameters = new UserParameters { Invoice = invoice, @@ -486,22 +497,13 @@ public void Handle_NewUserWithEmptyCollections_InitializesCollections() public void Handle_ExistingUserWithNullCollections_AddsNewItems() { // Arrange - var userId = Guid.NewGuid(); - var existingUser = new User - { - Id = userId, - Name = "Existing User", - Invoices = [], - JobSchedules = [], - EmailAuthTokens = [], - ScanEmailDefinitions = [] - }; - var result = new Dictionary { { userId, existingUser } }; + var existingUser = CreateFaker().Generate(); + var result = new Dictionary { { existingUser.Id, existingUser } }; var newUserReference = existingUser; - var invoice = new Invoice { Id = Guid.NewGuid() }; - var jobSchedule = new JobSchedule { Id = Guid.NewGuid() }; - var emailAuthToken = new EmailAuthToken { Id = Guid.NewGuid() }; - var scanEmailDefinition = new ScanEmailDefinition { Id = Guid.NewGuid() }; + var invoice = _invoiceFaker.Generate(); + var jobSchedule = _jobScheduleFaker.Generate(); + var scanEmailDefinition = _scanEmailDefinitionFaker.Generate(); + var emailAuthToken = _emailAuthTokenFaker.Generate(); var parameters = new UserParameters { Invoice = invoice, @@ -516,15 +518,15 @@ public void Handle_ExistingUserWithNullCollections_AddsNewItems() // Assert result.ShouldSatisfyAllConditions(result => { - result.ShouldContainKey(userId); - result[userId].Invoices.Count.ShouldBe(1); - result[userId].JobSchedules.Count.ShouldBe(1); - result[userId].EmailAuthTokens.Count.ShouldBe(1); - result[userId].ScanEmailDefinitions.Count.ShouldBe(1); - result[userId].Invoices.ShouldContain(invoice); - result[userId].JobSchedules.ShouldContain(jobSchedule); - result[userId].EmailAuthTokens.ShouldContain(emailAuthToken); - result[userId].ScanEmailDefinitions.ShouldContain(scanEmailDefinition); + result.ShouldContainKey(existingUser.Id); + result[existingUser.Id].Invoices.Count.ShouldBe(1); + result[existingUser.Id].JobSchedules.Count.ShouldBe(1); + result[existingUser.Id].EmailAuthTokens.Count.ShouldBe(1); + result[existingUser.Id].ScanEmailDefinitions.Count.ShouldBe(1); + result[existingUser.Id].Invoices.ShouldContain(invoice); + result[existingUser.Id].JobSchedules.ShouldContain(jobSchedule); + result[existingUser.Id].EmailAuthTokens.ShouldContain(emailAuthToken); + result[existingUser.Id].ScanEmailDefinitions.ShouldContain(scanEmailDefinition); }); newUserReference.ShouldSatisfyAllConditions(newUserReference => @@ -544,21 +546,17 @@ public void Handle_ExistingUserWithNullCollections_AddsNewItems() public void Handle_ExistingUserWithNullParameters_DoesNotModifyUser() { // Arrange - var userId = Guid.NewGuid(); - var existingInvoice = new Invoice { Id = Guid.NewGuid() }; - var existingJobSchedule = new JobSchedule { Id = Guid.NewGuid() }; - var exitingEmailAuthToken = new EmailAuthToken { Id = Guid.NewGuid() }; - var existingScanEmailDefinition = new ScanEmailDefinition { Id = Guid.NewGuid() }; - var existingUser = new User - { - Id = userId, - Name = "Existing User", - Invoices = [existingInvoice], - JobSchedules = [existingJobSchedule], - EmailAuthTokens = [exitingEmailAuthToken], - ScanEmailDefinitions = [existingScanEmailDefinition] - }; - var result = new Dictionary { { userId, existingUser } }; + var existingInvoice = _invoiceFaker.Generate(); + var existingJobSchedule = _jobScheduleFaker.Generate(); + var existingEmailAuthToken = _emailAuthTokenFaker.Generate(); + var existingScanEmailDefinition = _scanEmailDefinitionFaker.Generate(); + var existingUser = CreateFaker( + invoices: [existingInvoice], + jobSchedules: [existingJobSchedule], + emailAuthTokens: [existingEmailAuthToken], + scanEmailDefinitions: [existingScanEmailDefinition]) + .Generate(); + var result = new Dictionary { { existingUser.Id, existingUser } }; var newUserReference = existingUser; var parameters = new UserParameters { @@ -574,15 +572,15 @@ public void Handle_ExistingUserWithNullParameters_DoesNotModifyUser() // Assert result.ShouldSatisfyAllConditions(result => { - result.ShouldContainKey(userId); - result[userId].Invoices.ShouldContain(existingInvoice); - result[userId].JobSchedules.ShouldContain(existingJobSchedule); - result[userId].EmailAuthTokens.ShouldContain(exitingEmailAuthToken); - result[userId].ScanEmailDefinitions.ShouldContain(existingScanEmailDefinition); - result[userId].Invoices.Count.ShouldBe(1); - result[userId].JobSchedules.Count.ShouldBe(1); - result[userId].EmailAuthTokens.Count.ShouldBe(1); - result[userId].ScanEmailDefinitions.Count.ShouldBe(1); + result.ShouldContainKey(existingUser.Id); + result[existingUser.Id].Invoices.ShouldContain(existingInvoice); + result[existingUser.Id].JobSchedules.ShouldContain(existingJobSchedule); + result[existingUser.Id].EmailAuthTokens.ShouldContain(existingEmailAuthToken); + result[existingUser.Id].ScanEmailDefinitions.ShouldContain(existingScanEmailDefinition); + result[existingUser.Id].Invoices.Count.ShouldBe(1); + result[existingUser.Id].JobSchedules.Count.ShouldBe(1); + result[existingUser.Id].EmailAuthTokens.Count.ShouldBe(1); + result[existingUser.Id].ScanEmailDefinitions.Count.ShouldBe(1); }); newUserReference.ShouldSatisfyAllConditions(newUserReference => @@ -593,7 +591,7 @@ public void Handle_ExistingUserWithNullParameters_DoesNotModifyUser() newUserReference.ScanEmailDefinitions.Count.ShouldBe(1); newUserReference.Invoices.ShouldContain(existingInvoice); newUserReference.JobSchedules.ShouldContain(existingJobSchedule); - newUserReference.EmailAuthTokens.ShouldContain(exitingEmailAuthToken); + newUserReference.EmailAuthTokens.ShouldContain(existingEmailAuthToken); newUserReference.ScanEmailDefinitions.ShouldContain(existingScanEmailDefinition); }); } @@ -603,7 +601,7 @@ public void Handle_NewUserWithNullParameters_AddsUserWithEmptyCollections() { // Arrange var result = new Dictionary(); - var user = new User { Id = Guid.NewGuid(), Name = "Test User" }; + var user = CreateFaker().Generate(); var parameters = new UserParameters { Invoice = null, diff --git a/InvoiceReminder.DomainEntities.UnitTests/InvoiceReminder.DomainEntities.UnitTests.csproj b/InvoiceReminder.DomainEntities.UnitTests/InvoiceReminder.DomainEntities.UnitTests.csproj index 2c50dd8..ccd4db7 100644 --- a/InvoiceReminder.DomainEntities.UnitTests/InvoiceReminder.DomainEntities.UnitTests.csproj +++ b/InvoiceReminder.DomainEntities.UnitTests/InvoiceReminder.DomainEntities.UnitTests.csproj @@ -16,6 +16,7 @@ + diff --git a/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/BaseRepositoryTests.cs b/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/BaseRepositoryTests.cs index 8b14a05..a9ca8c5 100644 --- a/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/BaseRepositoryTests.cs +++ b/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/BaseRepositoryTests.cs @@ -1,3 +1,4 @@ +using Bogus; using InvoiceReminder.Data.Repository; using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; @@ -12,6 +13,7 @@ public sealed class BaseRepositoryTests { private readonly SqliteConnection _connection; private readonly DbContextOptions _contextOptions; + private Faker _testEntityFaker; public TestContext TestContext { get; set; } @@ -28,6 +30,7 @@ public BaseRepositoryTests() [TestInitialize] public async Task Setup() { + InitializeFaker(); using var context = new TestDbContext(_contextOptions); _ = await context.Database.EnsureCreatedAsync(TestContext.CancellationToken); } @@ -38,6 +41,13 @@ public void TearDown() _connection.Dispose(); } + private void InitializeFaker() + { + _testEntityFaker = new Faker() + .RuleFor(e => e.Id, _ => Guid.NewGuid()) + .RuleFor(e => e.Name, f => f.Lorem.Word()); + } + private TestDbContext CreateContext() { return new(_contextOptions); @@ -47,7 +57,7 @@ private TestDbContext CreateContext() public async Task AddAsync_Should_AddEntityToDatabase() { // Arrange - var entity = new TestEntity { Id = Guid.NewGuid(), Name = "Test" }; + var entity = _testEntityFaker.Generate(); using var context = CreateContext(); var repository = new BaseRepository(context); @@ -64,11 +74,7 @@ public async Task AddAsync_Should_AddEntityToDatabase() public async Task BulkInsertAsync_Should_AddMultipleEntitiesToDatabaseWithTimestamps() { // Arrange - var entities = new List - { - new() { Id = Guid.NewGuid(), Name = "Test1" }, - new() { Id = Guid.NewGuid(), Name = "Test2" } - }; + var entities = _testEntityFaker.Generate(2); using var context = CreateContext(); var repository = new BaseRepository(context); @@ -86,20 +92,18 @@ public async Task BulkInsertAsync_Should_AddMultipleEntitiesToDatabaseWithTimest total.ShouldBe(entities.Count); context.TestEntities.ShouldAllBe(e => e.CreatedAt.HasValue && e.UpdatedAt.HasValue); }); - } [TestMethod] public async Task Remove_Should_RemoveExistingEntityFromDatabase() { // Arrange - var entity = new TestEntity { Id = Guid.NewGuid(), Name = "Test" }; + var entity = _testEntityFaker.Generate(); using var context = CreateContext(); var repository = new BaseRepository(context); _ = await repository.AddAsync(entity, TestContext.CancellationToken); _ = await context.SaveChangesAsync(TestContext.CancellationToken); - // Act repository.Remove(entity); _ = await context.SaveChangesAsync(TestContext.CancellationToken); @@ -112,7 +116,7 @@ public async Task Remove_Should_RemoveExistingEntityFromDatabase() public async Task Remove_Should_AttachAndRemoveDetachedEntity() { // Arrange - var entity = new TestEntity { Id = Guid.NewGuid(), Name = "Test" }; + var entity = _testEntityFaker.Generate(); using var context = CreateContext(); var repository = new BaseRepository(context); @@ -134,7 +138,7 @@ public async Task Remove_Should_AttachAndRemoveDetachedEntity() public async Task GetByIdAsync_Should_ReturnEntityById() { // Arrange - var entity = new TestEntity { Id = Guid.NewGuid(), Name = "Test" }; + var entity = _testEntityFaker.Generate(); using var context = CreateContext(); var repository = new BaseRepository(context); @@ -172,11 +176,7 @@ public async Task GetByIdAsync_Should_ReturnNullWhenEntityNotFound() public async Task GetAll_Should_ReturnAllEntities() { // Arrange - var entities = new List - { - new() { Id = Guid.NewGuid(), Name = "Test1" }, - new() { Id = Guid.NewGuid(), Name = "Test2" } - }; + var entities = _testEntityFaker.Generate(2); using var context = CreateContext(); var repository = new BaseRepository(context); @@ -200,14 +200,14 @@ public async Task GetAll_Should_ReturnAllEntities() public async Task Update_Should_UpdateExistingEntity() { // Arrange - var entity = new TestEntity { Id = Guid.NewGuid(), Name = "Original Name" }; + var entity = _testEntityFaker.Generate(); using var context = CreateContext(); var repository = new BaseRepository(context); _ = await repository.AddAsync(entity, TestContext.CancellationToken); _ = await context.SaveChangesAsync(TestContext.CancellationToken); - entity.Name = "Updated Name"; + entity.Name = _testEntityFaker.Generate().Name; // Act var updatedEntity = repository.Update(entity); @@ -220,7 +220,7 @@ public async Task Update_Should_UpdateExistingEntity() retrievedEntity.ShouldSatisfyAllConditions(() => { _ = retrievedEntity.ShouldNotBeNull(); - retrievedEntity.Name.ShouldBe("Updated Name"); + retrievedEntity.Name.ShouldBe(entity.Name); }); } @@ -228,7 +228,7 @@ public async Task Update_Should_UpdateExistingEntity() public async Task Update_Should_AttachAndUpdateDetachedEntity() { // Arrange - var entity = new TestEntity { Id = Guid.NewGuid(), Name = "Original Name" }; + var entity = _testEntityFaker.Generate(); using var context = CreateContext(); var repository = new BaseRepository(context); @@ -238,7 +238,7 @@ public async Task Update_Should_AttachAndUpdateDetachedEntity() _ = context.Attach(entity); context.Entry(entity).State = EntityState.Detached; - entity.Name = "Updated Name"; + entity.Name = _testEntityFaker.Generate().Name; // Act var updatedEntity = repository.Update(entity); @@ -251,7 +251,7 @@ public async Task Update_Should_AttachAndUpdateDetachedEntity() retrievedEntity.ShouldSatisfyAllConditions(() => { _ = retrievedEntity.ShouldNotBeNull(); - retrievedEntity.Name.ShouldBe("Updated Name"); + retrievedEntity.Name.ShouldBe(entity.Name); }); } @@ -259,12 +259,16 @@ public async Task Update_Should_AttachAndUpdateDetachedEntity() public async Task Where_Should_ReturnEntitiesMatchingPredicate() { // Arrange - var entities = new List - { - new() { Id = Guid.NewGuid(), Name = "Test1" }, - new() { Id = Guid.NewGuid(), Name = "AnotherTest" }, - new() { Id = Guid.NewGuid(), Name = "Test2" } - }; + var entity1 = _testEntityFaker.Generate(); + entity1.Name = "Test1"; + + var entity2 = _testEntityFaker.Generate(); + entity2.Name = "AnotherTest"; + + var entity3 = _testEntityFaker.Generate(); + entity3.Name = "Test2"; + + var entities = new List { entity1, entity2, entity3 }; using var context = CreateContext(); context.TestEntities.AddRange(entities); diff --git a/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/ScanEmailDefinitionRepositoryTests.cs b/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/ScanEmailDefinitionRepositoryTests.cs index b7b09fa..214f7b7 100644 --- a/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/ScanEmailDefinitionRepositoryTests.cs +++ b/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/ScanEmailDefinitionRepositoryTests.cs @@ -1,7 +1,9 @@ +using Bogus; using InvoiceReminder.Data.Interfaces; using InvoiceReminder.Data.Persistence; using InvoiceReminder.Data.Repository; using InvoiceReminder.Domain.Entities; +using InvoiceReminder.Domain.Enums; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using NSubstitute; @@ -29,6 +31,22 @@ public ScanEmailDefinitionRepositoryTests() _repository = Substitute.For(); } + private static Faker CreateFaker(Action> configure = null) + { + var faker = new Faker() + .RuleFor(s => s.Id, _ => Guid.NewGuid()) + .RuleFor(s => s.UserId, _ => Guid.NewGuid()) + .RuleFor(s => s.InvoiceType, f => f.PickRandom()) + .RuleFor(s => s.Beneficiary, f => f.Company.CompanyName()) + .RuleFor(s => s.Description, f => f.Lorem.Sentence()) + .RuleFor(s => s.SenderEmailAddress, f => f.Internet.Email()) + .RuleFor(s => s.AttachmentFileName, f => f.System.FileName()); + + configure?.Invoke(faker); + + return faker; + } + [TestMethod] public void ScanEmailDefinitionRepository_ShouldBeAssignableToItsInterface_And_GenericInterface_And_GenericRepository() { @@ -52,13 +70,17 @@ public async Task GetBySenderBeneficiaryAsync_ShouldReturnScanEmailDefinition_Wh { // Arrange var userId = Guid.NewGuid(); - var scanEmailDefinition = new ScanEmailDefinition { UserId = userId, Beneficiary = "test" }; + var beneficiary = new Faker().Company.CompanyName(); + var scanEmailDefinition = CreateFaker() + .RuleFor(s => s.UserId, _ => userId) + .RuleFor(s => s.Beneficiary, _ => beneficiary) + .Generate(); _ = _repository.GetBySenderBeneficiaryAsync(Arg.Any(), Arg.Any(), Arg.Any()) .Returns(Task.FromResult(scanEmailDefinition)); // Act - var result = await _repository.GetBySenderBeneficiaryAsync("test", userId, TestContext.CancellationToken); + var result = await _repository.GetBySenderBeneficiaryAsync(beneficiary, userId, TestContext.CancellationToken); // Assert result.ShouldSatisfyAllConditions(() => @@ -66,6 +88,7 @@ public async Task GetBySenderBeneficiaryAsync_ShouldReturnScanEmailDefinition_Wh _ = result.ShouldNotBeNull(); _ = result.ShouldBeOfType(); result.UserId.ShouldBe(userId); + result.Beneficiary.ShouldBe(beneficiary); }); } @@ -74,13 +97,17 @@ public async Task GetBySenderEmailAsync_ShouldReturnScanEmailDefinition_WhenScan { // Arrange var userId = Guid.NewGuid(); - var scanEmailDefinition = new ScanEmailDefinition { UserId = userId, SenderEmailAddress = "test@mail.com" }; + var senderEmail = new Faker().Internet.Email(); + var scanEmailDefinition = CreateFaker() + .RuleFor(s => s.UserId, _ => userId) + .RuleFor(s => s.SenderEmailAddress, _ => senderEmail) + .Generate(); _ = _repository.GetBySenderEmailAddressAsync(Arg.Any(), Arg.Any(), Arg.Any()) .Returns(Task.FromResult(scanEmailDefinition)); // Act - var result = await _repository.GetBySenderEmailAddressAsync("test@mail.com", userId, TestContext.CancellationToken); + var result = await _repository.GetBySenderEmailAddressAsync(senderEmail, userId, TestContext.CancellationToken); // Assert result.ShouldSatisfyAllConditions(() => @@ -88,6 +115,7 @@ public async Task GetBySenderEmailAsync_ShouldReturnScanEmailDefinition_WhenScan _ = result.ShouldNotBeNull(); _ = result.ShouldBeOfType(); result.UserId.ShouldBe(userId); + result.SenderEmailAddress.ShouldBe(senderEmail); }); } @@ -96,11 +124,9 @@ public async Task GetByUserIdAsync_ShouldReturnScanEmailDefinition_WhenScanEmail { // Arrange var userId = Guid.NewGuid(); - var collection = new List - { - new() { Id = Guid.NewGuid(), UserId = userId, Beneficiary = "test_A" }, - new() { Id = Guid.NewGuid(), UserId = userId, Beneficiary = "test_B" } - }; + var collection = CreateFaker() + .RuleFor(s => s.UserId, _ => userId) + .Generate(2); _ = _repository.GetByUserIdAsync(Arg.Any(), Arg.Any()) .Returns(Task.FromResult>(collection)); @@ -115,6 +141,7 @@ public async Task GetByUserIdAsync_ShouldReturnScanEmailDefinition_WhenScanEmail _ = result.ShouldBeOfType>(); result.ShouldNotBeEmpty(); result.ShouldContain(x => x.UserId == userId); + result.Count().ShouldBe(2); }); } } diff --git a/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/UserRepositoryTests.cs b/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/UserRepositoryTests.cs index 8a1037f..46ec4da 100644 --- a/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/UserRepositoryTests.cs +++ b/InvoiceReminder.Infrastructure.UnitTests/Data/Repository/UserRepositoryTests.cs @@ -1,3 +1,4 @@ +using Bogus; using InvoiceReminder.Data.Interfaces; using InvoiceReminder.Data.Persistence; using InvoiceReminder.Data.Repository; @@ -29,6 +30,16 @@ public UserRepositoryTests() _repository = Substitute.For(); } + private static Faker CreateFaker() + { + return new Faker() + .RuleFor(u => u.Id, _ => Guid.NewGuid()) + .RuleFor(u => u.TelegramChatId, f => f.Random.Long(100000000, long.MaxValue)) + .RuleFor(u => u.Name, f => f.Person.FullName) + .RuleFor(u => u.Email, f => f.Internet.Email()) + .RuleFor(u => u.Password, f => f.Internet.Password(length: 16, memorable: false)); + } + [TestMethod] public void UserRepository_ShouldBeAssignableToItsInterface_And_GenericInterface_And_GenericRepository() { @@ -51,8 +62,10 @@ public void UserRepository_ShouldBeAssignableToItsInterface_And_GenericInterface public async Task GetByEmailAsync_ShouldReturnUser_WhenUserExists() { // Arrange - var email = "user_test@mail.com"; - var user = new User { Id = Guid.NewGuid(), Email = email }; + var email = new Faker().Internet.Email(); + var user = CreateFaker() + .RuleFor(u => u.Email, _ => email) + .Generate(); _ = _repository.GetByEmailAsync(Arg.Any(), Arg.Any()) .Returns(Task.FromResult(user)); @@ -66,6 +79,7 @@ public async Task GetByEmailAsync_ShouldReturnUser_WhenUserExists() _ = result.ShouldNotBeNull(); _ = result.ShouldBeOfType(); result.Email.ShouldBe(email); + result.Id.ShouldNotBe(Guid.Empty); }); } @@ -74,7 +88,9 @@ public async Task GetByIdAsync_ShouldReturnUser_WhenUserExists() { // Arrange var userId = Guid.NewGuid(); - var user = new User { Id = userId }; + var user = CreateFaker() + .RuleFor(u => u.Id, _ => userId) + .Generate(); _ = _repository.GetByIdAsync(Arg.Any(), Arg.Any()) .Returns(Task.FromResult(user)); diff --git a/InvoiceReminder.Infrastructure.UnitTests/InvoiceReminder.Infrastructure.UnitTests.csproj b/InvoiceReminder.Infrastructure.UnitTests/InvoiceReminder.Infrastructure.UnitTests.csproj index 4ef0b10..1f094f2 100644 --- a/InvoiceReminder.Infrastructure.UnitTests/InvoiceReminder.Infrastructure.UnitTests.csproj +++ b/InvoiceReminder.Infrastructure.UnitTests/InvoiceReminder.Infrastructure.UnitTests.csproj @@ -17,6 +17,7 @@ + diff --git a/InvoiceReminder.JobScheduler.UnitTests/HostedService/QuartzHostedServiceTests.cs b/InvoiceReminder.JobScheduler.UnitTests/HostedService/QuartzHostedServiceTests.cs index 6655524..20b4882 100644 --- a/InvoiceReminder.JobScheduler.UnitTests/HostedService/QuartzHostedServiceTests.cs +++ b/InvoiceReminder.JobScheduler.UnitTests/HostedService/QuartzHostedServiceTests.cs @@ -1,3 +1,4 @@ +using Bogus; using InvoiceReminder.Domain.Entities; using InvoiceReminder.JobScheduler.HostedService; using Microsoft.Extensions.Logging; @@ -17,6 +18,7 @@ public sealed class QuartzHostedServiceTests private readonly IScheduler _scheduler; private readonly List _schedules; private readonly QuartzHostedService _service; + private Faker _jobScheduleFaker; public TestContext TestContext { get; set; } @@ -26,24 +28,33 @@ public QuartzHostedServiceTests() _schedulerFactory = Substitute.For(); _jobFactory = Substitute.For(); _scheduler = Substitute.For(); - _schedules = - [ - new() { - Id = Guid.NewGuid(), - UserId = Guid.NewGuid(), - CronExpression = "0/5 * * * * ?" - } - ]; + _schedules = []; _service = Substitute.For(_logger, _schedulerFactory, _jobFactory, _schedules); _ = _schedulerFactory.GetScheduler(Arg.Any()).Returns(Task.FromResult(_scheduler)); } + [TestInitialize] + public void Setup() + { + InitializeFaker(); + _schedules.Clear(); + } + + private void InitializeFaker() + { + _jobScheduleFaker = new Faker() + .RuleFor(j => j.Id, _ => Guid.NewGuid()) + .RuleFor(j => j.UserId, _ => Guid.NewGuid()) + .RuleFor(j => j.CronExpression, _ => "0/5 * * * * ?"); + } + [TestMethod] public async Task ScheduleJobAsync_ShouldScheduleJobAndStartScheduler() { // Arrange - var schedule = _schedules[0]; + var schedule = _jobScheduleFaker.Generate(); + _schedules.Add(schedule); // Act await _service.ScheduleJobAsync(schedule, TestContext.CancellationToken); @@ -73,7 +84,8 @@ await _service.ScheduleJobAsync(null, TestContext.CancellationToken) public async Task DeleteJobAsync_ShouldDeleteJobIfExists() { // Arrange - var schedule = _schedules[0]; + var schedule = _jobScheduleFaker.Generate(); + _schedules.Add(schedule); var jobKey = new JobKey($"{schedule.Id}.job"); _ = _scheduler.CheckExists(jobKey, Arg.Is(ct => ct == TestContext.CancellationToken)) @@ -103,7 +115,8 @@ await _service.DeleteJobAsync(null, TestContext.CancellationToken) public async Task PauseJobAsync_ShouldPauseTriggerAndJob() { // Arrange - var schedule = _schedules[0]; + var schedule = _jobScheduleFaker.Generate(); + _schedules.Add(schedule); // Act await _service.PauseJobAsync(schedule, TestContext.CancellationToken); @@ -131,7 +144,8 @@ await _service.PauseJobAsync(null, TestContext.CancellationToken) public async Task ResumeJobAsync_ShouldResumeTriggerAndJob() { // Arrange - var schedule = new JobSchedule { Id = Guid.NewGuid() }; + var schedule = _jobScheduleFaker.Generate(); + _schedules.Add(schedule); var service = new QuartzHostedService(_logger, _schedulerFactory, _jobFactory, _schedules); // Act @@ -162,7 +176,10 @@ await service.ResumeJobAsync(null, TestContext.CancellationToken) [TestMethod] public async Task StartAsync_ShouldScheduleAndStartAllSchedules() { - // Arrange & Act + // Arrange + _schedules.Add(_jobScheduleFaker.Generate()); + + // Act await _service.StartAsync(TestContext.CancellationToken); // Assert @@ -180,9 +197,12 @@ public async Task StartAsync_ShouldScheduleAndStartAllSchedules() public async Task StartAsync_InvalidCronExpression_ShouldLogError() { // Arrange - var schedule = new JobSchedule { Id = Guid.NewGuid(), CronExpression = "invalid cron", UserId = Guid.NewGuid() }; - var schedules = new List { schedule }; - var service = new QuartzHostedService(_logger, _schedulerFactory, _jobFactory, schedules); + var schedule = _jobScheduleFaker + .RuleFor(j => j.CronExpression, _ => "invalid cron") + .Generate(); + + _schedules.Add(schedule); + var service = new QuartzHostedService(_logger, _schedulerFactory, _jobFactory, _schedules); _ = _logger.IsEnabled(Arg.Any()).Returns(true); @@ -204,7 +224,10 @@ public async Task StartAsync_InvalidCronExpression_ShouldLogError() [TestMethod] public async Task StopAsync_ShouldShutdownScheduler() { - // Arrange & Act + // Arrange + _schedules.Add(_jobScheduleFaker.Generate()); + + // Act await _service.StartAsync(TestContext.CancellationToken); await _service.StopAsync(TestContext.CancellationToken); diff --git a/InvoiceReminder.JobScheduler.UnitTests/InvoiceReminder.JobScheduler.UnitTests.csproj b/InvoiceReminder.JobScheduler.UnitTests/InvoiceReminder.JobScheduler.UnitTests.csproj index 618eb11..a4e37a0 100644 --- a/InvoiceReminder.JobScheduler.UnitTests/InvoiceReminder.JobScheduler.UnitTests.csproj +++ b/InvoiceReminder.JobScheduler.UnitTests/InvoiceReminder.JobScheduler.UnitTests.csproj @@ -15,6 +15,7 @@ + all diff --git a/InvoiceReminder.sln b/InvoiceReminder.sln index 6997efe..a69510d 100644 --- a/InvoiceReminder.sln +++ b/InvoiceReminder.sln @@ -65,6 +65,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Itens de Solução", "Itens Directory.Build.props = Directory.Build.props Directory.Packages.props = Directory.Packages.props docker-compose.yml = docker-compose.yml + global.json = global.json EndProjectSection EndProject Global diff --git a/global.json b/global.json new file mode 100644 index 0000000..12d1731 --- /dev/null +++ b/global.json @@ -0,0 +1,9 @@ +{ + "sdk": { + "version": "10.0.100" + }, + + "test": { + "runner": "Microsoft.Testing.Platform" + } +}