From d2d0a3e90e72362a83a4bf63eca6f155e52e2448 Mon Sep 17 00:00:00 2001 From: Felipe Augusto Date: Mon, 8 Apr 2024 20:55:57 -0300 Subject: [PATCH] feat: refatorando baseado em testes --- .gitignore | 2 + .vscode/launch.json | 68 +- CODEOWNERS | 2 + README.md | 18 +- docker/docker-compose.yml | 4 +- .../Controllers/AppConfigurationController.cs | 45 -- .../Controllers/PingController.cs | 18 + ...troller.cs => TestsResourcesController.cs} | 53 +- .../Controllers/v1/AdocoesController.cs | 41 +- .../Controllers/v1/CachorrosController.cs | 63 +- .../Controllers/v1/TutoresController.cs | 5 +- .../DEPLOY.Cachorro.Api.csproj | 10 +- .../Extensions/Database/DatabaseExtension.cs | 1 + .../HttpClient/HttpClientExtension.cs | 35 - .../Extensions/Swagger/SwaggerExtension.cs | 1 + src/DEPLOY.Cachorro.Api/Program.cs | 9 +- .../AppServices/AdocaoAppService.cs | 12 +- .../AppServices/CachorroAppServices.cs | 34 +- .../AppServices/TutorAppServices.cs | 35 +- .../DEPLOY.Cachorro.Application.csproj | 23 +- .../Dtos/BaseDto.cs | 3 +- .../Dtos/CachorroDto.cs | 7 +- .../Dtos/TutorDto.cs | 4 +- .../Interfaces/Services/IAdocaoAppService.cs | 9 +- .../Services/ICachorroAppServices.cs | 12 +- .../Interfaces/Services/ITutorAppServices.cs | 2 +- .../Interfaces/Service/IAdocaoService.cs | 9 +- .../Adotar/Services/AdocaoService.cs | 59 +- .../Adotar/Validations/AdocaoValidator.cs | 13 + .../Aggregates/Cachorro/Entities/Cachorro.cs | 9 +- .../Cachorro/Services/CachorroService.cs | 1 - .../Cachorro/Validations/CachorroValidator.cs | 22 +- .../Aggregates/Tutor/Entities/Tutor.cs | 5 +- .../Aggregates/Tutor/Services/TutorService.cs | 1 + .../Tutor/Validations/TutorValidator.cs | 76 +- .../DEPLOY.Cachorro.Domain.csproj | 31 +- .../Shared/GenericService.cs | 34 +- .../Shared/IGenericRepository.cs | 13 +- .../Shared/IGenericService.cs | 10 +- .../DEPLOY.Cachorro.Infra.CrossCutting.csproj | 28 +- src/DEPLOY.Cachorro.Infra.CrossCutting/IoC.cs | 8 +- .../DEPLOY.Cachorro.Infra.Repository.csproj | 49 +- .../DbContext/CachorroDbContext.cs | 2 +- .../CachorroEntityConfiguration.cs | 6 +- .../TutorEntityConfiguration.cs | 8 +- ...0240408230334_InitDatabaseAPI.Designer.cs} | 25 +- ...I.cs => 20240408230334_InitDatabaseAPI.cs} | 8 +- .../API/CachorroDbContextModelSnapshot.cs | 23 +- .../Repositories/Base/GenericRepository.cs | 32 +- .../Repositories/Base/UnitOfWork.cs | 22 +- .../Repositories/CachorroRepository.cs | 13 +- .../AdocoesControllerTest.cs | 190 +++++ .../CachorrosControllerTestOLD.cs | 329 --------- ...erTests.cs => CachorrosControllerTests.cs} | 200 +++++- .../DEPLOY.Cachorro.Api.Tests.csproj | 1 - .../TutoresControllerTest.cs | 675 +++++++++--------- .../CachorroDtoFixture.cs | 54 +- .../CachorroFixture.cs | 49 +- .../DEPLOY.Cachorro.Base.Tests.csproj | 1 + .../TutorDtoFixture.cs | 37 + .../TutorFixture.cs | 35 + .../AdotarServiceTests.cs | 160 +++++ .../CachorroServiceTest.cs | 129 +++- .../DEPLOY.Cachorro.Domain.Tests.csproj | 60 +- .../TutorServiceTest.cs | 97 +++ .../CachorroRepositoryTest.cs | 243 ++++++- .../DEPLOY.Cachorro.Repository.Tests.csproj | 64 +- .../TutorRepositoryTest.cs | 125 ++++ 68 files changed, 2335 insertions(+), 1137 deletions(-) create mode 100644 CODEOWNERS delete mode 100644 src/DEPLOY.Cachorro.Api/Controllers/AppConfigurationController.cs create mode 100644 src/DEPLOY.Cachorro.Api/Controllers/PingController.cs rename src/DEPLOY.Cachorro.Api/Controllers/{TestsKeyVaultController.cs => TestsResourcesController.cs} (56%) delete mode 100644 src/DEPLOY.Cachorro.Api/Extensions/HttpClient/HttpClientExtension.cs create mode 100644 src/DEPLOY.Cachorro.Domain/Aggregates/Adotar/Validations/AdocaoValidator.cs rename src/DEPLOY.Cachorro.Infra.Repository/Migrations/API/{20240317120035_InitDatabaseAPI.Designer.cs => 20240408230334_InitDatabaseAPI.Designer.cs} (85%) rename src/DEPLOY.Cachorro.Infra.Repository/Migrations/API/{20240317120035_InitDatabaseAPI.cs => 20240408230334_InitDatabaseAPI.cs} (87%) create mode 100644 src/Tests/DEPLOY.Cachorro.Api.Tests/AdocoesControllerTest.cs delete mode 100644 src/Tests/DEPLOY.Cachorro.Api.Tests/CachorrosControllerTestOLD.cs rename src/Tests/DEPLOY.Cachorro.Api.Tests/{CachorroControllerTests.cs => CachorrosControllerTests.cs} (51%) create mode 100644 src/Tests/DEPLOY.Cachorro.Base.Tests/TutorDtoFixture.cs create mode 100644 src/Tests/DEPLOY.Cachorro.Base.Tests/TutorFixture.cs create mode 100644 src/Tests/DEPLOY.Cachorro.Domain.Tests/AdotarServiceTests.cs create mode 100644 src/Tests/DEPLOY.Cachorro.Domain.Tests/TutorServiceTest.cs create mode 100644 src/Tests/DEPLOY.Cachorro.Repository.Tests/TutorRepositoryTest.cs diff --git a/.gitignore b/.gitignore index e9610ed..fc973ab 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,8 @@ src/DEPLOY.Cachorro.Api.Tests/coveragereport/ src/DEPLOY.Cachorro.Api.Tests/coveragereport/* src/coverage.xml +coverage.xml +.sonarqube # User-specific files *.rsuser diff --git a/.vscode/launch.json b/.vscode/launch.json index 5a51dba..7ce0d73 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,35 +1,35 @@ { - "version": "0.2.0", - "configurations": [ - { - // Use IntelliSense to find out which attributes exist for C# debugging - // Use hover for the description of the existing attributes - // For further information visit https://github.com/dotnet/vscode-csharp/blob/main/debugger-launchjson.md. - "name": ".NET Core Launch (web)", - "type": "coreclr", - "request": "launch", - "preLaunchTask": "build", - // If you have changed target frameworks, make sure to update the program path. - "program": "${workspaceFolder}/src/DEPLOY.Cachorro.Api/bin/Debug/net7.0/DEPLOY.Cachorro.Api.dll", - "args": [], - "cwd": "${workspaceFolder}/src/DEPLOY.Cachorro.Api", - "stopAtEntry": false, - // Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser - "serverReadyAction": { - "action": "openExternally", - "pattern": "\\bNow listening on:\\s+(https?://\\S+)" - }, - "env": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "sourceFileMap": { - "/Views": "${workspaceFolder}/Views" - } - }, - { - "name": ".NET Core Attach", - "type": "coreclr", - "request": "attach" - } - ] -} \ No newline at end of file + "version": "0.2.0", + "configurations": [ + { + // Use IntelliSense to find out which attributes exist for C# debugging + // Use hover for the description of the existing attributes + // For further information visit https://github.com/dotnet/vscode-csharp/blob/main/debugger-launchjson.md. + "name": ".NET Core Launch (web)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + // If you have changed target frameworks, make sure to update the program path. + "program": "${workspaceFolder}/src/DEPLOY.Cachorro.Api/bin/Debug/net8.0/DEPLOY.Cachorro.Api.dll", + "args": [], + "cwd": "${workspaceFolder}/src/DEPLOY.Cachorro.Api", + "stopAtEntry": false, + // Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser + "serverReadyAction": { + "action": "openExternally", + "pattern": "\\bNow listening on:\\s+(https?://\\S+)" + }, + "env": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "sourceFileMap": { + "/Views": "${workspaceFolder}/Views" + } + }, + { + "name": ".NET Core Attach", + "type": "coreclr", + "request": "attach" + } + ] +} diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000..16e819b --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,2 @@ +# Para todos os arquivos C# +*.cs @felipementel \ No newline at end of file diff --git a/README.md b/README.md index dce5efd..ac3e1c5 100644 --- a/README.md +++ b/README.md @@ -124,6 +124,22 @@ reportgenerator -reports:..\TestResults\DotnetCoverageCollect\**\coverage.cobert 1.2 A partir da pasta src execute o comando: +# Ambiente Local + +## SonarQube + +1. Comandos para submeter o código ao sonarqube do Container + +dotnet sonarscanner begin /k:"CachorroAPI" /d:sonar.host.url="http://localhost:9044" /d:sonar.token="xxxxx" /d:sonar.cs.vscoveragexml.reportsPaths=coverage.xml /d:sonar.exclusions="**/Migrations/**" + +dotnet restore .\src\DEPLOY.Cachorro.Api\DEPLOY.Cachorro.Api.csproj + +dotnet build .\src\DEPLOY.Cachorro.Api\DEPLOY.Cachorro.Api.csproj --no-incremental + +dotnet-coverage collect 'dotnet test ./src/' -f xml -o 'coverage.xml' + +dotnet-sonarscanner end /d:sonar.token="xxxxx" + --- # EntityFramework Commands @@ -144,8 +160,6 @@ dotnet ef migrations add InitDatabaseAPI -s DEPLOY.Cachorro.Api -p DEPLOY.Cachor dotnet ef database update InitDatabaseAPI --startup-project DEPLOY.Cachorro.Api --project DEPLOY.Cachorro.Infra.Repository --context DEPLOY.Cachorro.Infra.Repository.CachorroDbContext --verbose - - ``` Connection String diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 179b64f..313285b 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -2,7 +2,7 @@ # docker-compose -f ../docker/docker-compose.yml up -d --build # docker-compose -f ../docker/docker-compose.yml down --remove-orphans -version: "3.8" +version: '3.8' services: # cachorro.api: # hostname: cachorro-api @@ -44,7 +44,7 @@ services: - 1433:1433 sonarqube: - image: sonarqube:10.3.0-community + image: sonarqube:10.4.1-community hostname: sonar-canal-deploy container_name: sonar-canal-deploy restart: always diff --git a/src/DEPLOY.Cachorro.Api/Controllers/AppConfigurationController.cs b/src/DEPLOY.Cachorro.Api/Controllers/AppConfigurationController.cs deleted file mode 100644 index aa241f1..0000000 --- a/src/DEPLOY.Cachorro.Api/Controllers/AppConfigurationController.cs +++ /dev/null @@ -1,45 +0,0 @@ -using Asp.Versioning; -using DEPLOY.Cachorro.Api.Configs; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Options; -using Microsoft.FeatureManagement; -using System.Diagnostics.CodeAnalysis; - -namespace DEPLOY.Cachorro.Api.Controllers -{ - [ExcludeFromCodeCoverage] - [ApiController] - [ApiVersion("1.0")] - [Route("api/[controller]")] - public class AppConfigurationController : ControllerBase - { - public readonly Settings _settings; - - private readonly IFeatureManager _featureManager; - - public AppConfigurationController( - IOptions settings, - IFeatureManager featureManager) - { - _settings = settings.Value; - _featureManager = featureManager; - } - - [HttpGet] - public IActionResult GetAsync() - { - return Ok(_settings.ValorDaMensagem); - } - - [HttpGet("/featureflag/{featureflag}")] - public async Task Get2FeatureFlagAsync(string featureflag) - { - var IsEnable = await _featureManager.IsEnabledAsync(featureflag); - - if (IsEnable) - return Ok("Sistema no ar."); - else - return BadRequest("Sistema fora do ar"); - } - } -} diff --git a/src/DEPLOY.Cachorro.Api/Controllers/PingController.cs b/src/DEPLOY.Cachorro.Api/Controllers/PingController.cs new file mode 100644 index 0000000..7075173 --- /dev/null +++ b/src/DEPLOY.Cachorro.Api/Controllers/PingController.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Mvc; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; + +namespace DEPLOY.Cachorro.Api.Controllers +{ + [ExcludeFromCodeCoverage] + [ApiController] + [Route("api/[controller]")] + public class PingController : ControllerBase + { + [HttpGet] + public IActionResult GetAsync() + { + return Ok(Assembly.GetExecutingAssembly().GetName().Version); + } + } +} diff --git a/src/DEPLOY.Cachorro.Api/Controllers/TestsKeyVaultController.cs b/src/DEPLOY.Cachorro.Api/Controllers/TestsResourcesController.cs similarity index 56% rename from src/DEPLOY.Cachorro.Api/Controllers/TestsKeyVaultController.cs rename to src/DEPLOY.Cachorro.Api/Controllers/TestsResourcesController.cs index 43afe19..7c727af 100644 --- a/src/DEPLOY.Cachorro.Api/Controllers/TestsKeyVaultController.cs +++ b/src/DEPLOY.Cachorro.Api/Controllers/TestsResourcesController.cs @@ -1,31 +1,39 @@ -using Asp.Versioning; -using Azure.Core; +using Azure.Core; using Azure.Identity; using Azure.Security.KeyVault.Secrets; +using DEPLOY.Cachorro.Api.Configs; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; +using Microsoft.FeatureManagement; using System.Diagnostics.CodeAnalysis; namespace DEPLOY.Cachorro.Api.Controllers { [ExcludeFromCodeCoverage] + [ApiExplorerSettings(IgnoreApi = true)] [ApiController] - [ApiVersion("1.0")] [Route("api/[controller]")] - public class TestsKeyVaultController : ControllerBase + public class TestsResourcesController : ControllerBase { private readonly SecretClient _secretClient; + private readonly Settings _settings; + private readonly IFeatureManager _featureManager; - private readonly IConfiguration _configuration; - - public TestsKeyVaultController( - SecretClient secretClient, - IConfiguration configuration) - { - _secretClient = secretClient; - _configuration = configuration; + private readonly IConfiguration _configuration; + + public TestsResourcesController( + SecretClient secretClient, + IConfiguration configuration, + IOptions settings, + IFeatureManager featureManager) + { + _secretClient = secretClient; + _configuration = configuration; + _settings = settings.Value; + _featureManager = featureManager; } - [HttpGet("{key}")] + [HttpGet("keyvault/withoptions/{key}")] public async Task GetAsync(string key) { SecretClientOptions options = new SecretClientOptions() @@ -51,7 +59,7 @@ public async Task GetAsync(string key) return Ok(secretValue); } - [HttpGet("test2/{key}")] + [HttpGet("keyvault/{key}")] public async Task GetTest2Async(string key) { KeyVaultSecret secret = await _secretClient.GetSecretAsync(name: key); @@ -60,5 +68,22 @@ public async Task GetTest2Async(string key) return Ok(secretValue); } + + [HttpGet("featureflag")] + public IActionResult GetAsync() + { + return Ok(_settings.ValorDaMensagem); + } + + [HttpGet("featureflag/{featureflag}")] + public async Task Get2FeatureFlagAsync(string featureflag) + { + var IsEnable = await _featureManager.IsEnabledAsync(featureflag); + + if (IsEnable) + return Ok("Sistema no ar."); + else + return BadRequest("Sistema fora do ar"); + } } } diff --git a/src/DEPLOY.Cachorro.Api/Controllers/v1/AdocoesController.cs b/src/DEPLOY.Cachorro.Api/Controllers/v1/AdocoesController.cs index bbf29b4..026cd50 100644 --- a/src/DEPLOY.Cachorro.Api/Controllers/v1/AdocoesController.cs +++ b/src/DEPLOY.Cachorro.Api/Controllers/v1/AdocoesController.cs @@ -1,6 +1,6 @@ using Asp.Versioning; -using DEPLOY.Cachorro.Application.Dtos; using DEPLOY.Cachorro.Application.Interfaces.Services; +using DEPLOY.Cachorro.Domain.Aggregates.Cachorro.Interfaces.Repositories; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Swashbuckle.AspNetCore.Annotations; @@ -15,7 +15,8 @@ public class AdocoesController : ControllerBase { private readonly IAdocaoAppService _adocaoAppService; - public AdocoesController(IAdocaoAppService adocaoAppService) + public AdocoesController( + IAdocaoAppService adocaoAppService) { _adocaoAppService = adocaoAppService; } @@ -25,7 +26,7 @@ public AdocoesController(IAdocaoAppService adocaoAppService) [ProducesResponseType(StatusCodes.Status201Created)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] [SwaggerOperation( - Summary = "Adotar um cachorro Cachorro", + Summary = "Adotar um Cachorro", Tags = new[] { "Adocoes" }, Description = "Operação para um tutor adotar um cachorro")] public async Task AdotarAsync( @@ -33,7 +34,39 @@ public async Task AdotarAsync( [FromRoute] long tutorid, CancellationToken cancellationToken = default) { - await _adocaoAppService.AdotarAsync(cachorroid, tutorid, cancellationToken); + var item = await _adocaoAppService.AdotarAsync( + cachorroid, + tutorid, + cancellationToken); + + if (item?.Count() > 0) + { + return UnprocessableEntity(item); + } + + return Ok(); + } + + [HttpPost("cachorro/{cachorroid}")] + [Produces("application/json")] + [ProducesResponseType(StatusCodes.Status201Created)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [SwaggerOperation( + Summary = "Devolver um cachorro que estava adotado", + Tags = new[] { "Adocoes" }, + Description = "Operação para um tutor devolver um cachorro")] + public async Task DevolverAsync( + [FromRoute] Guid cachorroid, + CancellationToken cancellationToken = default) + { + var item = await _adocaoAppService.DevolverAdocaoAsync( + cachorroid, + cancellationToken); + + if (item?.Count() > 0) + { + return UnprocessableEntity(item); + } return Ok(); } diff --git a/src/DEPLOY.Cachorro.Api/Controllers/v1/CachorrosController.cs b/src/DEPLOY.Cachorro.Api/Controllers/v1/CachorrosController.cs index 6dbcce1..acf23af 100644 --- a/src/DEPLOY.Cachorro.Api/Controllers/v1/CachorrosController.cs +++ b/src/DEPLOY.Cachorro.Api/Controllers/v1/CachorrosController.cs @@ -2,6 +2,7 @@ using DEPLOY.Cachorro.Application.Dtos; using DEPLOY.Cachorro.Application.Interfaces.Services; using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using Swashbuckle.AspNetCore.Annotations; @@ -33,9 +34,46 @@ public async Task ListAllAsync( { var items = await _cachorroAppService.GetAllAsync(cancellationToken); - return items.Any() ? Ok(items) : NoContent(); + return items?.Count() > 0 ? Ok(items) : NoContent(); } + [HttpGet("adotados")] + [Produces("application/json")] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status201Created)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [SwaggerOperation( + Summary = "Lista de cachorro adotados", + Tags = new[] { "Cachorros" }, + Description = "Operação para listar de cachorros adotados")] + public async Task ListAllCachorrosAdotadosAsync( + CancellationToken cancellationToken = default) + { + var items = await _cachorroAppService.GetByKeyAsync( + c => c.Tutor != null, + cancellationToken); + + return items?.Count() > 0 ? Ok(items) : NoContent(); + } + + [HttpGet("paraadotar")] + [Produces("application/json")] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status201Created)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [SwaggerOperation( + Summary = "Lista de cachorro disponíveis para adoção", + Tags = new[] { "Cachorros" }, + Description = "Operação para listar de cachorros disponíveis para adoção")] + public async Task ListAllCachorrosParaAdocaoAsync( + CancellationToken cancellationToken = default) + { + var items = await _cachorroAppService.GetByKeyAsync( + c => c.Tutor == null, + cancellationToken); + + return items?.Count > 0 ? Ok(items) : NoContent(); + } + + [HttpGet("{id}")] [Produces("application/json")] [ProducesResponseType(typeof(CachorroDto), StatusCodes.Status201Created)] @@ -50,9 +88,9 @@ public async Task GetByIdAsync( CancellationToken cancellationToken = default) { var items = await _cachorroAppService.GetByIdAsync( - id, + id, cancellationToken); - + if (items == null) { return NotFound(); @@ -78,15 +116,18 @@ public async Task CreateAsync( cachorroCreateDto, cancellationToken); - if (item.Erros.Any()) + if (item?.Erros.Count() > 0) return UnprocessableEntity(item.Erros); return CreatedAtAction("GetById", - new { id = item.Id, + new + { + id = item?.Id, version = new ApiVersion( 1, 0) - .ToString() }, + .ToString() + }, item); } @@ -105,12 +146,14 @@ public async Task UpdateAsync( return UnprocessableEntity(); } - return await _cachorroAppService.UpdateAsync( - id, + var item = await _cachorroAppService.UpdateAsync( + id, cachorroDto, - cancellationToken) + cancellationToken); + + return !item.Any() ? NoContent() - : NotFound(); + : UnprocessableEntity(item); } [HttpDelete("{id}")] diff --git a/src/DEPLOY.Cachorro.Api/Controllers/v1/TutoresController.cs b/src/DEPLOY.Cachorro.Api/Controllers/v1/TutoresController.cs index a182f9e..b7d4872 100644 --- a/src/DEPLOY.Cachorro.Api/Controllers/v1/TutoresController.cs +++ b/src/DEPLOY.Cachorro.Api/Controllers/v1/TutoresController.cs @@ -2,6 +2,7 @@ using DEPLOY.Cachorro.Application.Dtos; using DEPLOY.Cachorro.Application.Interfaces.Services; using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using Swashbuckle.AspNetCore.Annotations; @@ -109,8 +110,8 @@ public async Task UpdateAsync( tutorDto, cancellationToken); - return retorned ? NoContent() - : NotFound(); + return !retorned.Any() ? NoContent() + : UnprocessableEntity(retorned); } [HttpDelete("{id}")] diff --git a/src/DEPLOY.Cachorro.Api/DEPLOY.Cachorro.Api.csproj b/src/DEPLOY.Cachorro.Api/DEPLOY.Cachorro.Api.csproj index 3b95afe..aaa65b9 100644 --- a/src/DEPLOY.Cachorro.Api/DEPLOY.Cachorro.Api.csproj +++ b/src/DEPLOY.Cachorro.Api/DEPLOY.Cachorro.Api.csproj @@ -5,16 +5,17 @@ enable $(NoWarn);1591 $(NoWarn);8604 + $(NoWarn);8603 enable CachorroAPI2 true Linux /subscriptions/f4574f09-7fab-45bd-8eb5-508b8c6aa04a/resourceGroups/rg-canaldeploy-dev/providers/microsoft.insights/components/appi-cachorroapi-dev - 2.1.2 + 2.1.4 - + @@ -31,9 +32,8 @@ - - - + + diff --git a/src/DEPLOY.Cachorro.Api/Extensions/Database/DatabaseExtension.cs b/src/DEPLOY.Cachorro.Api/Extensions/Database/DatabaseExtension.cs index 596e054..c8241a8 100644 --- a/src/DEPLOY.Cachorro.Api/Extensions/Database/DatabaseExtension.cs +++ b/src/DEPLOY.Cachorro.Api/Extensions/Database/DatabaseExtension.cs @@ -1,5 +1,6 @@ using DEPLOY.Cachorro.Infra.Repository; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; using System.Diagnostics.CodeAnalysis; namespace DEPLOY.Cachorro.Api.Extensions.Database diff --git a/src/DEPLOY.Cachorro.Api/Extensions/HttpClient/HttpClientExtension.cs b/src/DEPLOY.Cachorro.Api/Extensions/HttpClient/HttpClientExtension.cs deleted file mode 100644 index d1c89c8..0000000 --- a/src/DEPLOY.Cachorro.Api/Extensions/HttpClient/HttpClientExtension.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Microsoft.Extensions.Http.Resilience; -using Polly; -using Polly.Retry; -using Polly.Timeout; -using System.Net; -using System.Net.Http.Headers; -using System.Threading.RateLimiting; - -namespace DEPLOY.Cachorro.Api.Extensions.HttpClient -{ - public static class HttpClientExtension - { - public static void AddHttpClient(this IServiceCollection services) - { - services - .AddHttpClient("Pokemon", client => - { - client.BaseAddress = new Uri("https://pokeapi.co/api/v2/pokemon"); - client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); - }) - .AddStandardResilienceHandler(options => - { - // Customize retry - options.Retry.ShouldHandle = new PredicateBuilder() - .Handle() - .Handle() - .HandleResult(response => response.StatusCode == HttpStatusCode.InternalServerError); - options.Retry.MaxRetryAttempts = 5; - - // Customize attempt timeout - options.AttemptTimeout.Timeout = TimeSpan.FromSeconds(2); - }); - } - } -} diff --git a/src/DEPLOY.Cachorro.Api/Extensions/Swagger/SwaggerExtension.cs b/src/DEPLOY.Cachorro.Api/Extensions/Swagger/SwaggerExtension.cs index 7316018..271d091 100644 --- a/src/DEPLOY.Cachorro.Api/Extensions/Swagger/SwaggerExtension.cs +++ b/src/DEPLOY.Cachorro.Api/Extensions/Swagger/SwaggerExtension.cs @@ -49,6 +49,7 @@ public static void UseSwaggerExtension(this WebApplication app) app.UseSwaggerUI( options => { + options.DisplayRequestDuration(); var descriptions = app.DescribeApiVersions(); options.RoutePrefix = string.Empty; diff --git a/src/DEPLOY.Cachorro.Api/Program.cs b/src/DEPLOY.Cachorro.Api/Program.cs index 22c9ca5..1e89ef5 100644 --- a/src/DEPLOY.Cachorro.Api/Program.cs +++ b/src/DEPLOY.Cachorro.Api/Program.cs @@ -7,7 +7,6 @@ using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization; using DEPLOY.Cachorro.Infra.CrossCutting; - namespace DEPLOY.Cachorro.Api { [ExcludeFromCodeCoverage] @@ -18,7 +17,7 @@ public static void Main(string[] args) { var builder = WebApplication.CreateBuilder(args); - builder.Services.RegisterServices(); + builder.Services.AddRegisterServices(); builder.Services.AddControllers() .AddJsonOptions(opt => @@ -37,10 +36,10 @@ public static void Main(string[] args) builder.Services.AddEndpointsApiExplorer(); - //Extensions + //Configure Extensions builder.Logging.AddLogExtension(builder.Configuration); builder.Services.AddAuthExtension(builder.Configuration); - builder.Services.AddKeyVaultExtension(builder.Configuration); + builder.Services.AddKeyVaultExtension(builder.Configuration); builder.Services.AddDatabaseExtension(builder.Configuration); builder.Services.AddTelemetriaExtension(builder.Configuration); builder.Services.AddSwaggerExtension(); @@ -48,7 +47,7 @@ public static void Main(string[] args) var app = builder.Build(); - //Extensions + //Use Extensions app.UseSwaggerExtension(); app.UseHttpsRedirection(); diff --git a/src/DEPLOY.Cachorro.Application/AppServices/AdocaoAppService.cs b/src/DEPLOY.Cachorro.Application/AppServices/AdocaoAppService.cs index 3c732e9..e5db92e 100644 --- a/src/DEPLOY.Cachorro.Application/AppServices/AdocaoAppService.cs +++ b/src/DEPLOY.Cachorro.Application/AppServices/AdocaoAppService.cs @@ -14,24 +14,24 @@ public AdocaoAppService(IAdocaoService adocaoService) _adocaoService = adocaoService; } - //https://github.com/altmann/FluentResults - public async Task AdotarAsync( + public async Task> AdotarAsync( Guid cachorroId, long tutorId, CancellationToken cancellationToken = default) { - await _adocaoService.AdotarAsync( + return await _adocaoService.AdotarAsync( cachorroId, tutorId, cancellationToken); } - public Task DevolverAdocaoAsync( + public Task> DevolverAdocaoAsync( Guid cachorroId, - long TutorId, CancellationToken cancellationToken = default) { - throw new NotImplementedException(); + return _adocaoService.DevolverAdocaoAsync( + cachorroId, + cancellationToken); } } } diff --git a/src/DEPLOY.Cachorro.Application/AppServices/CachorroAppServices.cs b/src/DEPLOY.Cachorro.Application/AppServices/CachorroAppServices.cs index d85273f..a2856eb 100644 --- a/src/DEPLOY.Cachorro.Application/AppServices/CachorroAppServices.cs +++ b/src/DEPLOY.Cachorro.Application/AppServices/CachorroAppServices.cs @@ -2,6 +2,7 @@ using DEPLOY.Cachorro.Application.Interfaces.Services; using DEPLOY.Cachorro.Domain.Aggregates.Cachorro.Interfaces.Services; using System.Diagnostics.CodeAnalysis; +using System.Linq.Expressions; namespace DEPLOY.Cachorro.Application.AppServices { @@ -25,31 +26,50 @@ public async Task DeleteAsync( public async Task> GetAllAsync( CancellationToken cancellationToken = default) { - var retorno = await _cachorroService.GetAllAsync(cancellationToken); + var retorno = await _cachorroService.GetAllAsync( + cancellationToken); - return retorno.Select(x => x!).ToList(); + return retorno + .Select(x => x!).ToList(); } public async Task GetByIdAsync( Guid id, CancellationToken cancellationToken = default) { - return await _cachorroService.GetByIdAsync(id, cancellationToken); + return await _cachorroService.GetByIdAsync( + id, + cancellationToken); } - public async Task InsertAsync( + + public async Task> GetByKeyAsync( + Expression> predicate, + CancellationToken cancellationToken = default) + { + var item = await _cachorroService.GetByKeyAsync(predicate, cancellationToken); + + return item.Select(x => (CachorroDto)x!).ToList(); + } + + public async Task InsertAsync( CachorroCreateDto cachorroDto, CancellationToken cancellationToken = default) { - return await _cachorroService.CreateAsync(cachorroDto, cancellationToken); + return await _cachorroService.CreateAsync( + cachorroDto, + cancellationToken); } - public async Task UpdateAsync( + public async Task> UpdateAsync( Guid id, CachorroDto cachorroDto, CancellationToken cancellationToken = default) { - return await _cachorroService.UpdateAsync(id, cachorroDto, cancellationToken); + return await _cachorroService.UpdateAsync( + id, + cachorroDto, + cancellationToken); } } } diff --git a/src/DEPLOY.Cachorro.Application/AppServices/TutorAppServices.cs b/src/DEPLOY.Cachorro.Application/AppServices/TutorAppServices.cs index 3ea1d7a..a776aac 100644 --- a/src/DEPLOY.Cachorro.Application/AppServices/TutorAppServices.cs +++ b/src/DEPLOY.Cachorro.Application/AppServices/TutorAppServices.cs @@ -1,9 +1,11 @@ using DEPLOY.Cachorro.Application.Dtos; using DEPLOY.Cachorro.Application.Interfaces.Services; using DEPLOY.Cachorro.Domain.Aggregates.Tutor.Interfaces.Services; +using System.Diagnostics.CodeAnalysis; namespace DEPLOY.Cachorro.Application.AppServices { + [ExcludeFromCodeCoverage] public class TutorAppServices : ITutorAppServices { private readonly ITutorService _tutorService; @@ -13,42 +15,53 @@ public TutorAppServices(ITutorService tutorService) _tutorService = tutorService; } - public async Task DeleteAsync(long id, + public async Task DeleteAsync( + long id, CancellationToken cancellationToken = default) { - return await _tutorService.DeleteAsync(id); + return await _tutorService.DeleteAsync( + id, + cancellationToken); } public async Task> GetAllAsync( CancellationToken cancellationToken = default) { - var retorno = await _tutorService.GetAllAsync(); + var retorno = await _tutorService.GetAllAsync( + cancellationToken); - return retorno.Select(x => x!).ToList(); + return retorno + .Select(x => x!).ToList(); } public async Task GetByIdAsync( long id, CancellationToken cancellationToken = default) { - return await _tutorService.GetByIdAsync(id); + return await _tutorService.GetByIdAsync(id, + cancellationToken); } - public async Task InsertAsync( - TutorDto TutorDto, + public async Task InsertAsync( + TutorDto tutorDto, CancellationToken cancellationToken = default) { - var item = await _tutorService.CreateAsync(TutorDto); + var item = await _tutorService.CreateAsync( + tutorDto, + cancellationToken); return item; } - public async Task UpdateAsync( + public async Task> UpdateAsync( long id, - TutorDto TutorDto, + TutorDto tutorDto, CancellationToken cancellationToken = default) { - return await _tutorService.UpdateAsync(id, TutorDto); + return await _tutorService.UpdateAsync( + id, + tutorDto, + cancellationToken); } } } diff --git a/src/DEPLOY.Cachorro.Application/DEPLOY.Cachorro.Application.csproj b/src/DEPLOY.Cachorro.Application/DEPLOY.Cachorro.Application.csproj index 61a8330..4f18e2f 100644 --- a/src/DEPLOY.Cachorro.Application/DEPLOY.Cachorro.Application.csproj +++ b/src/DEPLOY.Cachorro.Application/DEPLOY.Cachorro.Application.csproj @@ -1,17 +1,18 @@  - - net8.0 - enable - enable - + + net8.0 + enable + enable + $(NoWarn);8603 + - - - + + + - - - + + + diff --git a/src/DEPLOY.Cachorro.Application/Dtos/BaseDto.cs b/src/DEPLOY.Cachorro.Application/Dtos/BaseDto.cs index a7cd336..1041108 100644 --- a/src/DEPLOY.Cachorro.Application/Dtos/BaseDto.cs +++ b/src/DEPLOY.Cachorro.Application/Dtos/BaseDto.cs @@ -1,5 +1,4 @@ -using Swashbuckle.AspNetCore.Annotations; -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization; namespace DEPLOY.Cachorro.Application.Dtos diff --git a/src/DEPLOY.Cachorro.Application/Dtos/CachorroDto.cs b/src/DEPLOY.Cachorro.Application/Dtos/CachorroDto.cs index 46d192d..566162e 100644 --- a/src/DEPLOY.Cachorro.Application/Dtos/CachorroDto.cs +++ b/src/DEPLOY.Cachorro.Application/Dtos/CachorroDto.cs @@ -16,7 +16,7 @@ public record CachorroDto( float Peso, TutorDto? Tutor, [SwaggerSchema(ReadOnly = true)] - IEnumerable Erros = null) : BaseDto(Erros) + IEnumerable Erros) : BaseDto(Erros) { public static implicit operator Domain.Aggregates.Cachorro.Entities.Cachorro(CachorroDto dto) => new Domain.Aggregates.Cachorro.Entities.Cachorro( @@ -27,10 +27,9 @@ public static implicit operator Domain.Aggregates.Cachorro.Entities.Cachorro(Cac dto.Nascimento, dto.Adotado, dto.Pelagem, - dto.Peso - ); + dto.Peso); - public static implicit operator CachorroDto?(Domain.Aggregates.Cachorro.Entities.Cachorro entity) => + public static implicit operator CachorroDto?(Domain.Aggregates.Cachorro.Entities.Cachorro? entity) => entity == null ? null : new CachorroDto( entity.Id, diff --git a/src/DEPLOY.Cachorro.Application/Dtos/TutorDto.cs b/src/DEPLOY.Cachorro.Application/Dtos/TutorDto.cs index 754af2f..018aed0 100644 --- a/src/DEPLOY.Cachorro.Application/Dtos/TutorDto.cs +++ b/src/DEPLOY.Cachorro.Application/Dtos/TutorDto.cs @@ -10,7 +10,7 @@ public record TutorDto( string Nome, DateTime Cadastro, DateTime? Atualizacao, - long CPF, + string CPF, [SwaggerSchema(ReadOnly = true)] IEnumerable Erros = null) : BaseDto(Erros) { @@ -22,7 +22,7 @@ public static implicit operator Tutor(TutorDto dto) => dto.Atualizacao, dto.CPF); - public static implicit operator TutorDto?(Tutor entity) => + public static implicit operator TutorDto?(Tutor? entity) => entity == null ? null : new TutorDto( entity.Id, diff --git a/src/DEPLOY.Cachorro.Application/Interfaces/Services/IAdocaoAppService.cs b/src/DEPLOY.Cachorro.Application/Interfaces/Services/IAdocaoAppService.cs index 80310a2..77fb0df 100644 --- a/src/DEPLOY.Cachorro.Application/Interfaces/Services/IAdocaoAppService.cs +++ b/src/DEPLOY.Cachorro.Application/Interfaces/Services/IAdocaoAppService.cs @@ -4,8 +4,13 @@ namespace DEPLOY.Cachorro.Application.Interfaces.Services { public interface IAdocaoAppService { - Task AdotarAsync(Guid cachorroId, long TutorId, CancellationToken cancellationToken = default); + Task> AdotarAsync( + Guid cachorroId, + long tutorId, + CancellationToken cancellationToken = default); - Task DevolverAdocaoAsync(Guid cachorroId, long TutorId, CancellationToken cancellationToken = default); + Task> DevolverAdocaoAsync( + Guid cachorroId, + CancellationToken cancellationToken = default); } } diff --git a/src/DEPLOY.Cachorro.Application/Interfaces/Services/ICachorroAppServices.cs b/src/DEPLOY.Cachorro.Application/Interfaces/Services/ICachorroAppServices.cs index 6fa6b2f..61aedfd 100644 --- a/src/DEPLOY.Cachorro.Application/Interfaces/Services/ICachorroAppServices.cs +++ b/src/DEPLOY.Cachorro.Application/Interfaces/Services/ICachorroAppServices.cs @@ -1,16 +1,15 @@ using DEPLOY.Cachorro.Application.Dtos; -using System.Diagnostics.CodeAnalysis; -using System.Threading; +using System.Linq.Expressions; namespace DEPLOY.Cachorro.Application.Interfaces.Services { public interface ICachorroAppServices { public Task InsertAsync( - CachorroCreateDto cachorroDto, + CachorroCreateDto cachorroDto, CancellationToken cancellationToken = default); - public Task UpdateAsync( + public Task> UpdateAsync( Guid id, CachorroDto cachorroDto, CancellationToken cancellationToken = default); @@ -25,5 +24,10 @@ public Task DeleteAsync( public Task GetByIdAsync( Guid id, CancellationToken cancellationToken = default); + + Task> GetByKeyAsync( + Expression> predicate, + CancellationToken cancellationToken = default); + } } diff --git a/src/DEPLOY.Cachorro.Application/Interfaces/Services/ITutorAppServices.cs b/src/DEPLOY.Cachorro.Application/Interfaces/Services/ITutorAppServices.cs index 877498b..197bf50 100644 --- a/src/DEPLOY.Cachorro.Application/Interfaces/Services/ITutorAppServices.cs +++ b/src/DEPLOY.Cachorro.Application/Interfaces/Services/ITutorAppServices.cs @@ -8,7 +8,7 @@ public Task InsertAsync( TutorDto tutorDto, CancellationToken cancellationToken = default); - public Task UpdateAsync( + public Task> UpdateAsync( long id, TutorDto tutorDto, CancellationToken cancellationToken = default); diff --git a/src/DEPLOY.Cachorro.Domain/Aggregates/Adotar/Interfaces/Service/IAdocaoService.cs b/src/DEPLOY.Cachorro.Domain/Aggregates/Adotar/Interfaces/Service/IAdocaoService.cs index a58c3b5..b96e465 100644 --- a/src/DEPLOY.Cachorro.Domain/Aggregates/Adotar/Interfaces/Service/IAdocaoService.cs +++ b/src/DEPLOY.Cachorro.Domain/Aggregates/Adotar/Interfaces/Service/IAdocaoService.cs @@ -2,8 +2,13 @@ { public interface IAdocaoService { - Task AdotarAsync(Guid cachorroId, long TutorId, CancellationToken cancellationToken = default); + Task> AdotarAsync( + Guid cachorroId, + long tutorId, + CancellationToken cancellationToken = default); - Task DevolverAdocaoAsync(Guid cachorroId, long TutorId, CancellationToken cancellationToken = default); + Task> DevolverAdocaoAsync( + Guid cachorroId, + CancellationToken cancellationToken = default); } } diff --git a/src/DEPLOY.Cachorro.Domain/Aggregates/Adotar/Services/AdocaoService.cs b/src/DEPLOY.Cachorro.Domain/Aggregates/Adotar/Services/AdocaoService.cs index 8973c33..64dc4c7 100644 --- a/src/DEPLOY.Cachorro.Domain/Aggregates/Adotar/Services/AdocaoService.cs +++ b/src/DEPLOY.Cachorro.Domain/Aggregates/Adotar/Services/AdocaoService.cs @@ -2,6 +2,7 @@ using DEPLOY.Cachorro.Domain.Aggregates.Cachorro.Interfaces.Repositories; using DEPLOY.Cachorro.Domain.Aggregates.Tutor.Interfaces.Repositories; using DEPLOY.Cachorro.Infra.Repository.Repositories.Base; +using System.Collections.Frozen; namespace DEPLOY.Cachorro.Domain.Aggregates.Adotar.Services { @@ -10,6 +11,7 @@ public class AdocaoService : IAdocaoService private readonly IUnitOfWork _uow; private readonly ICachorroRepository _cachorroRepository; private readonly ITutorRepository _tutorRepository; + private readonly IList _validation; public AdocaoService( IUnitOfWork uow, @@ -19,35 +21,74 @@ public AdocaoService( _uow = uow; _cachorroRepository = cachorroRepository; _tutorRepository = tutorRepository; + _validation = new List(3); } - //https://github.com/altmann/FluentResults - public async Task AdotarAsync( + public async Task> AdotarAsync( Guid cachorroId, long tutorId, CancellationToken cancellationToken = default) { var existedCachorro = await _cachorroRepository.GetByIdAsync(cachorroId, cancellationToken); - // capturar erro de cachorro não encontrado + + if (existedCachorro is null) + { + _validation.Add("Cachorro não encontrado"); + } + else if (existedCachorro.Tutor is not null) + { + _validation.Add("Cachorro já adotado"); + } var existedTutor = await _tutorRepository.GetByIdAsync(tutorId, cancellationToken); - // capturar erro de tutor não encontrado - //retornar validacao + if (existedTutor is null) + { + _validation.Add("Tutor não encontrado"); + } + + if (_validation.Count > 0) + { + return _validation; + } - existedCachorro.Adotar(existedTutor); + existedCachorro?.Adotar(existedTutor); await _cachorroRepository.UpdateAsync(existedCachorro); await _uow.CommitAndSaveChangesAsync(cancellationToken); + + return _validation; } - public Task DevolverAdocaoAsync( + + public async Task> DevolverAdocaoAsync( Guid cachorroId, - long TutorId, CancellationToken cancellationToken = default) { - throw new NotImplementedException(); + var existedCachorro = await _cachorroRepository.GetByIdAsync(cachorroId, cancellationToken); + + if (existedCachorro is null) + { + _validation.Add("Cachorro não encontrado"); + } + else if (existedCachorro.Tutor is null) + { + _validation.Add("Cachorro não consta como adotado no sistema"); + } + + if (_validation?.Count() > 0) + { + return _validation.ToFrozenSet(); + } + + existedCachorro.Devolver(); + + await _cachorroRepository.UpdateAsync(existedCachorro, cancellationToken); + + await _uow.CommitAndSaveChangesAsync(cancellationToken); + + return _validation; } } } diff --git a/src/DEPLOY.Cachorro.Domain/Aggregates/Adotar/Validations/AdocaoValidator.cs b/src/DEPLOY.Cachorro.Domain/Aggregates/Adotar/Validations/AdocaoValidator.cs new file mode 100644 index 0000000..973f263 --- /dev/null +++ b/src/DEPLOY.Cachorro.Domain/Aggregates/Adotar/Validations/AdocaoValidator.cs @@ -0,0 +1,13 @@ +using FluentValidation; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DEPLOY.Cachorro.Domain.Aggregates.Adotar.Validations +{ + internal class AdocaoValidator //: AbstractValidator<> + { + } +} diff --git a/src/DEPLOY.Cachorro.Domain/Aggregates/Cachorro/Entities/Cachorro.cs b/src/DEPLOY.Cachorro.Domain/Aggregates/Cachorro/Entities/Cachorro.cs index bd7ab19..f2156ca 100644 --- a/src/DEPLOY.Cachorro.Domain/Aggregates/Cachorro/Entities/Cachorro.cs +++ b/src/DEPLOY.Cachorro.Domain/Aggregates/Cachorro/Entities/Cachorro.cs @@ -26,6 +26,7 @@ public Cachorro( Adotado = adotado; Pelagem = pelagem; Peso = peso; + Tutor = tutor; } public Cachorro( @@ -70,16 +71,18 @@ public Cachorro( public Tutor.Entities.Tutor? Tutor { get; set; } - public void Adotar(Tutor.Entities.Tutor tutor) + internal void Adotar(Tutor.Entities.Tutor tutor) { Atualizacao = DateTime.Now; Tutor = tutor; Adotado = true; } - internal void UpdateNascimento(DateTime nascimento) + internal void Devolver() { - Nascimento = nascimento; + Atualizacao = DateTime.Now; + Tutor = null; + Adotado = false; } } } diff --git a/src/DEPLOY.Cachorro.Domain/Aggregates/Cachorro/Services/CachorroService.cs b/src/DEPLOY.Cachorro.Domain/Aggregates/Cachorro/Services/CachorroService.cs index 2fa7cf5..df1ff0b 100644 --- a/src/DEPLOY.Cachorro.Domain/Aggregates/Cachorro/Services/CachorroService.cs +++ b/src/DEPLOY.Cachorro.Domain/Aggregates/Cachorro/Services/CachorroService.cs @@ -13,7 +13,6 @@ public CachorroService( IValidator validator, ICachorroRepository cachorroRepository) : base(unitOfWork, validator, cachorroRepository) { - } } } diff --git a/src/DEPLOY.Cachorro.Domain/Aggregates/Cachorro/Validations/CachorroValidator.cs b/src/DEPLOY.Cachorro.Domain/Aggregates/Cachorro/Validations/CachorroValidator.cs index 32f0aad..bc0b044 100644 --- a/src/DEPLOY.Cachorro.Domain/Aggregates/Cachorro/Validations/CachorroValidator.cs +++ b/src/DEPLOY.Cachorro.Domain/Aggregates/Cachorro/Validations/CachorroValidator.cs @@ -1,4 +1,6 @@ -using FluentValidation; +using DEPLOY.Cachorro.Domain.Aggregates.Cachorro.Interfaces.Repositories; +using DEPLOY.Cachorro.Domain.Aggregates.Tutor.Interfaces.Repositories; +using FluentValidation; using System.Diagnostics.CodeAnalysis; namespace DEPLOY.Cachorro.Domain.Aggregates.Cachorro.Validations @@ -6,14 +8,30 @@ namespace DEPLOY.Cachorro.Domain.Aggregates.Cachorro.Validations [ExcludeFromCodeCoverage] public class CachorroValidator : AbstractValidator { - public CachorroValidator() + private readonly ICachorroRepository _cachorroRepository; + + public CachorroValidator(ICachorroRepository cachorroRepository) { + _cachorroRepository = cachorroRepository; + RuleSet("CreateNew", () => { RuleFor(x => x.Nome) .NotEmpty() .WithMessage("Nome é obrigatório"); + RuleFor(x => x.Nome) + .MustAsync(async (id, cancellationToken) => + { + var item = await _cachorroRepository.GetByKeyAsync(t => t.Nome == id); + + if (item.Count == 0) + return true; + + return false; + }) + .WithMessage("Já existe um cachorro com o nome informado"); + RuleFor(x => x.Nome) .MaximumLength(100) .WithMessage("Nome deve ter no máximo 100 caracteres"); diff --git a/src/DEPLOY.Cachorro.Domain/Aggregates/Tutor/Entities/Tutor.cs b/src/DEPLOY.Cachorro.Domain/Aggregates/Tutor/Entities/Tutor.cs index 8abc17b..7c1df5d 100644 --- a/src/DEPLOY.Cachorro.Domain/Aggregates/Tutor/Entities/Tutor.cs +++ b/src/DEPLOY.Cachorro.Domain/Aggregates/Tutor/Entities/Tutor.cs @@ -1,6 +1,5 @@ using System.Diagnostics.CodeAnalysis; using DEPLOY.Cachorro.Domain.Shared; - namespace DEPLOY.Cachorro.Domain.Aggregates.Tutor.Entities { [ExcludeFromCodeCoverage] @@ -11,7 +10,7 @@ public Tutor( string nome, DateTime cadastro, DateTime? atualizacao, - long cPF) : base(id, nome, cadastro, atualizacao) + string cPF) : base(id, nome, cadastro, atualizacao) { Id = id; Nome = nome; @@ -20,6 +19,6 @@ public Tutor( CPF = cPF; } - public long CPF { get; set; } + public string CPF { get; set; } } } diff --git a/src/DEPLOY.Cachorro.Domain/Aggregates/Tutor/Services/TutorService.cs b/src/DEPLOY.Cachorro.Domain/Aggregates/Tutor/Services/TutorService.cs index 20ffd62..3d500b4 100644 --- a/src/DEPLOY.Cachorro.Domain/Aggregates/Tutor/Services/TutorService.cs +++ b/src/DEPLOY.Cachorro.Domain/Aggregates/Tutor/Services/TutorService.cs @@ -3,6 +3,7 @@ using DEPLOY.Cachorro.Domain.Shared; using DEPLOY.Cachorro.Infra.Repository.Repositories.Base; using FluentValidation; +using System.Security.Cryptography.X509Certificates; namespace DEPLOY.Cachorro.Domain.Aggregates.Tutor.Services { diff --git a/src/DEPLOY.Cachorro.Domain/Aggregates/Tutor/Validations/TutorValidator.cs b/src/DEPLOY.Cachorro.Domain/Aggregates/Tutor/Validations/TutorValidator.cs index 6aa88ec..3399d1a 100644 --- a/src/DEPLOY.Cachorro.Domain/Aggregates/Tutor/Validations/TutorValidator.cs +++ b/src/DEPLOY.Cachorro.Domain/Aggregates/Tutor/Validations/TutorValidator.cs @@ -1,4 +1,5 @@ -using FluentValidation; +using DEPLOY.Cachorro.Domain.Aggregates.Tutor.Interfaces.Repositories; +using FluentValidation; using System.Diagnostics.CodeAnalysis; namespace DEPLOY.Cachorro.Domain.Aggregates.Tutor.Validations @@ -6,14 +7,30 @@ namespace DEPLOY.Cachorro.Domain.Aggregates.Tutor.Validations [ExcludeFromCodeCoverage] public class TutorValidator : AbstractValidator { - public TutorValidator() + public readonly ITutorRepository _tutorRepository; + + public TutorValidator(ITutorRepository tutorRepository) { + _tutorRepository = tutorRepository; + RuleSet("CreateNew", () => { RuleFor(x => x.Nome) .NotEmpty() .WithMessage("Nome é obrigatório"); + RuleFor(x => x.Nome) + .MustAsync(async (id, cancellation) => + { + var item = await _tutorRepository.GetByKeyAsync(t => t.Nome == id); + + if (item.Count == 0) + return true; + + return false; + }) + .WithMessage("Nome já existe na base"); + RuleFor(x => x.Nome) .MaximumLength(100) .WithMessage("Nome deve ter no máximo 100 caracteres"); @@ -25,6 +42,61 @@ public TutorValidator() RuleFor(x => x.CPF.ToString().PadLeft(11,'0')) .Must(IsCpf) .WithMessage("CPF inválido"); + + RuleFor(x => x.CPF.ToString().PadLeft(11, '0')) + .MustAsync(async (id, cancellation) => + { + var item = await _tutorRepository.GetByKeyAsync(t => t.CPF == id); + + if (item.Count == 0) + return true; + + return false; + }) + .WithMessage("CPF já cadastrado"); + }); + + RuleSet("Update", () => + { + RuleFor(x => x.Nome) + .NotEmpty() + .WithMessage("Nome é obrigatório"); + + RuleFor(x => x.Nome) + .MustAsync(async (id, cancellation) => + { + var item = await _tutorRepository.GetByKeyAsync(t => t.Nome == id); + + if (item.Count == 0) + return true; + + return false; + }) + .WithMessage("Está tentando alterar para um Nome que já existe"); + + RuleFor(x => x.Nome) + .MaximumLength(100) + .WithMessage("Nome deve ter no máximo 100 caracteres"); + + RuleFor(x => x.CPF) + .NotEmpty() + .WithMessage("CPF é obrigatório"); + + RuleFor(x => x.CPF.ToString().PadLeft(11, '0')) + .Must(IsCpf) + .WithMessage("CPF inválido"); + + RuleFor(x => x.CPF.ToString().PadLeft(11, '0')) + .MustAsync(async (id, cancellation) => + { + var item = await _tutorRepository.GetByKeyAsync(t => t.CPF == id); + + if (item.Count == 0) + return true; + + return false; + }) + .WithMessage("Está tentando alterar para um CPF que já existe"); }); } diff --git a/src/DEPLOY.Cachorro.Domain/DEPLOY.Cachorro.Domain.csproj b/src/DEPLOY.Cachorro.Domain/DEPLOY.Cachorro.Domain.csproj index 55301f9..e40ca07 100644 --- a/src/DEPLOY.Cachorro.Domain/DEPLOY.Cachorro.Domain.csproj +++ b/src/DEPLOY.Cachorro.Domain/DEPLOY.Cachorro.Domain.csproj @@ -1,19 +1,22 @@ - + - - net8.0 - enable - enable - True - + + net8.0 + enable + enable + True + $(NoWarn);8618 + $(NoWarn);8604 + - - - + + + - - - - + + + + + diff --git a/src/DEPLOY.Cachorro.Domain/Shared/GenericService.cs b/src/DEPLOY.Cachorro.Domain/Shared/GenericService.cs index 271166f..068e5c7 100644 --- a/src/DEPLOY.Cachorro.Domain/Shared/GenericService.cs +++ b/src/DEPLOY.Cachorro.Domain/Shared/GenericService.cs @@ -1,6 +1,8 @@ using DEPLOY.Cachorro.Infra.Repository.Repositories.Base; using FluentValidation; +using System.Collections.Frozen; using System.Diagnostics.CodeAnalysis; +using System.Linq.Expressions; namespace DEPLOY.Cachorro.Domain.Shared { @@ -38,7 +40,7 @@ public virtual async Task CreateAsync( return entity; } - await _genericRepository.InsertAsync(entity); + await _genericRepository.InsertAsync(entity, cancellationToken); await _uow.CommitAndSaveChangesAsync(cancellationToken); @@ -49,7 +51,7 @@ public virtual async Task DeleteAsync( Tid id, CancellationToken cancellationToken = default) { - var item = await _genericRepository.DeleteAsync(id); + var item = await _genericRepository.DeleteAsync(id, cancellationToken); if (!item) return false; @@ -57,7 +59,7 @@ public virtual async Task DeleteAsync( return await _uow.CommitAndSaveChangesAsync(cancellationToken); } - public virtual async Task UpdateAsync( + public virtual async Task> UpdateAsync( Tid id, TEntity entity, CancellationToken cancellationToken = default) @@ -67,33 +69,41 @@ public virtual async Task UpdateAsync( if (!validated.IsValid) { - entity.Erros = validated.Errors.Select(x => x.ErrorMessage).ToList(); - return false; + return validated.Errors.Select(x => x.ErrorMessage).ToList(); } - var item = await _genericRepository.GetByIdAsync(id); + var item = await _genericRepository.GetByIdAsync(id, cancellationToken); if (item == null) - return false; + return entity.Erros.ToFrozenSet(); - await _genericRepository.UpdateAsync(entity); + await _genericRepository.UpdateAsync(entity, cancellationToken); - return await _uow.CommitAndSaveChangesAsync(cancellationToken); + await _uow.CommitAndSaveChangesAsync(cancellationToken); + + return entity.Erros; } public virtual async Task> GetAllAsync( CancellationToken cancellationToken = default) { - var item = await _genericRepository.GetAllAsync(); + var items = await _genericRepository.GetAllAsync(cancellationToken); - return item.Any() ? item : Enumerable.Empty(); + return items?.Count() > 0 ? items : Enumerable.Empty(); } public virtual async Task GetByIdAsync( Tid id, CancellationToken cancellationToken = default) { - return await _genericRepository.GetByIdAsync(id); + return await _genericRepository.GetByIdAsync(id, cancellationToken); + } + + public Task> GetByKeyAsync( + Expression> predicate, + CancellationToken cancellationToken = default) + { + return _genericRepository.GetByKeyAsync(predicate, cancellationToken); } } } diff --git a/src/DEPLOY.Cachorro.Domain/Shared/IGenericRepository.cs b/src/DEPLOY.Cachorro.Domain/Shared/IGenericRepository.cs index 3f3f06b..0f989ce 100644 --- a/src/DEPLOY.Cachorro.Domain/Shared/IGenericRepository.cs +++ b/src/DEPLOY.Cachorro.Domain/Shared/IGenericRepository.cs @@ -1,3 +1,5 @@ +using System.Linq.Expressions; + namespace DEPLOY.Cachorro.Domain.Shared { public interface IGenericRepository where TEntity : BaseEntity @@ -10,14 +12,17 @@ Task> GetAllAsync( CancellationToken cancellationToken = default); Task InsertAsync( - TEntity obj, + TEntity entity, CancellationToken cancellationToken = default); - Task UpdateAsync(TEntity obj, + Task UpdateAsync(TEntity entity, CancellationToken cancellationToken = default); - Task DeleteAsync(Tid id, + Task DeleteAsync( + Tid id, CancellationToken cancellationToken = default); - //Task> GetByKey(Func predicate); + Task> GetByKeyAsync( + Expression> predicate, + CancellationToken cancellationToken = default); } } \ No newline at end of file diff --git a/src/DEPLOY.Cachorro.Domain/Shared/IGenericService.cs b/src/DEPLOY.Cachorro.Domain/Shared/IGenericService.cs index c2a73bc..1093cdf 100644 --- a/src/DEPLOY.Cachorro.Domain/Shared/IGenericService.cs +++ b/src/DEPLOY.Cachorro.Domain/Shared/IGenericService.cs @@ -1,4 +1,6 @@ -namespace DEPLOY.Cachorro.Domain.Shared +using System.Linq.Expressions; + +namespace DEPLOY.Cachorro.Domain.Shared { public interface IGenericService where TEntity : BaseEntity { @@ -17,9 +19,13 @@ Task> GetAllAsync( Tid id, CancellationToken cancellationToken = default); - Task UpdateAsync( + Task> UpdateAsync( Tid id, TEntity entity, CancellationToken cancellationToken = default); + + Task> GetByKeyAsync( + Expression> predicate, + CancellationToken cancellationToken = default); } } \ No newline at end of file diff --git a/src/DEPLOY.Cachorro.Infra.CrossCutting/DEPLOY.Cachorro.Infra.CrossCutting.csproj b/src/DEPLOY.Cachorro.Infra.CrossCutting/DEPLOY.Cachorro.Infra.CrossCutting.csproj index 602beba..1c2bf0d 100644 --- a/src/DEPLOY.Cachorro.Infra.CrossCutting/DEPLOY.Cachorro.Infra.CrossCutting.csproj +++ b/src/DEPLOY.Cachorro.Infra.CrossCutting/DEPLOY.Cachorro.Infra.CrossCutting.csproj @@ -1,19 +1,21 @@  - - net8.0 - enable - enable - + + net8.0 + enable + enable + - - - + + + + + - - - - - + + + + + diff --git a/src/DEPLOY.Cachorro.Infra.CrossCutting/IoC.cs b/src/DEPLOY.Cachorro.Infra.CrossCutting/IoC.cs index 4aa5f96..5e83da1 100644 --- a/src/DEPLOY.Cachorro.Infra.CrossCutting/IoC.cs +++ b/src/DEPLOY.Cachorro.Infra.CrossCutting/IoC.cs @@ -11,12 +11,18 @@ using DEPLOY.Cachorro.Infra.Repository.Repositories.Base; using FluentValidation; using Microsoft.Extensions.DependencyInjection; +using Polly; +using Polly.Timeout; +using System.Diagnostics.CodeAnalysis; +using System.Net; +using System.Net.Http.Headers; namespace DEPLOY.Cachorro.Infra.CrossCutting { + [ExcludeFromCodeCoverage] public static class IoC { - public static void RegisterServices(this IServiceCollection services) + public static void AddRegisterServices(this IServiceCollection services) { services.AddScoped(); services.AddScoped(); diff --git a/src/DEPLOY.Cachorro.Infra.Repository/DEPLOY.Cachorro.Infra.Repository.csproj b/src/DEPLOY.Cachorro.Infra.Repository/DEPLOY.Cachorro.Infra.Repository.csproj index c5fba20..efe6d97 100644 --- a/src/DEPLOY.Cachorro.Infra.Repository/DEPLOY.Cachorro.Infra.Repository.csproj +++ b/src/DEPLOY.Cachorro.Infra.Repository/DEPLOY.Cachorro.Infra.Repository.csproj @@ -1,31 +1,32 @@  - - net8.0 - enable - enable - + + net8.0 + enable + enable + $(NoWarn);8601 + - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + - - - + + + - - - - + + + + diff --git a/src/DEPLOY.Cachorro.Infra.Repository/DbContext/CachorroDbContext.cs b/src/DEPLOY.Cachorro.Infra.Repository/DbContext/CachorroDbContext.cs index 49f84d6..9ce8884 100644 --- a/src/DEPLOY.Cachorro.Infra.Repository/DbContext/CachorroDbContext.cs +++ b/src/DEPLOY.Cachorro.Infra.Repository/DbContext/CachorroDbContext.cs @@ -12,7 +12,7 @@ public CachorroDbContext(DbContextOptions options) : { } - public CachorroDbContext() + protected CachorroDbContext() { } diff --git a/src/DEPLOY.Cachorro.Infra.Repository/EntityConfigurations/CachorroEntityConfiguration.cs b/src/DEPLOY.Cachorro.Infra.Repository/EntityConfigurations/CachorroEntityConfiguration.cs index 7369a93..4fa039b 100644 --- a/src/DEPLOY.Cachorro.Infra.Repository/EntityConfigurations/CachorroEntityConfiguration.cs +++ b/src/DEPLOY.Cachorro.Infra.Repository/EntityConfigurations/CachorroEntityConfiguration.cs @@ -1,11 +1,12 @@ -using DEPLOY.Cachorro.Domain; using DEPLOY.Cachorro.Domain.Aggregates.Cachorro.ValueObject; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata.Builders; +using System.Diagnostics.CodeAnalysis; namespace DEPLOY.Cachorro.Infra.Repository.EntityConfigurations { + [ExcludeFromCodeCoverage] internal class CachorroEntityConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) @@ -86,8 +87,7 @@ public void Configure(EntityTypeBuilder { public void Configure(EntityTypeBuilder builder) @@ -17,15 +19,15 @@ public void Configure(EntityTypeBuilder builder) builder .Property(x => x.Id) - .UseIdentityColumn(seed: 1,increment: 1) + .UseIdentityColumn() .HasColumnName("Id") - .HasColumnType("int") + .HasColumnType("bigint") .IsRequired(); builder .Property(x => x.CPF) .HasColumnName("CPF") - .HasColumnType("bigint") + .HasColumnType("varchar(11)") .IsRequired(); builder diff --git a/src/DEPLOY.Cachorro.Infra.Repository/Migrations/API/20240317120035_InitDatabaseAPI.Designer.cs b/src/DEPLOY.Cachorro.Infra.Repository/Migrations/API/20240408230334_InitDatabaseAPI.Designer.cs similarity index 85% rename from src/DEPLOY.Cachorro.Infra.Repository/Migrations/API/20240317120035_InitDatabaseAPI.Designer.cs rename to src/DEPLOY.Cachorro.Infra.Repository/Migrations/API/20240408230334_InitDatabaseAPI.Designer.cs index 344a05a..a4eb265 100644 --- a/src/DEPLOY.Cachorro.Infra.Repository/Migrations/API/20240317120035_InitDatabaseAPI.Designer.cs +++ b/src/DEPLOY.Cachorro.Infra.Repository/Migrations/API/20240408230334_InitDatabaseAPI.Designer.cs @@ -12,7 +12,7 @@ namespace DEPLOY.Cachorro.Infra.Repository.Migrations.API { [DbContext(typeof(CachorroDbContext))] - [Migration("20240317120035_InitDatabaseAPI")] + [Migration("20240408230334_InitDatabaseAPI")] partial class InitDatabaseAPI { /// @@ -64,8 +64,8 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("decimal(6,2)") .HasColumnName("Peso"); - b.Property("TutorId") - .HasColumnType("int"); + b.Property("TutorId") + .HasColumnType("bigint"); b.HasKey("Id"); @@ -80,11 +80,11 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.HasData( new { - Id = new Guid("ac9b57e9-c326-45d2-bc48-853a146be964"), + Id = new Guid("4b7f75cc-bad0-4c5e-87ba-70178ab9fe13"), Adotado = false, - Atualizacao = new DateTime(2024, 3, 17, 9, 0, 35, 399, DateTimeKind.Local).AddTicks(5238), - Cadastro = new DateTime(2024, 3, 17, 9, 0, 35, 399, DateTimeKind.Local).AddTicks(5220), - Nascimento = new DateTime(2024, 3, 17, 9, 0, 35, 399, DateTimeKind.Local).AddTicks(5242), + Atualizacao = new DateTime(2024, 4, 8, 20, 3, 33, 613, DateTimeKind.Local).AddTicks(1770), + Cadastro = new DateTime(2024, 4, 8, 20, 3, 33, 613, DateTimeKind.Local).AddTicks(1739), + Nascimento = new DateTime(2024, 4, 8, 20, 3, 33, 613, DateTimeKind.Local).AddTicks(1776), Nome = "Rex", Pelagem = "Curto", Peso = 10.3m @@ -93,20 +93,21 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) modelBuilder.Entity("DEPLOY.Cachorro.Domain.Aggregates.Tutor.Entities.Tutor", b => { - b.Property("Id") + b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("int") + .HasColumnType("bigint") .HasColumnName("Id"); - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); b.Property("Atualizacao") .ValueGeneratedOnAddOrUpdate() .HasColumnType("datetime") .HasColumnName("DataAlteracao"); - b.Property("CPF") - .HasColumnType("bigint") + b.Property("CPF") + .IsRequired() + .HasColumnType("varchar(11)") .HasColumnName("CPF"); b.Property("Cadastro") diff --git a/src/DEPLOY.Cachorro.Infra.Repository/Migrations/API/20240317120035_InitDatabaseAPI.cs b/src/DEPLOY.Cachorro.Infra.Repository/Migrations/API/20240408230334_InitDatabaseAPI.cs similarity index 87% rename from src/DEPLOY.Cachorro.Infra.Repository/Migrations/API/20240317120035_InitDatabaseAPI.cs rename to src/DEPLOY.Cachorro.Infra.Repository/Migrations/API/20240408230334_InitDatabaseAPI.cs index 7af8967..3ca8c2c 100644 --- a/src/DEPLOY.Cachorro.Infra.Repository/Migrations/API/20240317120035_InitDatabaseAPI.cs +++ b/src/DEPLOY.Cachorro.Infra.Repository/Migrations/API/20240408230334_InitDatabaseAPI.cs @@ -15,9 +15,9 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "Tutor", columns: table => new { - Id = table.Column(type: "int", nullable: false) + Id = table.Column(type: "bigint", nullable: false) .Annotation("SqlServer:Identity", "1, 1"), - CPF = table.Column(type: "bigint", nullable: false), + CPF = table.Column(type: "varchar(11)", nullable: false), Nome = table.Column(type: "varchar(100)", nullable: false), DataCadastro = table.Column(type: "datetime", nullable: false, defaultValueSql: "getdate()"), DataAlteracao = table.Column(type: "datetime", nullable: true) @@ -36,7 +36,7 @@ protected override void Up(MigrationBuilder migrationBuilder) Adotado = table.Column(type: "bit", nullable: false), Pelagem = table.Column(type: "nvarchar(max)", nullable: false), Peso = table.Column(type: "decimal(6,2)", nullable: false), - TutorId = table.Column(type: "int", nullable: true), + TutorId = table.Column(type: "bigint", nullable: true), Nome = table.Column(type: "varchar(100)", nullable: false), DataCadastro = table.Column(type: "datetime", nullable: false, defaultValueSql: "getdate()"), DataAlteracao = table.Column(type: "datetime", nullable: true) @@ -54,7 +54,7 @@ protected override void Up(MigrationBuilder migrationBuilder) migrationBuilder.InsertData( table: "Cachorro", columns: new[] { "Id", "Adotado", "DataCadastro", "DataNascimento", "Nome", "Pelagem", "Peso", "TutorId" }, - values: new object[] { new Guid("ac9b57e9-c326-45d2-bc48-853a146be964"), false, new DateTime(2024, 3, 17, 9, 0, 35, 399, DateTimeKind.Local).AddTicks(5220), new DateTime(2024, 3, 17, 9, 0, 35, 399, DateTimeKind.Local).AddTicks(5242), "Rex", "Curto", 10.3m, null }); + values: new object[] { new Guid("4b7f75cc-bad0-4c5e-87ba-70178ab9fe13"), false, new DateTime(2024, 4, 8, 20, 3, 33, 613, DateTimeKind.Local).AddTicks(1739), new DateTime(2024, 4, 8, 20, 3, 33, 613, DateTimeKind.Local).AddTicks(1776), "Rex", "Curto", 10.3m, null }); migrationBuilder.CreateIndex( name: "IX_Cachorro_Adotado", diff --git a/src/DEPLOY.Cachorro.Infra.Repository/Migrations/API/CachorroDbContextModelSnapshot.cs b/src/DEPLOY.Cachorro.Infra.Repository/Migrations/API/CachorroDbContextModelSnapshot.cs index 05d9a50..f341703 100644 --- a/src/DEPLOY.Cachorro.Infra.Repository/Migrations/API/CachorroDbContextModelSnapshot.cs +++ b/src/DEPLOY.Cachorro.Infra.Repository/Migrations/API/CachorroDbContextModelSnapshot.cs @@ -61,8 +61,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("decimal(6,2)") .HasColumnName("Peso"); - b.Property("TutorId") - .HasColumnType("int"); + b.Property("TutorId") + .HasColumnType("bigint"); b.HasKey("Id"); @@ -77,11 +77,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasData( new { - Id = new Guid("ac9b57e9-c326-45d2-bc48-853a146be964"), + Id = new Guid("4b7f75cc-bad0-4c5e-87ba-70178ab9fe13"), Adotado = false, - Atualizacao = new DateTime(2024, 3, 17, 9, 0, 35, 399, DateTimeKind.Local).AddTicks(5238), - Cadastro = new DateTime(2024, 3, 17, 9, 0, 35, 399, DateTimeKind.Local).AddTicks(5220), - Nascimento = new DateTime(2024, 3, 17, 9, 0, 35, 399, DateTimeKind.Local).AddTicks(5242), + Atualizacao = new DateTime(2024, 4, 8, 20, 3, 33, 613, DateTimeKind.Local).AddTicks(1770), + Cadastro = new DateTime(2024, 4, 8, 20, 3, 33, 613, DateTimeKind.Local).AddTicks(1739), + Nascimento = new DateTime(2024, 4, 8, 20, 3, 33, 613, DateTimeKind.Local).AddTicks(1776), Nome = "Rex", Pelagem = "Curto", Peso = 10.3m @@ -90,20 +90,21 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("DEPLOY.Cachorro.Domain.Aggregates.Tutor.Entities.Tutor", b => { - b.Property("Id") + b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("int") + .HasColumnType("bigint") .HasColumnName("Id"); - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); b.Property("Atualizacao") .ValueGeneratedOnAddOrUpdate() .HasColumnType("datetime") .HasColumnName("DataAlteracao"); - b.Property("CPF") - .HasColumnType("bigint") + b.Property("CPF") + .IsRequired() + .HasColumnType("varchar(11)") .HasColumnName("CPF"); b.Property("Cadastro") diff --git a/src/DEPLOY.Cachorro.Infra.Repository/Repositories/Base/GenericRepository.cs b/src/DEPLOY.Cachorro.Infra.Repository/Repositories/Base/GenericRepository.cs index 62cd0ab..13c64ef 100644 --- a/src/DEPLOY.Cachorro.Infra.Repository/Repositories/Base/GenericRepository.cs +++ b/src/DEPLOY.Cachorro.Infra.Repository/Repositories/Base/GenericRepository.cs @@ -1,8 +1,11 @@ using DEPLOY.Cachorro.Domain.Shared; using Microsoft.EntityFrameworkCore; +using System.Diagnostics.CodeAnalysis; +using System.Linq.Expressions; namespace DEPLOY.Cachorro.Infra.Repository.Repositories.Base { + [ExcludeFromCodeCoverage] public abstract class GenericRepository : IGenericRepository where TEntity : BaseEntity @@ -32,38 +35,41 @@ public async virtual Task> GetAllAsync( } public async virtual Task InsertAsync( - TEntity obj, + TEntity entity, CancellationToken cancellationToken = default) { //obj.GetType().GetProperty("Cadastro").SetValue(obj, new DateTime().ToLocalTime); await _cachorroDbContext .Set() - .AddAsync(obj, cancellationToken); + .AddAsync(entity, cancellationToken); - return obj; + return entity; } public async virtual Task UpdateAsync( - TEntity obj, + TEntity entity, CancellationToken cancellationToken = default) { await Task.Run(() => { _cachorroDbContext .Set() - .Attach(obj); + .Attach(entity); _cachorroDbContext - .Entry(obj).State = EntityState.Modified; - }); + .Entry(entity).State = EntityState.Modified; + + }, cancellationToken); } public async virtual Task DeleteAsync( Tid id, CancellationToken cancellationToken = default) { - TEntity? existing = await _cachorroDbContext.Set().FindAsync(new object[] {id}, cancellationToken); + TEntity? existing = await _cachorroDbContext + .Set() + .FindAsync(new object[] { id }, cancellationToken); if (existing != null) { @@ -76,5 +82,15 @@ public async virtual Task DeleteAsync( return false; } + + public virtual async Task> GetByKeyAsync( + Expression> predicate, + CancellationToken cancellationToken = default) + { + return await _cachorroDbContext + .Set() + .Where(predicate) + .ToListAsync(cancellationToken); + } } } \ No newline at end of file diff --git a/src/DEPLOY.Cachorro.Infra.Repository/Repositories/Base/UnitOfWork.cs b/src/DEPLOY.Cachorro.Infra.Repository/Repositories/Base/UnitOfWork.cs index 552d5a0..d7903c7 100644 --- a/src/DEPLOY.Cachorro.Infra.Repository/Repositories/Base/UnitOfWork.cs +++ b/src/DEPLOY.Cachorro.Infra.Repository/Repositories/Base/UnitOfWork.cs @@ -1,7 +1,8 @@ -using Microsoft.EntityFrameworkCore; +using System.Diagnostics.CodeAnalysis; namespace DEPLOY.Cachorro.Infra.Repository.Repositories.Base { + [ExcludeFromCodeCoverage] public class UnitOfWork : IUnitOfWork, IDisposable { private readonly CachorroDbContext _context; @@ -11,17 +12,20 @@ public UnitOfWork(CachorroDbContext context) _context = context; } - public async Task BeginTransactionAsync(CancellationToken cancellationToken) + public async Task BeginTransactionAsync( + CancellationToken cancellationToken) { await _context.Database.BeginTransactionAsync(cancellationToken); } - public async Task RollbackTransactionAsync(CancellationToken cancellationToken) + public async Task RollbackTransactionAsync( + CancellationToken cancellationToken) { await _context.Database.RollbackTransactionAsync(cancellationToken); } - public async Task CommitAndSaveChangesAsync(CancellationToken cancellationToken) + public async Task CommitAndSaveChangesAsync( + CancellationToken cancellationToken) { return await _context.SaveChangesAsync(cancellationToken) > 0; } @@ -30,14 +34,12 @@ public async Task CommitAndSaveChangesAsync(CancellationToken cancellation protected virtual void Dispose(bool disposing) { - if (!this.disposed) + if (!disposed && disposing) { - if (disposing) - { - _context.Dispose(); - } + _context.Dispose(); } - this.disposed = true; + + disposed = true; } public void Dispose() diff --git a/src/DEPLOY.Cachorro.Infra.Repository/Repositories/CachorroRepository.cs b/src/DEPLOY.Cachorro.Infra.Repository/Repositories/CachorroRepository.cs index 36ef1fe..e286778 100644 --- a/src/DEPLOY.Cachorro.Infra.Repository/Repositories/CachorroRepository.cs +++ b/src/DEPLOY.Cachorro.Infra.Repository/Repositories/CachorroRepository.cs @@ -1,10 +1,11 @@ using DEPLOY.Cachorro.Domain.Aggregates.Cachorro.Interfaces.Repositories; using DEPLOY.Cachorro.Infra.Repository.Repositories.Base; using Microsoft.EntityFrameworkCore; +using System.Linq.Expressions; namespace DEPLOY.Cachorro.Infra.Repository.Repositories { - public class CachorroRepository + public class CachorroRepository : GenericRepository , ICachorroRepository { @@ -23,5 +24,15 @@ public CachorroRepository(CachorroDbContext cachorroContext) : base(cachorroCont .Include(x => x.Tutor) .FirstOrDefaultAsync(x => x.Id == id, cancellationToken); } + + public override async Task> GetByKeyAsync( + Expression> predicate, + CancellationToken cancellationToken = default) + { + return await _cachorroContext.Set() + .Include(x => x.Tutor) + .Where(predicate) + .ToListAsync(cancellationToken); + } } } diff --git a/src/Tests/DEPLOY.Cachorro.Api.Tests/AdocoesControllerTest.cs b/src/Tests/DEPLOY.Cachorro.Api.Tests/AdocoesControllerTest.cs new file mode 100644 index 0000000..ca7e8af --- /dev/null +++ b/src/Tests/DEPLOY.Cachorro.Api.Tests/AdocoesControllerTest.cs @@ -0,0 +1,190 @@ +using DEPLOY.Cachorro.Api.Controllers.v1; +using DEPLOY.Cachorro.Application.Interfaces.Services; +using DEPLOY.Cachorro.Base.Tests; +using DEPLOY.Cachorro.Domain.Aggregates.Cachorro.Validations; +using FluentAssertions; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; +using Microsoft.AspNetCore.Mvc; +using Moq; +using System.Diagnostics.CodeAnalysis; +using Xunit; + +namespace DEPLOY.Cachorro.Api.Tests +{ + [ExcludeFromCodeCoverage] + public class AdocoesControllerTest + : IClassFixture, IClassFixture + { + private readonly Mock _adocaoAppServiceMock; + private readonly CachorroDtoFixture _cachorroDtoFixture; + private readonly TutorDtoFixture _tutorDtoFixture; + + private readonly AdocoesController _adocoesController; + + public AdocoesControllerTest( + CachorroDtoFixture cachorroDtoFixture, + TutorDtoFixture tutorDtoFixture) + { + _cachorroDtoFixture = cachorroDtoFixture; + _tutorDtoFixture = tutorDtoFixture; + _adocaoAppServiceMock = new Mock(); + + _adocoesController = new AdocoesController( + _adocaoAppServiceMock.Object); + } + + [Fact] + public async Task GivenAdotarAsync_WhenCachorroExists_ThanReturnSuccess() + { + // Arrange + var cachorroAdotado = _cachorroDtoFixture + .CreateManyCachorroDtoWithoutTutorDto(1)[0]; + + var tutor = _tutorDtoFixture.CreateManyTutorDto(1)[0]; + + _adocaoAppServiceMock + .Setup(x => x.AdotarAsync( + cachorroAdotado.Id, + tutor.Id, + default)) + .ReturnsAsync(Enumerable.Empty()); + + // Act + var result = await _adocoesController.AdotarAsync( + cachorroAdotado.Id, + tutor.Id, + default); + + // Assert + var item = result as OkResult; + Assert.NotNull(item); + } + + [Fact] + public async Task GivenAdotarAsync_WhenCachorroJaEstaAdotado_ThanReturnSuccess() + { + // Arrange + var cachorroAdotado = _cachorroDtoFixture + .CreateManyCachorroDtoWithTutorDto(1)[0]; + + var tutor = _tutorDtoFixture.CreateManyTutorDto(1)[0]; + + _adocaoAppServiceMock + .Setup(x => x.AdotarAsync( + cachorroAdotado.Id, + tutor.Id, + default)) + .ReturnsAsync(Enumerable.Empty().Append("Cachorro já tem tutor.")); + + // Act + var result = await _adocoesController.AdotarAsync( + cachorroAdotado.Id, + tutor.Id, + default); + + // Assert + var item = result as UnprocessableEntityObjectResult; + Assert.NotNull(item); + } + + [Fact] + public async Task GivenAdotarAsync_WhenItemExists_ThanReturnSuccess() + { + // Arrange + var cachorroAdotado = _cachorroDtoFixture + .CreateManyCachorroDtoWithoutTutorDto(1)[0]; + + var tutor = _tutorDtoFixture.CreateManyTutorDto(1)[0]; + + _adocaoAppServiceMock + .Setup(x => x.AdotarAsync( + cachorroAdotado.Id, + tutor.Id, + default)) + .ReturnsAsync(Enumerable.Empty()); + + // Act + var result = await _adocoesController.AdotarAsync( + cachorroAdotado.Id, + default); + + // Assert + var item = result as OkResult; + Assert.NotNull(item); + } + + [Fact] + public async Task GivenDevolverAsync_WhenItemExists_ThanReturnSuccess() + { + // Arrange + var cachorroid = Guid.NewGuid(); + + _adocaoAppServiceMock + .Setup(x => x.DevolverAdocaoAsync(cachorroid, default)) + .ReturnsAsync(Enumerable.Empty()); + + // Act + var result = await _adocoesController.DevolverAsync(cachorroid, default); + + // Assert + var item = result as OkResult; + Assert.NotNull(item); + } + + [Fact] + public async Task GivenDevolverAsync_WhenItemNotExists_ThanReturnUnprocessableEntity() + { + // Arrange + var cachorroid = Guid.NewGuid(); + + _adocaoAppServiceMock + .Setup(x => x.DevolverAdocaoAsync(cachorroid, default)) + .ReturnsAsync(new List { "Cachorro não existe" }); + + // Act + var result = await _adocoesController.DevolverAsync(cachorroid, default); + + // Assert + var item = result as UnprocessableEntityObjectResult; + Assert.NotNull(item); + } + + [Fact] + public async Task GivenDevolverAsync_WhenCachorroEstaAdotado_ThanReturnUnprocessableEntity() + { + // Arrange + var msgErro = new List { "Cachorro já esta adotado" }; + + var cachorroAdotado = _cachorroDtoFixture.CreateManyCachorroDtoWithERRORName(1)[0]; + + _adocaoAppServiceMock + .Setup(x => x.DevolverAdocaoAsync(cachorroAdotado.Id, default)) + .ReturnsAsync(msgErro); + + // Act + var result = await _adocoesController.DevolverAsync(cachorroAdotado.Id, default) as UnprocessableEntityObjectResult; + + var cachorroValidado = result?.Value as IEnumerable; + + cachorroValidado.Should().BeOfType>(); + + // Assert + Assert.NotNull(result); + result.Equals(StatusCodes.Status422UnprocessableEntity); + + //result.Should().Subject.Should().BeOfType(); + + //var model = result + // .As() + // .Value + // .Should() + // .BeOfType>(); + + //model.Subject.Should().OnlyContain(x => x.Equals("Cachorro já esta adotado.")); + + // test the message + //Assert.Equal("Cachorro já esta adotado.", cachorroValidator[0].ErrorMessage); + } + } +} \ No newline at end of file diff --git a/src/Tests/DEPLOY.Cachorro.Api.Tests/CachorrosControllerTestOLD.cs b/src/Tests/DEPLOY.Cachorro.Api.Tests/CachorrosControllerTestOLD.cs deleted file mode 100644 index a2f630b..0000000 --- a/src/Tests/DEPLOY.Cachorro.Api.Tests/CachorrosControllerTestOLD.cs +++ /dev/null @@ -1,329 +0,0 @@ -using Asp.Versioning; -using Bogus; -using DEPLOY.Cachorro.Application.Dtos; -using DEPLOY.Cachorro.Application.Interfaces.Services; -using FluentAssertions; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Moq; -using System.Diagnostics.CodeAnalysis; - -namespace DEPLOY.Cachorro.Api.Tests -{ - [ExcludeFromCodeCoverage] - public class CachorrosControllerTestOLD - { - - } - //[Fact] - //[Trait("Read", "API")] - //public async Task ListarAsync_ReturnOk() - //{ - // // Arrange - // var cachorro = new Domain.Cachorro { Nome = "Sirius" }; - - // var options = new DbContextOptionsBuilder() - // .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) - // .ConfigureWarnings(b => b.Ignore(InMemoryEventId.TransactionIgnoredWarning)) - // .Options; - - // using (var context = new CachorroDbContext(options)) - // { - // context.Database.EnsureDeleted(); - // context.Database.EnsureCreated(); - - // context.Cachorros.Add(cachorro); - // context.SaveChanges(); - // } - - // using (var context = new CachorroDbContext(options)) - // { - // var controller = new CachorrosController(context); - - // // Act - // var result = await controller.ListarAsync(); - - // // Assert - // result.Should().BeOfType(); - // result.As().Value - // .Should().BeOfType>(); - // } - //} - - //[Fact] - //[Trait("Read", "API")] - //public async Task ObterPorIdAsync_ReturnsOk_WhenCachorroIsFound() - //{ - // // Arrange - // var cachorro = new Domain.Cachorro { Nome = "Sirius" }; - - // var options = new DbContextOptionsBuilder() - // .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) - // .ConfigureWarnings(b => b.Ignore(InMemoryEventId.TransactionIgnoredWarning)) - // .Options; - - // using (var context = new CachorroDbContext(options)) - // { - // context.Database.EnsureDeleted(); - // context.Database.EnsureCreated(); - - // context.Cachorros.Add(cachorro); - // context.SaveChanges(); - // } - - // using (var context = new CachorroDbContext(options)) - // { - // var controller = new CachorrosController(context); - - // // Act - // var result = await controller.ObterPorIdAsync(cachorro.Id); - - // // Assert - // result.Should().BeOfType(); - // var model = result.As().Value - // .Should().BeOfType(); - // model.Subject.Id.Should().Be(cachorro.Id); - // model.Subject.Nome.Should().Be(cachorro.Nome); - // } - //} - - //[Fact] - //[Trait("Read", "API")] - //public async Task ObterPorIdAsync_ReturnsNotFound_WhenCachorroNotFound() - //{ - // // Arrange - // var cachorro = new Domain.Cachorro { Nome = "Sirius" }; - - // var options = new DbContextOptionsBuilder() - // .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) - // .ConfigureWarnings(b => b.Ignore(InMemoryEventId.TransactionIgnoredWarning)) - // .Options; - - // using (var context = new CachorroDbContext(options)) - // { - // context.Database.EnsureDeleted(); - // context.Database.EnsureCreated(); - - // context.Cachorros.Add(cachorro); - // context.SaveChanges(); - // } - - // using (var context = new CachorroDbContext(options)) - // { - // var controller = new CachorrosController(context); - - // // Act - // var result = await controller.ObterPorIdAsync(Guid.NewGuid()); - - // // Assert - // result.Should().BeOfType(); - // context.Cachorros.Should().NotContain(c => c.Id == Guid.NewGuid()); - // } - //} - - //[Fact] - //[Trait("Create", "API")] - //public async Task CadastrarCachorroAsync_ReturnsCreated_WhenCachorroIsValid() - //{ - // // Arrange - // var cachorro = new Domain.Cachorro { Nome = "Sirius" }; - - // var options = new DbContextOptionsBuilder() - // .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) - // .ConfigureWarnings(b => b.Ignore(InMemoryEventId.TransactionIgnoredWarning)) - // .Options; - - // using (var context = new CachorroDbContext(options)) - // { - // context.Database.EnsureDeleted(); - // context.Database.EnsureCreated(); - // } - - // using (var context = new CachorroDbContext(options)) - // { - // var controller = new CachorrosController(context); - - // // Act - // var result = await controller.CadastrarCachorroAsync(cachorro); - - // // Assert - // result.Should().BeOfType(); - // var model = result.As().Value - // .Should().BeOfType(); - // model.Subject.Nome.Should().Be(cachorro.Nome); - // } - //} - - //[Fact] - //[Trait("Update", "API")] - //public async Task PutCachorroAsync_ReturnsNoContent_WhenCachorroIsValid() - //{ - // // Arrange - // var cachorro = new Domain.Cachorro { Nome = "Sirius" }; - - // var options = new DbContextOptionsBuilder() - // .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) - // .ConfigureWarnings(b => b.Ignore(InMemoryEventId.TransactionIgnoredWarning)) - // .Options; - - // using (var context = new CachorroDbContext(options)) - // { - // context.Database.EnsureDeleted(); - // context.Database.EnsureCreated(); - - // context.Cachorros.Add(cachorro); - // context.SaveChanges(); - // } - - // using (var context = new CachorroDbContext(options)) - // { - // var controller = new CachorrosController(context); - - // // Act - // var result = await controller.PutCachorroAsync( - // cachorro.Id, - // new Domain.Cachorro() - // { - // Id = cachorro.Id, - // Nome = "Sirius v2" - // }); - - // // Assert - // result.Should().BeOfType(); - // //context.Entry(cachorro).State.Should().Equals(EntityState.Modified); - // } - //} - - //[Fact] - //[Trait("Update", "API")] - //public async Task PutCachorroAsync_ReturnsBadRequest_WhenCachorroIsInvalid() - //{ - // // Arrange - // var cachorro = new Domain.Cachorro { Nome = "Sirius" }; - - // var options = new DbContextOptionsBuilder() - // .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) - // .ConfigureWarnings(b => b.Ignore(InMemoryEventId.TransactionIgnoredWarning)) - // .Options; - - // using (var context = new CachorroDbContext(options)) - // { - // context.Database.EnsureDeleted(); - // context.Database.EnsureCreated(); - - // context.Cachorros.Add(cachorro); - // context.SaveChanges(); - // } - - // using (var context = new CachorroDbContext(options)) - // { - // var controller = new CachorrosController(context); - - // // Act - // var result = await controller.PutCachorroAsync( - // Guid.NewGuid(), - // new Domain.Cachorro() - // { - // Nome = "Sirius v2" - // }); - - // // Assert - // result.Should().BeOfType(); - // } - //} - - //[Fact] - //[Trait("Delete", "API")] - //public async Task ExcluirCachorroAsync_ReturnsNotFound_WhenCachorroIdDontExists() - //{ - // // Arrange - // var cachorroIdInexistente = Guid.NewGuid(); - - // var options = new DbContextOptionsBuilder() - // .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) - // .ConfigureWarnings(b => b.Ignore(InMemoryEventId.TransactionIgnoredWarning)) - // .Options; - - // using (var context = new CachorroDbContext(options)) - // { - // context.Database.EnsureDeleted(); - // context.Database.EnsureCreated(); - // } - - // using (var context = new CachorroDbContext(options)) - // { - // var controller = new CachorrosController(context); - - // // Act - // var result = await controller.ExcluirCachorroAsync(cachorroIdInexistente); - - // // Assert - // result.Should().BeOfType(); - // } - //} - - //[Fact] - //[Trait("Delete", "API")] - //public async Task ExcluirCachorroAsync_ReturnsNoContent_WhenCachorroIsDeleted() - //{ - // // Arrange - // var cachorroId = Guid.NewGuid(); - // var cachorro = new Domain.Cachorro { Id = cachorroId, Nome = "Sirius" }; - - // var options = new DbContextOptionsBuilder() - // .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) - // .ConfigureWarnings(b => b.Ignore(InMemoryEventId.TransactionIgnoredWarning)) - // .Options; - - // using (var context = new CachorroDbContext(options)) - // { - // context.Database.EnsureDeleted(); - // context.Database.EnsureCreated(); - - // context.Cachorros.Add(cachorro); - // context.SaveChanges(); - // } - - // using (var context = new CachorroDbContext(options)) - // { - // var controller = new CachorrosController(context); - - // // Act - // var result = await controller.ExcluirCachorroAsync(cachorroId); - - // // Assert - // result.Should().BeOfType(); - // context.Cachorros.Should().NotContain(c => c.Id == cachorroId); - // } - //} - - - //[Fact] - //public async Task AdotarCachorro_DeveRetornarNoContent() - //{ - // //Arrange - // var mockCachorroAppService = new Mock(); - // var cachorroController = new Controllers.v1.CachorrosController(mockCachorroAppService.Object); - - // var cachorro = new Faker() - // .RuleFor(x => x.Id, f => f.Random.Guid()) - // .RuleFor(x => x.Nome, f => f.Person.FirstName) - // .RuleFor(x => x.Pelagem, f => f.PickRandom()) - // .RuleFor(x => x.Peso, f => f.Random.Float(1, 100)) - // .RuleFor(x => x.Nascimento, f => f.Date.Past()) - // .Generate(); - - // var items = mockCachorroAppService.Setup(x => x.AdotarCachorro(It.IsAny(), It.IsAny())).Returns(() => Task.CompletedTask); - - // // Act - // var result = await cachorroController.AdotarCachorro(cachorro.Id, cachorro.Id) as NoContentResult; - - // //Assert - // Assert.NotNull(result); - // Assert.Equal(StatusCodes.Status204NoContent, result.StatusCode); - // result.Should().BeOfType(); - - // mockCachorroAppService.Verify(x => x.AdotarCachorro(It.IsAny(), It.IsAny()), Times.Once); - //} - -} \ No newline at end of file diff --git a/src/Tests/DEPLOY.Cachorro.Api.Tests/CachorroControllerTests.cs b/src/Tests/DEPLOY.Cachorro.Api.Tests/CachorrosControllerTests.cs similarity index 51% rename from src/Tests/DEPLOY.Cachorro.Api.Tests/CachorroControllerTests.cs rename to src/Tests/DEPLOY.Cachorro.Api.Tests/CachorrosControllerTests.cs index 5743fa1..0eb2ba7 100644 --- a/src/Tests/DEPLOY.Cachorro.Api.Tests/CachorroControllerTests.cs +++ b/src/Tests/DEPLOY.Cachorro.Api.Tests/CachorrosControllerTests.cs @@ -5,31 +5,34 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Moq; +using System.Diagnostics.CodeAnalysis; +using System.Linq.Expressions; using Xunit; namespace DEPLOY.Cachorro.Api.Tests { - public class CachorroControllerTests : IClassFixture + [ExcludeFromCodeCoverage] + public class CachorrosControllerTests : IClassFixture { + private Mock _cachorroAppServiceMock; private readonly CachorroDtoFixture _cachorroDtoFixture; - private readonly Mock _cachorroAppServiceMock; - private readonly Controllers.v1.CachorrosController _cachorroController; - public CachorroControllerTests() - { - _cachorroDtoFixture = new(); + private Controllers.v1.CachorrosController? _cachorroController; + public CachorrosControllerTests(CachorroDtoFixture cachorroDtoFixture) + { + _cachorroDtoFixture = cachorroDtoFixture; _cachorroAppServiceMock = new Mock(); - _cachorroController = new Controllers.v1.CachorrosController(_cachorroAppServiceMock.Object); } [Fact] [Trait("Read", "API")] - public async Task CadastrarCachorroAsync_DeveRetornarCreatedResult_ComIdEVersionQuandoSucesso() + public async Task GivenInsertAsync_WhenCachorroIsValid_ThanReturnCreateAtActionResult() { // Arrange var cachorro = _cachorroDtoFixture.CreateManyCachorroDtoWithTutorDto(1)[0]; + // Configurar o Setup para o método InsertAsync _cachorroAppServiceMock .Setup(x => x.InsertAsync(It.IsAny(), @@ -44,6 +47,9 @@ public async Task CadastrarCachorroAsync_DeveRetornarCreatedResult_ComIdEVersion Peso: cachorro.Peso ); + _cachorroController = new Controllers.v1.CachorrosController( + _cachorroAppServiceMock.Object); + // Act var result = await _cachorroController.CreateAsync(cachorroCreateDto) as CreatedAtActionResult; @@ -75,7 +81,49 @@ public async Task CadastrarCachorroAsync_DeveRetornarCreatedResult_ComIdEVersion } [Fact] - public async Task ObterTodosCachorroAsync_DeveRetornarListaCheia() + public async Task GivenInsertAsync_WhenCachorroIsNotValid_ThanReturnUnprocessableEntity() + { + // Arrange + var cachorro = _cachorroDtoFixture.CreateManyCachorroDtoWithERRORName(1)[0]; + + // Configurar o Setup para o método InsertAsync + _cachorroAppServiceMock + .Setup(x => x.InsertAsync( + It.IsAny(), + CancellationToken.None)) + .ReturnsAsync(cachorro); + + var cachorroCreateDto = new CachorroCreateDto + ( + Nome: cachorro.Nome, + Nascimento: cachorro.Nascimento, + Pelagem: cachorro.Pelagem, + Peso: cachorro.Peso + ); + + _cachorroController = new Controllers.v1.CachorrosController( + _cachorroAppServiceMock.Object); + + // Act + var result = await _cachorroController.CreateAsync(cachorroCreateDto) as UnprocessableEntityObjectResult; + + // Assert + Assert.NotNull(result); + + Assert.Equal(StatusCodes.Status422UnprocessableEntity, result.StatusCode); + result.Should().BeOfType(); + + var erros = result.Value as List; + + erros.Should().Contain("Nome é obrigatório"); + erros.Should().NotContain("Nome é obrigatório."); + + _cachorroAppServiceMock.Verify(x => x.InsertAsync(It.IsAny(), + CancellationToken.None), Times.Once); + } + + [Fact] + public async Task GivenGetAllAsync_WhenExistemCachorros_ThanDeveRetornarListaCheia() { //Arrange @@ -85,6 +133,9 @@ public async Task ObterTodosCachorroAsync_DeveRetornarListaCheia() .Setup(x => x.GetAllAsync(CancellationToken.None)) .ReturnsAsync(() => cachorros); + _cachorroController = new Controllers.v1.CachorrosController( + _cachorroAppServiceMock.Object); + // Act var result = await _cachorroController.ListAllAsync() as OkObjectResult; @@ -105,7 +156,7 @@ public async Task ObterTodosCachorroAsync_DeveRetornarListaCheia() } [Fact] - public async Task ObterTodosCachorroAsync_DeveRetornarListaVazia() + public async Task GivenGetAllAsync_WhenCachorrosDontExistis_ThanDeveRetornarListaVazia() { //Arrange var CachorroAppServiceMock = new Mock(); @@ -117,18 +168,21 @@ public async Task ObterTodosCachorroAsync_DeveRetornarListaVazia() .Setup(x => x.GetAllAsync(CancellationToken.None)) .ReturnsAsync(() => cachorros); + _cachorroController = new Controllers.v1.CachorrosController( + _cachorroAppServiceMock.Object); + // Act var result = await cachorroController.ListAllAsync() as NoContentResult; //Assert - Assert.Equal(StatusCodes.Status204NoContent, result.StatusCode); - var model = result.Should().BeOfType(); + Assert.Equal(StatusCodes.Status204NoContent, result?.StatusCode); + result.Should().BeOfType(); CachorroAppServiceMock.Verify(x => x.GetAllAsync(CancellationToken.None), Times.Once); } [Fact] - public async Task ObterCachorroPorId_DeveRetornarCachorro() + public async Task GivenGetByIdAsync_WhenCachorroIsIsValdid_ThanShouldReturnNewDog() { //Arrange var cachorro = _cachorroDtoFixture @@ -139,6 +193,9 @@ public async Task ObterCachorroPorId_DeveRetornarCachorro() CancellationToken.None)) .ReturnsAsync(() => cachorro); + _cachorroController = new Controllers.v1.CachorrosController( + _cachorroAppServiceMock.Object); + // Act var result = await _cachorroController.GetByIdAsync(cachorro.Id) as OkObjectResult; @@ -165,7 +222,7 @@ public async Task ObterCachorroPorId_DeveRetornarCachorro() } [Fact] - public async Task ObterCachorroPorId_DeveRetornarNotFound() + public async Task GivenGetByIdAsync_ThanCachorroIdIsNotValid_ShouldReturnNotFound() { //Arrange var cachorro = _cachorroDtoFixture.CreateManyCachorroDtoWithTutorDto(1)[0]; @@ -175,6 +232,9 @@ public async Task ObterCachorroPorId_DeveRetornarNotFound() CancellationToken.None)) .ReturnsAsync(() => null); + _cachorroController = new Controllers.v1.CachorrosController( + _cachorroAppServiceMock.Object); + // Act var result = await _cachorroController.GetByIdAsync(cachorro.Id) as NotFoundResult; @@ -188,7 +248,7 @@ public async Task ObterCachorroPorId_DeveRetornarNotFound() } [Fact] - public async Task AtualizarCachorro_DeveRetornarNoContent() + public async Task GivenUpdateAsync_WhenCachorroIsValid_ThanDeveRetornarNoContent() { //Arrange var cachorro = _cachorroDtoFixture.CreateManyCachorroDtoWithTutorDto(1)[0]; @@ -196,7 +256,10 @@ public async Task AtualizarCachorro_DeveRetornarNoContent() _cachorroAppServiceMock .Setup(x => x.UpdateAsync(It.IsAny(), It.IsAny(), CancellationToken.None)) - .ReturnsAsync(() => true); + .ReturnsAsync(() => Enumerable.Empty()); + + _cachorroController = new Controllers.v1.CachorrosController( + _cachorroAppServiceMock.Object); // Act var result = await _cachorroController.UpdateAsync(cachorro.Id, cachorro) as NoContentResult; @@ -211,7 +274,7 @@ public async Task AtualizarCachorro_DeveRetornarNoContent() } [Fact] - public async Task DeletarCachorro_DeveRetornarNoContent() + public async Task GivenDeleteAsync_ThanCachorroIsValid_ThanShouldReturnNoContent() { //Arrange var cachorro = _cachorroDtoFixture.CreateManyCachorroDtoWithTutorDto(1)[0]; @@ -221,6 +284,9 @@ public async Task DeletarCachorro_DeveRetornarNoContent() CancellationToken.None)) .ReturnsAsync(() => true); + _cachorroController = new Controllers.v1.CachorrosController( + _cachorroAppServiceMock.Object); + // Act var result = await _cachorroController.DeleteAsync(cachorro.Id) as NoContentResult; @@ -234,7 +300,7 @@ public async Task DeletarCachorro_DeveRetornarNoContent() } [Fact] - public async Task DeletarCachorro_DeveRetornarNotFound() + public async Task GivenDeleteAsync_WhenCachorroIdIsInvalid_ThanSouldReturnNotFound() { //Arrange var cachorro = _cachorroDtoFixture.CreateManyCachorroDtoWithTutorDto(1)[0]; @@ -244,6 +310,9 @@ public async Task DeletarCachorro_DeveRetornarNotFound() CancellationToken.None)) .ReturnsAsync(() => false); + _cachorroController = new Controllers.v1.CachorrosController( + _cachorroAppServiceMock.Object); + // Act var result = await _cachorroController.DeleteAsync(cachorro.Id) as NotFoundResult; @@ -255,5 +324,100 @@ public async Task DeletarCachorro_DeveRetornarNotFound() _cachorroAppServiceMock.Verify(x => x.DeleteAsync(It.IsAny(), CancellationToken.None), Times.Once); } + + [Fact] + public async Task GivenListAllCachorrosAdotadosAsync_WhenExistsCachorrosAdotados_ThanDeveRetornarDeCachorrosAdotados() + { + // Arrange + var cachorrosAdotados = _cachorroDtoFixture.CreateManyCachorroDtoWithTutorDto(40); + + _cachorroAppServiceMock.Setup(repo => repo.GetByKeyAsync( + It.IsAny>>(), + It.IsAny())) + .ReturnsAsync(cachorrosAdotados); + + _cachorroController = new Controllers.v1.CachorrosController( + _cachorroAppServiceMock.Object); + + // Act + var controller = await _cachorroController.ListAllCachorrosAdotadosAsync(default); + + // Assert + Assert.NotNull(controller); + + var result = controller as OkObjectResult; + + result.As().Value + .Should() + .BeOfType>(); + } + + [Fact] + public async Task GivenListAllCachorrosAdotadosAsync_WhenDontExistsCachorrosAdotados_ThanDeveRetornarDeCachorrosAdotadosVazia() + { + // Arrange + _cachorroAppServiceMock.Setup(repo => repo.GetByKeyAsync( + It.IsAny>>(), + It.IsAny())) + .ReturnsAsync(new List()); + + _cachorroController = new Controllers.v1.CachorrosController( + _cachorroAppServiceMock.Object); + + // Act + var controller = await _cachorroController.ListAllCachorrosAdotadosAsync(default); + + // Assert + Assert.NotNull(controller); + + var result = controller as NoContentResult; + + + } + + [Fact] + public async Task GivenListAllCachorrosParaAdocaoAsync_WhenCachorrosExistis_ThanDeveRetornarListaVazia() + { + // Arrange + var cachorrosParaAdocao = _cachorroDtoFixture.CreateManyCachorroDtoWithoutTutorDto(40); + + _cachorroAppServiceMock.Setup(repo => repo.GetByKeyAsync( + It.IsAny>>(), + It.IsAny())) + .ReturnsAsync(cachorrosParaAdocao); + + _cachorroController = new Controllers.v1.CachorrosController( + _cachorroAppServiceMock.Object); + + // Act + var controller = await _cachorroController.ListAllCachorrosParaAdocaoAsync(default); + + // Assert + Assert.NotNull(controller); + + var result = controller as OkObjectResult; + + } + + [Fact] + public async Task GivenListAllCachorrosParaAdocaoAsync_WhenCachorrosDontExistis_ThanDeveRetornarListaVazia() + { + // Arrange + _cachorroAppServiceMock.Setup(repo => repo.GetByKeyAsync( + It.IsAny>>(), + It.IsAny())) + .ReturnsAsync(new List()); + + _cachorroController = new Controllers.v1.CachorrosController( + _cachorroAppServiceMock.Object); + + // Act + var controller = await _cachorroController.ListAllCachorrosParaAdocaoAsync(default); + + // Assert + Assert.NotNull(controller); + + var result = controller as NoContentResult; + } } } diff --git a/src/Tests/DEPLOY.Cachorro.Api.Tests/DEPLOY.Cachorro.Api.Tests.csproj b/src/Tests/DEPLOY.Cachorro.Api.Tests/DEPLOY.Cachorro.Api.Tests.csproj index d8f6de3..3ea2b73 100644 --- a/src/Tests/DEPLOY.Cachorro.Api.Tests/DEPLOY.Cachorro.Api.Tests.csproj +++ b/src/Tests/DEPLOY.Cachorro.Api.Tests/DEPLOY.Cachorro.Api.Tests.csproj @@ -15,7 +15,6 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - diff --git a/src/Tests/DEPLOY.Cachorro.Api.Tests/TutoresControllerTest.cs b/src/Tests/DEPLOY.Cachorro.Api.Tests/TutoresControllerTest.cs index d2768eb..d1a3b29 100644 --- a/src/Tests/DEPLOY.Cachorro.Api.Tests/TutoresControllerTest.cs +++ b/src/Tests/DEPLOY.Cachorro.Api.Tests/TutoresControllerTest.cs @@ -1,350 +1,347 @@ using DEPLOY.Cachorro.Api.Controllers.v1; +using DEPLOY.Cachorro.Application.Dtos; +using DEPLOY.Cachorro.Application.Interfaces.Services; +using DEPLOY.Cachorro.Base.Tests; using FluentAssertions; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Diagnostics; +using Moq; using System.Diagnostics.CodeAnalysis; +using Xunit; namespace DEPLOY.Cachorro.Api.Tests { [ExcludeFromCodeCoverage] - public class TutoresControllerTest + public class TutoresControllerTest : IClassFixture { - //[Fact] - //[Trait("List", "API")] - //public async Task ListarAsync_ReturnsOk_WhenTutoresIsFound() - //{ - // // Arrange - // var tutor1 = new Domain.Tutor { Nome = "Eu cuido da silva" }; - // var tutor2 = new Domain.Tutor { Nome = "Eu cuido dos santos" }; - - // var options = new DbContextOptionsBuilder() - // .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) - // .ConfigureWarnings(b => b.Ignore(InMemoryEventId.TransactionIgnoredWarning)) - // .Options; - - // using (var context = new CachorroDbContext(options)) - // { - // context.Database.EnsureDeleted(); - // context.Database.EnsureCreated(); - - // context.Tutores.Add(tutor1); - // context.Tutores.Add(tutor2); - // context.SaveChanges(); - // } - - // using (var context = new CachorroDbContext(options)) - // { - // var controller = new TutoresController(context); - - // // Act - // var result = await controller.ListarAsync(); - - // // Assert - // result.Should().BeOfType(); - // result.As().Value.Should().BeOfType>(); - // } - //} - - //[Fact] - //[Trait("List", "API")] - //public async Task ListarAsync_ReturnsOk_WhenTutoresNotFound() - //{ - // var options = new DbContextOptionsBuilder() - // .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) - // .ConfigureWarnings(b => b.Ignore(InMemoryEventId.TransactionIgnoredWarning)) - // .Options; - - // using (var context = new CachorroDbContext(options)) - // { - // context.Database.EnsureDeleted(); - // context.Database.EnsureCreated(); - // } - - // using (var context = new CachorroDbContext(options)) - // { - // var controller = new TutoresController(context); - - // // Act - // var result = await controller.ListarAsync(); - - // // Assert - // result.Should().BeOfType(); - // result.As().Value.Should().BeOfType>(); - // result.As>().Should().BeNull(); - // } - //} - - //[Fact] - //[Trait("Read", "API")] - //public async Task ObterPorIdAsync_ReturnsOk_WhenTutorIsFound() - //{ - // // Arrange - // var tutor = new Domain.Tutor { Nome = "Eu cuido da silva" }; - - // var options = new DbContextOptionsBuilder() - // .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) - // .ConfigureWarnings(b => b.Ignore(InMemoryEventId.TransactionIgnoredWarning)) - // .Options; - - // using (var context = new CachorroDbContext(options)) - // { - // context.Database.EnsureDeleted(); - // context.Database.EnsureCreated(); - - // context.Tutores.Add(tutor); - // context.SaveChanges(); - // } - - // using (var context = new CachorroDbContext(options)) - // { - // var controller = new TutoresController(context); - - // // Act - // var result = await controller.ObterPorIdAsync(tutor.Id); - - // // Assert - // result.Should().BeOfType(); - // var model = result.As().Value.Should().BeOfType(); - // model.Subject.Id.Should().Be(tutor.Id); - // model.Subject.Nome.Should().Be(tutor.Nome); - // } - //} - - //[Fact] - //[Trait("Read", "API")] - //public async Task ObterPorIdAsync_ReturnsNotFound_WhenTutorNotFound() - //{ - // // Arrange - // var tutor = new Domain.Tutor { Nome = "Eu cuido da silva" }; - - // var options = new DbContextOptionsBuilder() - // .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) - // .ConfigureWarnings(b => b.Ignore(InMemoryEventId.TransactionIgnoredWarning)) - // .Options; - - // using (var context = new CachorroDbContext(options)) - // { - // context.Database.EnsureDeleted(); - // context.Database.EnsureCreated(); - - // context.Tutores.Add(tutor); - // context.SaveChanges(); - // } - - // using (var context = new CachorroDbContext(options)) - // { - // var controller = new TutoresController(context); - - // // Act - // var result = await controller.ObterPorIdAsync(tutor.Id + 1); - - // // Assert - // result.Should().BeOfType(); - // context.Tutores.Should().NotContain(t => t.Id == tutor.Id + 1); - // } - //} - - //[Fact] - //[Trait("Create", "API")] - //public async Task CadastrarTutotAsync_ReturnsCreated_WhenTutorIsValid() - //{ - // // Arrange - // var tutor = new Domain.Tutor { Nome = "Eu cuido da silva" }; - - // var options = new DbContextOptionsBuilder() - // .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) - // .ConfigureWarnings(b => b.Ignore(InMemoryEventId.TransactionIgnoredWarning)) - // .Options; - - // using (var context = new CachorroDbContext(options)) - // { - // context.Database.EnsureDeleted(); - // context.Database.EnsureCreated(); - // } - - // using (var context = new CachorroDbContext(options)) - // { - // var controller = new TutoresController(context); - - // // Act - // var result = await controller.CadastrarTutorAsync(tutor); - - // // Assert - // result.Should().BeOfType(); - // var model = result.As().Value.Should().BeOfType(); - // model.Subject.Nome.Should().Be(tutor.Nome); - // } - //} - - //[Fact] - //[Trait("Update", "API")] - //public async Task PutTutotAsync_ReturnsNoContent_WhenTutorIsValid() - //{ - // // Arrange - // var tutor = new Domain.Tutor { Nome = "Eu cuido da silva" }; - - // var options = new DbContextOptionsBuilder() - // .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) - // .ConfigureWarnings(b => b.Ignore(InMemoryEventId.TransactionIgnoredWarning)) - // .Options; - - // using (var context = new CachorroDbContext(options)) - // { - // context.Database.EnsureDeleted(); - // context.Database.EnsureCreated(); - - // context.Tutores.Add(tutor); - // context.SaveChanges(); - // } - - // using (var context = new CachorroDbContext(options)) - // { - // var controller = new TutoresController(context); - - // // Act - // var result = await controller.PutTutorAsync(tutor.Id, new Domain.Tutor() { Id = tutor.Id, Nome = "Sirius v2" }); - - // // Assert - // result.Should().BeOfType(); - // } - //} - - //[Fact] - //[Trait("Update", "API")] - //public async Task PutTutorAsync_ReturnsBadRequest_WhenTutorIsInvalid() - //{ - // // Arrange - // var tutor = new Domain.Tutor { Nome = "Eu cuido da silva" }; - - // var options = new DbContextOptionsBuilder() - // .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) - // .ConfigureWarnings(b => b.Ignore(InMemoryEventId.TransactionIgnoredWarning)) - // .Options; - - // using (var context = new CachorroDbContext(options)) - // { - // context.Database.EnsureDeleted(); - // context.Database.EnsureCreated(); - - // context.Tutores.Add(tutor); - // context.SaveChanges(); - // } - - // using (var context = new CachorroDbContext(options)) - // { - // var controller = new TutoresController(context); - - // // Act - // var result = await controller.PutTutorAsync(2, new Domain.Tutor() { Nome = "Nome Atualizado" }); - - // // Assert - // result.Should().BeOfType(); - // } - //} - - //[Fact] - //[Trait("Delete", "API")] - //public async Task ExcluirTutorAsync_ReturnsNotFound_WhenTutorIdDontExists() - //{ - // // Arrange - // var TutorId = 2; - // var tutor = new Domain.Tutor { Id = TutorId, Nome = "Eu cuido da silva" }; - - // var options = new DbContextOptionsBuilder() - // .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) - // .ConfigureWarnings(b => b.Ignore(InMemoryEventId.TransactionIgnoredWarning)) - // .Options; - - // using (var context = new CachorroDbContext(options)) - // { - // context.Database.EnsureDeleted(); - // context.Database.EnsureCreated(); - - // context.Tutores.Add(tutor); - // context.SaveChanges(); - // } - - // using (var context = new CachorroDbContext(options)) - // { - // var controller = new TutoresController(context); - - // // Act - // var result = await controller.ExcluirTutorAsync(tutor.Id + 1); - - // // Assert - // result.Should().BeOfType(); - // context.Tutores.Should().NotContain(t => t.Id == tutor.Id + 1); - // } - //} - - //[Fact] - //[Trait("Delete", "API")] - //public async Task ExcluirTutorAsync_ReturnsNoContent_WhenTutorIdIsValid() - //{ - // // Arrange - // var tutor = new Domain.Tutor { Nome = "Eu cuido da silva" }; - - // var options = new DbContextOptionsBuilder() - // .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) - // .ConfigureWarnings(b => b.Ignore(InMemoryEventId.TransactionIgnoredWarning)) - // .Options; - - // using (var context = new CachorroDbContext(options)) - // { - // context.Database.EnsureDeleted(); - // context.Database.EnsureCreated(); - - // context.Tutores.Add(tutor); - // context.SaveChanges(); - // } - - // using (var context = new CachorroDbContext(options)) - // { - // var controller = new TutoresController(context); - - // // Act - // var result = await controller.ExcluirTutorAsync(tutor.Id); - - // // Assert - // result.Should().BeOfType(); - // context.Tutores.Should().NotContain(t => t.Id == tutor.Id); - // } - //} - - //[Fact] - //[Trait("Delete", "API")] - //public async Task ExcluirTutorAsync_ReturnsNoContent_WhenTutorIsDeleted() - //{ - // // Arrange - // var cachorroId = 1; - // var tutor = new Domain.Tutor { Id = cachorroId, Nome = "Eu cuido da silva" }; - - // var options = new DbContextOptionsBuilder() - // .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) - // .ConfigureWarnings(b => b.Ignore(InMemoryEventId.TransactionIgnoredWarning)) - // .Options; - - // using (var context = new CachorroDbContext(options)) - // { - // context.Database.EnsureDeleted(); - // context.Database.EnsureCreated(); - - // context.Tutores.Add(tutor); - // context.SaveChanges(); - // } - - // using (var context = new CachorroDbContext(options)) - // { - // var controller = new TutoresController(context); - - // // Act - // var result = await controller.ExcluirTutorAsync(cachorroId); - - // // Assert - // result.Should().BeOfType(); - // context.Cachorros.Should().NotContain(c => c.Id == Guid.NewGuid()); - // } - //} + private readonly Mock _tutorAppServiceMock; + private readonly TutorDtoFixture _tutorDtoFixture; + + private Controllers.v1.TutoresController? _tutoresController; + + public TutoresControllerTest(TutorDtoFixture tutorDtoFixture) + { + _tutorDtoFixture = tutorDtoFixture; + _tutorAppServiceMock = new Mock(); + } + + [Fact] + public async Task GivenListAllAsync_WhenExistsTutores_ThanReturnListWithTutores() + { + // Arrange + var tutor = _tutorDtoFixture.CreateManyTutorDto(3); + + _tutorAppServiceMock + .Setup(t => t.GetAllAsync(CancellationToken.None)) + .ReturnsAsync(tutor); + + _tutoresController = new Controllers.v1.TutoresController(_tutorAppServiceMock.Object); + + // Act + var result = await _tutoresController.ListAllAsync(CancellationToken.None) as OkObjectResult; + + //Assert + Assert.NotNull(result); + + Assert.Equal(StatusCodes.Status200OK, result.StatusCode); + + result.Should().BeOfType(); + + var model = result.As().Value + .Should() + .BeOfType>(); + + model.Subject.Count.Should().Be(3); + + _tutorAppServiceMock.Verify(x => x.GetAllAsync(CancellationToken.None), Times.Once); + } + + [Fact] + public async Task GivenListAllAsync_WhenNotExistsTutores_ThanReturnEmptyList() + { + // Arrange + var tutor = _tutorDtoFixture.CreateManyTutorDto(0); + + _tutorAppServiceMock + .Setup(t => t.GetAllAsync(CancellationToken.None)) + .ReturnsAsync(tutor); + + _tutoresController = new Controllers.v1.TutoresController(_tutorAppServiceMock.Object); + + // Act + var result = await _tutoresController.ListAllAsync(CancellationToken.None) as OkObjectResult; + + //Assert + Assert.NotNull(result); + + Assert.Equal(StatusCodes.Status200OK, result.StatusCode); + + result.Should().BeOfType(); + + var model = result.As().Value + .Should() + .BeOfType>(); + + model.Subject.Count.Should().Be(0); + + _tutorAppServiceMock.Verify(x => x.GetAllAsync(CancellationToken.None), Times.Once); + } + + [Fact] + public async Task GivenGetByIdAsync_WhenIdIsValid_ThanSuccess() + { + // Arrange + var tutor = _tutorDtoFixture.CreateManyTutorDto(1)[0]; + + _tutorAppServiceMock + .Setup(t => t.GetByIdAsync(tutor.Id, CancellationToken.None)) + .ReturnsAsync(tutor); + + _tutoresController = new Controllers.v1.TutoresController(_tutorAppServiceMock.Object); + + // Act + var result = await _tutoresController + .GetByIdAsync( + tutor.Id, + CancellationToken.None) + as OkObjectResult; + + // Assert + Assert.NotNull(result); + + Assert.Equal(StatusCodes.Status200OK, result.StatusCode); + + result.Should().BeOfType(); + + _tutorAppServiceMock.Verify(x => x.GetAllAsync(CancellationToken.None), Times.Never); + } + + [Fact] + public async Task GivenGetByIdAsync_WhenTutorIdIsNotValid_ThanNorFoundResult() + { + // Arrange + var tutor = _tutorDtoFixture.CreateManyTutorDto(1)[0]; + + _tutorAppServiceMock + .Setup(t => t.GetByIdAsync(It.IsAny(), CancellationToken.None)) + .ReturnsAsync(() => null); + + _tutoresController = new Controllers.v1.TutoresController(_tutorAppServiceMock.Object); + + // Act + var result = await _tutoresController.GetByIdAsync(tutor.Id, CancellationToken.None) as NotFoundResult; + + // Assert + Assert.NotNull(result); + + Assert.Equal(StatusCodes.Status404NotFound, result.StatusCode); + + result.Should().BeOfType(); + + _tutorAppServiceMock.Verify(x => x.GetAllAsync(CancellationToken.None), Times.Never); + _tutorAppServiceMock.Verify(x => x.GetByIdAsync(It.IsAny(), CancellationToken.None), Times.Once); + } + + [Fact] + public async Task GivenTutorCreateAsync_WhenTutorIsValid_ThanReturnCreatedAction201() + { + // Arrange + var tutor = _tutorDtoFixture.CreateManyTutorDto(1)[0]; + + _tutorAppServiceMock + .Setup(x => x.InsertAsync(It.IsAny(), + CancellationToken.None)) + .ReturnsAsync(tutor); + + _tutoresController = new Controllers.v1.TutoresController(_tutorAppServiceMock.Object); + + // Act + var result = await _tutoresController.CreateAsync(tutor) as CreatedAtActionResult; + + // Assert + Assert.NotNull(result); + + Assert.Equal(StatusCodes.Status201Created, result.StatusCode); + result.Should().BeOfType(); + + Assert.Equal("GetById", result.ActionName); + + var routeValues = result.RouteValues; + Assert.NotNull(routeValues); + Assert.True(routeValues.ContainsKey("id")); + Assert.True(routeValues.ContainsKey("version")); + + Assert.Equal("1.0", routeValues["version"]); + + Assert.Equal(tutor, result.Value); + + _tutorAppServiceMock.Verify(x => x.InsertAsync(It.IsAny(), + CancellationToken.None), Times.Once); + + var model = result.As().Value + .Should().BeOfType(); + + model.Subject.Nome.Should().Be(tutor.Nome); + model.Subject.Erros.Should().BeNullOrEmpty(); + } + + [Fact] + public async Task GivenTutorCreateAsync_WhenTutorIsNotValid_ThanReturnUnprocessableEntity() + { + // Arrange + var tutor = _tutorDtoFixture.CreateManyTutorDtoWithNameError(1)[0]; + + _tutorAppServiceMock + .Setup(x => x.InsertAsync(It.IsAny(), + CancellationToken.None)) + .ReturnsAsync(tutor); + + _tutoresController = new Controllers.v1.TutoresController(_tutorAppServiceMock.Object); + + // Act + var result = await _tutoresController.CreateAsync(tutor) as UnprocessableEntityObjectResult; + + // Assert + Assert.NotNull(result); + + Assert.Equal(StatusCodes.Status422UnprocessableEntity, result.StatusCode); + result.Should().BeOfType(); + + Assert.Equal(tutor.Erros, result.Value); + + _tutorAppServiceMock.Verify(x => x.InsertAsync(It.IsAny(), + CancellationToken.None), Times.Once); + } + + [Fact] + public async Task GivenUpdateAsync_WhenTutorIsValid_ThanReturnNoContent() + { + // Arrange + var tutor = _tutorDtoFixture.CreateManyTutorDto(1)[0]; + + _tutorAppServiceMock + .Setup(t => t.UpdateAsync(tutor.Id, tutor, CancellationToken.None)) + .ReturnsAsync(Enumerable.Empty()); + + _tutoresController = new Controllers.v1.TutoresController(_tutorAppServiceMock.Object); + + // Act + var result = await _tutoresController.UpdateAsync(tutor.Id, tutor, CancellationToken.None) as NoContentResult; + + // Assert + Assert.Equal(StatusCodes.Status204NoContent, result?.StatusCode); + + result.Should().BeOfType(); + + _tutorAppServiceMock.Verify(x => x.GetAllAsync(CancellationToken.None), Times.Never); + _tutorAppServiceMock.Verify(x => x.UpdateAsync(tutor.Id, tutor, CancellationToken.None), Times.Once); + } + + [Fact] + public async Task GivenUpdateAsync_WhenTutorDontExistis_ThanReturnUnprocessableEntity() + { + // Arrange + var tutor = _tutorDtoFixture.CreateManyTutorDto(1)[0]; + + _tutorAppServiceMock + .Setup(t => t.UpdateAsync(It.IsAny(), tutor, CancellationToken.None)) + .ReturnsAsync(Enumerable.Empty().Append("Tutor não encontado")); + + _tutoresController = new Controllers.v1.TutoresController(_tutorAppServiceMock.Object); + + // Act + var result = await _tutoresController.UpdateAsync(tutor.Id, tutor, CancellationToken.None) as UnprocessableEntityObjectResult; + + // Assert + Assert.NotNull(result); + + Assert.Equal(StatusCodes.Status422UnprocessableEntity, result.StatusCode); + + result.Should().BeOfType(); + + _tutorAppServiceMock.Verify(x => x.GetAllAsync(CancellationToken.None), Times.Never); + _tutorAppServiceMock.Verify(x => x.UpdateAsync(tutor.Id, tutor, CancellationToken.None), Times.Once); + } + + [Fact] + public async Task GivenUpdateAsync_WhenIdIsEqualButTutorDontExists_ThanError() + { + // Arrange + var tutor = _tutorDtoFixture.CreateManyTutorDto(1)[0]; + + _tutorAppServiceMock + .Setup(t => t.UpdateAsync(tutor.Id, tutor, CancellationToken.None)) + .ReturnsAsync(Enumerable.Empty().Append("Tutor não existe")); + + var tutor2 = _tutorDtoFixture.CreateManyTutorDto(1)[0]; + + _tutoresController = new TutoresController( + _tutorAppServiceMock.Object); + + // Act + var result = await _tutoresController.UpdateAsync(tutor.Id, tutor, CancellationToken.None) as UnprocessableEntityObjectResult; + + // Assert + Assert.NotNull(result); + + Assert.Equal(StatusCodes.Status422UnprocessableEntity, result.StatusCode); + + result.Should().BeOfType(); + + _tutorAppServiceMock.Verify(x => x.GetAllAsync(CancellationToken.None), Times.Never); + _tutorAppServiceMock.Verify(x => x.UpdateAsync(tutor.Id, tutor, CancellationToken.None), Times.Once); + _tutorAppServiceMock.Verify(x => x.UpdateAsync(tutor2.Id, tutor2, CancellationToken.None), Times.Never); + } + + [Fact] + public async Task GivenDeleteAsync_WhenTutorExists_ThanSuccess() + { + //Arrange + var tutor = _tutorDtoFixture.CreateManyTutorDto(1)[0]; + + _tutorAppServiceMock + .Setup(x => x.DeleteAsync(It.IsAny(), + CancellationToken.None)) + .ReturnsAsync(() => true); + + _tutoresController = new Controllers.v1.TutoresController(_tutorAppServiceMock.Object); + + // Act + var result = await _tutoresController.DeleteAsync(tutor.Id) as NoContentResult; + + //Assert + Assert.NotNull(result); + Assert.Equal(StatusCodes.Status204NoContent, result.StatusCode); + result.Should().BeOfType(); + + _tutorAppServiceMock.Verify(x => x.DeleteAsync(It.IsAny(), + CancellationToken.None), Times.Once); + } + + [Fact] + public async Task GivenDeleteAsync_WhenTutorDontExists_ThanReturnNotFound() + { + //Arrange + var tutor = _tutorDtoFixture.CreateManyTutorDto(1)[0]; + + _tutorAppServiceMock + .Setup(x => x.DeleteAsync(It.IsAny(), + CancellationToken.None)) + .ReturnsAsync(() => false); + + _tutoresController = new Controllers.v1.TutoresController(_tutorAppServiceMock.Object); + + // Act + var result = await _tutoresController.DeleteAsync(tutor.Id) as NotFoundResult; + + //Assert + Assert.NotNull(result); + Assert.Equal(StatusCodes.Status404NotFound, result.StatusCode); + result.Should().BeOfType(); + + _tutorAppServiceMock.Verify(x => x.DeleteAsync(It.IsAny(), + CancellationToken.None), Times.Once); + } } } \ No newline at end of file diff --git a/src/Tests/DEPLOY.Cachorro.Base.Tests/CachorroDtoFixture.cs b/src/Tests/DEPLOY.Cachorro.Base.Tests/CachorroDtoFixture.cs index ea4bc4f..ff3c2f5 100644 --- a/src/Tests/DEPLOY.Cachorro.Base.Tests/CachorroDtoFixture.cs +++ b/src/Tests/DEPLOY.Cachorro.Base.Tests/CachorroDtoFixture.cs @@ -1,14 +1,16 @@ using Bogus; using Bogus.Extensions.Brazil; using DEPLOY.Cachorro.Application.Dtos; +using System.Diagnostics.CodeAnalysis; namespace DEPLOY.Cachorro.Base.Tests { + [ExcludeFromCodeCoverage] public class CachorroDtoFixture { public List CreateManyCachorroDtoWithTutorDto(int quantity) { - return new Faker() + return new Faker(locale: "pt_BR") .CustomInstantiator(f => new CachorroDto( f.Random.Guid(), f.Person.FirstName, @@ -18,19 +20,47 @@ public List CreateManyCachorroDtoWithTutorDto(int quantity) f.Random.Bool(), f.PickRandom(), f.Random.Float(1, 100), - new Faker() + new Faker(locale: "pt_BR") .CustomInstantiator(t => new TutorDto( t.Random.Long(), t.Person.FirstName, f.Date.Past(), f.Date.Past(), - long.Parse(f.Person.Cpf().Replace(".", "").Replace("-", "")), + f.Person.Cpf(false), Enumerable.Empty())) .Generate(), Enumerable.Empty())) .Generate(quantity); } + public List CreateManyCachorroDtoWithoutTutorDto(int quantity) + { + return new Faker(locale: "pt_BR") + .CustomInstantiator(f => new CachorroDto( + f.Random.Guid(), + f.Person.FirstName, + f.Date.Past(), + f.Date.Past(), + f.Date.Past(), + f.Random.Bool(), + f.PickRandom(), + f.Random.Float(1, 100), + Tutor: null, + Enumerable.Empty())) + .Generate(quantity); + } + + public List CreateManyCachorroCreateDto(int quantity) + { + return new Faker(locale: "pt_BR") + .CustomInstantiator(f => new CachorroCreateDto( + f.Person.FirstName, + f.Date.Past(), + f.PickRandom(), + f.Random.Float(1, 100))) + .Generate(quantity); + } + public List CreateManyCachorroCreatedDto(int quantity) { return new Faker() @@ -60,9 +90,25 @@ public List CreateManyCachorroCreatedDto(int quantity) t.Person.FirstName, f.Date.Past(), f.Date.Past(), - long.Parse(f.Person.Cpf().Replace(".", "").Replace("-", "")))) + f.Person.Cpf(false))) .Generate())) .Generate(quantity); } + public List CreateManyCachorroDtoWithERRORName(int quantity) + { + return new Faker(locale: "pt_BR") + .CustomInstantiator(f => new CachorroDto( + f.Random.Guid(), + f.Person.FirstName, + f.Date.Past(), + f.Date.Past(), + f.Date.Past(), + f.Random.Bool(), + f.PickRandom(), + f.Random.Float(1, 100), + Tutor: null, + Erros: Enumerable.Empty().Append("Nome é obrigatório").ToList())) + .Generate(quantity); + } } } diff --git a/src/Tests/DEPLOY.Cachorro.Base.Tests/CachorroFixture.cs b/src/Tests/DEPLOY.Cachorro.Base.Tests/CachorroFixture.cs index b82245a..b3bc2fd 100644 --- a/src/Tests/DEPLOY.Cachorro.Base.Tests/CachorroFixture.cs +++ b/src/Tests/DEPLOY.Cachorro.Base.Tests/CachorroFixture.cs @@ -1,31 +1,72 @@ using Bogus; using Bogus.Extensions.Brazil; +using System.Diagnostics.CodeAnalysis; namespace DEPLOY.Cachorro.Base.Tests { + [ExcludeFromCodeCoverage] public class CachorroFixture { public List CreateManyCachorroWithTutor(int quantity) { - return new Faker() + return new Faker(locale: "pt_BR") .CustomInstantiator(f => new Domain.Aggregates.Cachorro.Entities.Cachorro( f.Random.Guid(), f.Person.FirstName, f.Date.Past(), f.Date.Past(), f.Date.Past(), - f.Random.Bool(), + true, f.PickRandom(), f.Random.Float(1, 100), - new Faker() + new Faker(locale: "pt_BR") .CustomInstantiator(t => new Domain.Aggregates.Tutor.Entities.Tutor( t.Random.Long(), t.Person.FirstName, f.Date.Past(), f.Date.Past(), - long.Parse(f.Person.Cpf().Replace(".", "").Replace("-", "")))) + f.Person.Cpf(false))) .Generate())) .Generate(quantity); } + + public List CreateManyCachorroWithoutTutor(int quantity) + { + return new Faker(locale: "pt_BR") + .CustomInstantiator(f => new Domain.Aggregates.Cachorro.Entities.Cachorro( + f.Random.Guid(), + f.Person.FirstName, + f.Date.Past(), + f.Date.Past(), + f.Date.Past(), + true, + f.PickRandom(), + f.Random.Float(1, 100), + null)) + .Generate(quantity); + } + + public List CreateManyCachorroWithNameError(int quantity) + { + var cachorros = new Faker(locale: "pt_BR") + .CustomInstantiator(f => new Domain.Aggregates.Cachorro.Entities.Cachorro( + f.Random.Guid(), + f.Person.FirstName, + f.Date.Past(), + f.Date.Past(), + f.Date.Past(), + f.Random.Bool(), + f.PickRandom(), + f.Random.Float(min: 1, max: 100), + null)) + .Generate(quantity); + + foreach (var cachorro in cachorros) + { + cachorro.Erros.Add("Nome é obrigatório"); + } + + return cachorros; + } } } diff --git a/src/Tests/DEPLOY.Cachorro.Base.Tests/DEPLOY.Cachorro.Base.Tests.csproj b/src/Tests/DEPLOY.Cachorro.Base.Tests/DEPLOY.Cachorro.Base.Tests.csproj index c5bbed2..cb0121e 100644 --- a/src/Tests/DEPLOY.Cachorro.Base.Tests/DEPLOY.Cachorro.Base.Tests.csproj +++ b/src/Tests/DEPLOY.Cachorro.Base.Tests/DEPLOY.Cachorro.Base.Tests.csproj @@ -8,6 +8,7 @@ + diff --git a/src/Tests/DEPLOY.Cachorro.Base.Tests/TutorDtoFixture.cs b/src/Tests/DEPLOY.Cachorro.Base.Tests/TutorDtoFixture.cs new file mode 100644 index 0000000..2bf0b0f --- /dev/null +++ b/src/Tests/DEPLOY.Cachorro.Base.Tests/TutorDtoFixture.cs @@ -0,0 +1,37 @@ +using Bogus; +using Bogus.Extensions.Brazil; +using DEPLOY.Cachorro.Application.Dtos; +using System.Diagnostics.CodeAnalysis; + +namespace DEPLOY.Cachorro.Base.Tests +{ + [ExcludeFromCodeCoverage] + public class TutorDtoFixture + { + public List CreateManyTutorDto(int quantity) + { + return new Faker(locale: "pt_BR") + .CustomInstantiator(t => new TutorDto( + Id: t.Random.Long(0, long.MaxValue), + Nome: t.Person.FirstName, + Cadastro: DateTime.Now, + Atualizacao: t.Date.Past(), + CPF: t.Person.Cpf(false), + Erros: Enumerable.Empty())) + .Generate(quantity); + } + + public List CreateManyTutorDtoWithNameError(int quantity) + { + return new Faker(locale: "pt_BR") + .CustomInstantiator(t => new TutorDto( + Id: t.Random.Long(0, long.MaxValue), + Nome: string.Empty, + Cadastro: DateTime.Now, + Atualizacao: t.Date.Past(), + CPF: t.Person.Cpf(false), + Erros: Enumerable.Empty().Append("Nome é obrigatório"))) + .Generate(quantity); + } + } +} diff --git a/src/Tests/DEPLOY.Cachorro.Base.Tests/TutorFixture.cs b/src/Tests/DEPLOY.Cachorro.Base.Tests/TutorFixture.cs new file mode 100644 index 0000000..e1e6827 --- /dev/null +++ b/src/Tests/DEPLOY.Cachorro.Base.Tests/TutorFixture.cs @@ -0,0 +1,35 @@ +using Bogus; +using Bogus.Extensions.Brazil; +using System.Diagnostics.CodeAnalysis; + +namespace DEPLOY.Cachorro.Base.Tests +{ + [ExcludeFromCodeCoverage] + public class TutorFixture + { + public List CreateManyTutores(int quantity) + { + return new Faker(locale: "pt_BR") + .CustomInstantiator(t => new Domain.Aggregates.Tutor.Entities.Tutor( + t.Random.Long(), + t.Person.FirstName, + t.Date.Past(), + t.Date.Past(), + t.Person.Cpf(false))) + .Generate(quantity); + } + + public List CreateManyTutoresWithERRORCPF(int quantity) + { + return new Faker(locale: "pt_BR") + .CustomInstantiator(t => new Domain.Aggregates.Tutor.Entities.Tutor( + t.Random.Long(), + t.Person.FirstName, + t.Date.Past(), + t.Date.Past(), + t.Random.Long(736273840532, 996273840532).ToString()) + { Erros = new List(1) { "CPF inválido" } }) + .Generate(quantity); + } + } +} diff --git a/src/Tests/DEPLOY.Cachorro.Domain.Tests/AdotarServiceTests.cs b/src/Tests/DEPLOY.Cachorro.Domain.Tests/AdotarServiceTests.cs new file mode 100644 index 0000000..6ed22ec --- /dev/null +++ b/src/Tests/DEPLOY.Cachorro.Domain.Tests/AdotarServiceTests.cs @@ -0,0 +1,160 @@ +using DEPLOY.Cachorro.Base.Tests; +using DEPLOY.Cachorro.Domain.Aggregates.Adotar.Services; +using DEPLOY.Cachorro.Domain.Aggregates.Cachorro.Interfaces.Repositories; +using DEPLOY.Cachorro.Domain.Aggregates.Tutor.Interfaces.Repositories; +using DEPLOY.Cachorro.Infra.Repository.Repositories.Base; +using FluentAssertions; +using Moq; +using System.Diagnostics.CodeAnalysis; + +namespace DEPLOY.Cachorro.Domain.Tests +{ + [ExcludeFromCodeCoverage] + public class AdotarServiceTests : IClassFixture, IClassFixture + { + private readonly CachorroFixture _cachorroFixture; + private readonly TutorFixture _tutorFixture; + + private readonly Mock _cachorroRepositoryMock; + private readonly Mock _tutorRepositoryMock; + private readonly Mock _uowMock; + + public AdotarServiceTests( + CachorroFixture cachorroFixture, + TutorFixture tutorFixture) + { + _cachorroFixture = cachorroFixture; + _tutorFixture = tutorFixture; + + _cachorroRepositoryMock = new Mock(); + _tutorRepositoryMock = new Mock(); + _uowMock = new Mock(); + } + + [Fact] + public async Task GivenAdotarAsync_WhenCachorroExiste_ThanDeveRetornarErro() + { + // Arrange + var adocaoService = new AdocaoService( + _uowMock.Object, + _cachorroRepositoryMock.Object, + _tutorRepositoryMock.Object); + + var cachorro = _cachorroFixture.CreateManyCachorroWithoutTutor(1)[0]; + var tutor = _tutorFixture.CreateManyTutores(1)[0]; + + _cachorroRepositoryMock + .Setup(x => x.GetByIdAsync(It.IsAny(), CancellationToken.None)) + .ReturnsAsync(cachorro); + + _tutorRepositoryMock + .Setup(x => x.GetByIdAsync(It.IsAny(), CancellationToken.None)) + .ReturnsAsync(tutor); + + // Act + var result = await adocaoService.AdotarAsync(Guid.NewGuid(), 1); + + // Assert + result.Should().BeOfType>(); + } + + [Fact] + public async Task GivenAdotarAsync_WhenCachorroNaoEncontrado_ThanDeveRetornarErro() + { + // Arrange + var adocaoService = new AdocaoService( + _uowMock.Object, + _cachorroRepositoryMock.Object, + _tutorRepositoryMock.Object); + + _cachorroRepositoryMock + .Setup(x => x.GetByIdAsync(It.IsAny(), CancellationToken.None)) + .ReturnsAsync(null as Domain.Aggregates.Cachorro.Entities.Cachorro); + + // Act + var result = await adocaoService.AdotarAsync(Guid.NewGuid(), 1); + + // Assert + result.Should().BeOfType>(); + + Assert.Contains("Cachorro não encontrado", result); + } + + [Fact] + public async Task GivenDevolverAdocaoAsync_WhenCachorroNotFound_ReturnsValidationMessage() + { + // Arrange + var adocaoService = new AdocaoService( + _uowMock.Object, + _cachorroRepositoryMock.Object, + _tutorRepositoryMock.Object); + + var cachorroId = Guid.NewGuid(); + + _cachorroRepositoryMock + .Setup(x => x.GetByIdAsync(cachorroId, default)) + .ReturnsAsync((Domain.Aggregates.Cachorro.Entities.Cachorro)null); + + // Act + var result = await adocaoService.DevolverAdocaoAsync(cachorroId); + + // Assert + Assert.Equal("Cachorro não encontrado", result.Single()); + _cachorroRepositoryMock + .Verify(x => x.UpdateAsync(It.IsAny(), CancellationToken.None), Times.Never); + + _uowMock.Verify(x => x.CommitAndSaveChangesAsync(CancellationToken.None), Times.Never); + } + + [Fact] + public async Task GivenDevolverAdocaoAsync_WhenCachorroNotAdopted_ReturnsValidationMessage() + { + // Arrange + var adocaoService = new AdocaoService( + _uowMock.Object, + _cachorroRepositoryMock.Object, + _tutorRepositoryMock.Object); + + var cachorro = _cachorroFixture.CreateManyCachorroWithTutor(1)[0]; + + _cachorroRepositoryMock + .Setup(x => x.GetByIdAsync(cachorro.Id, CancellationToken.None)) + .ReturnsAsync(cachorro); + + // Act + var result = await adocaoService.DevolverAdocaoAsync(cachorro.Id); + + // Assert + Assert.Equal(Enumerable.Empty(), result); + + _cachorroRepositoryMock.Verify(x => x.UpdateAsync(It.IsAny(), CancellationToken.None), Times.Once); + + _uowMock.Verify(x => x.CommitAndSaveChangesAsync(CancellationToken.None), Times.Once); + } + + [Fact] + public async Task GivenDevolverAdocaoAsync_WhenCachorroIsAdopted_ReturnsEmptyValidation() + { + // Arrange + var adocaoService = new AdocaoService( + _uowMock.Object, + _cachorroRepositoryMock.Object, + _tutorRepositoryMock.Object); + + var cachorro = _cachorroFixture.CreateManyCachorroWithTutor(1)[0]; + + _cachorroRepositoryMock + .Setup(x => x.GetByIdAsync(cachorro.Id, CancellationToken.None)) + .ReturnsAsync(cachorro); + + // Act + var result = await adocaoService.DevolverAdocaoAsync(cachorro.Id); + + // Assert + Assert.False(result.Any()); + + _cachorroRepositoryMock.Verify(x => x.UpdateAsync(cachorro, CancellationToken.None), Times.Once); + _uowMock.Verify(x => x.CommitAndSaveChangesAsync(CancellationToken.None), Times.Once); + } + } +} diff --git a/src/Tests/DEPLOY.Cachorro.Domain.Tests/CachorroServiceTest.cs b/src/Tests/DEPLOY.Cachorro.Domain.Tests/CachorroServiceTest.cs index a29963d..2830165 100644 --- a/src/Tests/DEPLOY.Cachorro.Domain.Tests/CachorroServiceTest.cs +++ b/src/Tests/DEPLOY.Cachorro.Domain.Tests/CachorroServiceTest.cs @@ -1,10 +1,13 @@ -using DEPLOY.Cachorro.Base.Tests; +using Bogus; +using DEPLOY.Cachorro.Base.Tests; using DEPLOY.Cachorro.Domain.Aggregates.Cachorro.Interfaces.Repositories; using DEPLOY.Cachorro.Domain.Aggregates.Cachorro.Interfaces.Services; using DEPLOY.Cachorro.Domain.Aggregates.Cachorro.Services; +using DEPLOY.Cachorro.Domain.Aggregates.Cachorro.Validations; using DEPLOY.Cachorro.Infra.Repository.Repositories.Base; using FluentAssertions; using FluentValidation; +using FluentValidation.TestHelper; using Moq; using System.Diagnostics.CodeAnalysis; @@ -13,32 +16,40 @@ namespace DEPLOY.Cachorro.Domain.Tests [ExcludeFromCodeCoverage] public class CachorroServiceTest : IClassFixture { - private readonly CachorroDtoFixture _cachorroDtoFixture; + private readonly CachorroFixture _cachorroFixture; + private readonly Mock _cachorroRepositoryMock; private readonly Mock _uowMock; - private InlineValidator _validator; - private ICachorroService _cachorroService; - public CachorroServiceTest() + private readonly CachorroValidator _cachorroValidator; + + public CachorroServiceTest(CachorroFixture cachorroFixture) { - _cachorroDtoFixture = new(); + _cachorroFixture = cachorroFixture; _cachorroRepositoryMock = new Mock(); _uowMock = new Mock(); + _cachorroValidator = new CachorroValidator(_cachorroRepositoryMock.Object); } [Fact] - public async Task CreateCachorroAsync_ReturnsCreatedCachorro() + public async Task GivenCreateCachorroAsync_WhenCachorroIsValid_ThanReturnsCreatedCachorro() { // Arrange - _validator = new InlineValidator(); + InlineValidator _validator = new InlineValidator(); - _cachorroService = new CachorroService(_uowMock.Object, _validator, _cachorroRepositoryMock.Object); + ICachorroService _cachorroService = new CachorroService( + _uowMock.Object, + _validator, + _cachorroRepositoryMock.Object); - var expectedCachorro = _cachorroDtoFixture.CreateManyCachorroWithTutor(1)[0]; + var expectedCachorro = _cachorroFixture + .CreateManyCachorroWithTutor(1)[0]; - _cachorroRepositoryMock.Setup(repo => repo.InsertAsync(It.IsAny(), + _cachorroRepositoryMock.Setup(repo => repo + .InsertAsync( + It.IsAny(), CancellationToken.None)) - .ReturnsAsync(expectedCachorro); + .ReturnsAsync(expectedCachorro); // Act var createdCachorro = await _cachorroService.CreateAsync(expectedCachorro); @@ -47,8 +58,57 @@ public async Task CreateCachorroAsync_ReturnsCreatedCachorro() Assert.NotNull(createdCachorro); Assert.Equal(expectedCachorro, createdCachorro); - _cachorroRepositoryMock.Verify(repo => repo.InsertAsync(It.IsAny(), - CancellationToken.None), Times.Once); + _cachorroRepositoryMock.Verify(repo => repo + .InsertAsync( + It.IsAny(), + CancellationToken.None) + , Times.Once); + + // Assert using FluentAssertions + createdCachorro + .Should() + .NotBeNull(); + + createdCachorro + .Should() + .BeEquivalentTo(expectedCachorro); + } + + [Fact] + public async Task GivenCreateCachorroAsync_WhenErroInLine_ThanReturnsError() + { + // Arrange + InlineValidator _validator = new InlineValidator(); + _validator.RuleFor(x => x.Nome) + .NotEmpty() + .WithMessage("Nome é obrigatório"); + + ICachorroService _cachorroService = new CachorroService( + _uowMock.Object, + _validator, + _cachorroRepositoryMock.Object); + + var expectedCachorro = _cachorroFixture + .CreateManyCachorroWithTutor(1)[0]; + + _cachorroRepositoryMock + .Setup(repo => repo.InsertAsync( + It.IsAny(), + CancellationToken.None)) + .ReturnsAsync(expectedCachorro); + + // Act + var createdCachorro = await _cachorroService + .CreateAsync(expectedCachorro); + + // Assert + Assert.NotNull(createdCachorro); + Assert.Equal(expectedCachorro, createdCachorro); + + _cachorroRepositoryMock + .Verify(repo => repo.InsertAsync( + It.IsAny(), + CancellationToken.None), Times.Once); // Assert using FluentAssertions createdCachorro.Should().NotBeNull(); @@ -56,22 +116,36 @@ public async Task CreateCachorroAsync_ReturnsCreatedCachorro() } [Fact] - public async Task CreateCachorroAsync_WithInvalidCachorro_ReturnsCreatedCachorro() + public async Task GivenCreateCachorroAsync_WhenWithInvalidCachorro_ThanReturnsCreatedCachorro() { // Arrange - _validator = new InlineValidator(); + InlineValidator _validator = new InlineValidator(); - _cachorroService = new CachorroService(_uowMock.Object, _validator, _cachorroRepositoryMock.Object); + ICachorroService _cachorroService = new CachorroService( + _uowMock.Object, + _validator, + _cachorroRepositoryMock.Object); - var expectedCachorro = _cachorroDtoFixture.CreateManyCachorroWithTutor(1)[0]; + var expectedCachorro = _cachorroFixture + .CreateManyCachorroWithNameError(1)[0]; - _cachorroRepositoryMock.Setup(repo => repo.InsertAsync( - It.IsAny(), - CancellationToken.None)) - .ReturnsAsync(expectedCachorro); + /*var cachorroValidator = */ + _cachorroValidator +.TestValidate(expectedCachorro); + + //cachorroValidator + // .ShouldHaveValidationErrorFor(c => c.Nome) + // .WithErrorMessage("Nome é obrigatório"); + + _cachorroRepositoryMock + .Setup(repo => repo.InsertAsync( + It.IsAny(), + CancellationToken.None)) + .ReturnsAsync(expectedCachorro); // Act - var createdCachorro = await _cachorroService.CreateAsync(expectedCachorro); + var createdCachorro = await _cachorroService + .CreateAsync(expectedCachorro); // Assert Assert.NotNull(createdCachorro); @@ -83,8 +157,13 @@ public async Task CreateCachorroAsync_WithInvalidCachorro_ReturnsCreatedCachorro Times.Once); // Assert using FluentAssertions - createdCachorro.Should().NotBeNull(); - createdCachorro.Should().BeEquivalentTo(expectedCachorro); + createdCachorro + .Should() + .NotBeNull(); + + createdCachorro + .Should() + .BeEquivalentTo(expectedCachorro); } } } diff --git a/src/Tests/DEPLOY.Cachorro.Domain.Tests/DEPLOY.Cachorro.Domain.Tests.csproj b/src/Tests/DEPLOY.Cachorro.Domain.Tests/DEPLOY.Cachorro.Domain.Tests.csproj index bd7c7ad..01f6df7 100644 --- a/src/Tests/DEPLOY.Cachorro.Domain.Tests/DEPLOY.Cachorro.Domain.Tests.csproj +++ b/src/Tests/DEPLOY.Cachorro.Domain.Tests/DEPLOY.Cachorro.Domain.Tests.csproj @@ -1,37 +1,37 @@ - - net8.0 - enable - enable + + net8.0 + enable + enable + false + true + $(NoWarn);8600 + - false - true - + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - + + + + - - - - - - - - + + + diff --git a/src/Tests/DEPLOY.Cachorro.Domain.Tests/TutorServiceTest.cs b/src/Tests/DEPLOY.Cachorro.Domain.Tests/TutorServiceTest.cs new file mode 100644 index 0000000..e365607 --- /dev/null +++ b/src/Tests/DEPLOY.Cachorro.Domain.Tests/TutorServiceTest.cs @@ -0,0 +1,97 @@ +using DEPLOY.Cachorro.Base.Tests; +using DEPLOY.Cachorro.Domain.Aggregates.Cachorro.Validations; +using DEPLOY.Cachorro.Domain.Aggregates.Tutor.Interfaces.Repositories; +using DEPLOY.Cachorro.Domain.Aggregates.Tutor.Interfaces.Services; +using DEPLOY.Cachorro.Domain.Aggregates.Tutor.Services; +using DEPLOY.Cachorro.Domain.Aggregates.Tutor.Validations; +using DEPLOY.Cachorro.Infra.Repository.Repositories.Base; +using FluentAssertions; +using FluentValidation; +using FluentValidation.TestHelper; +using Moq; +using System.Diagnostics.CodeAnalysis; + +namespace DEPLOY.Cachorro.Domain.Tests +{ + [ExcludeFromCodeCoverage] + public class TutorServiceTest : IClassFixture + { + private readonly TutorFixture _tutorFixture; + private readonly Mock _tutorRepositoryMock; + private readonly Mock _uowMock; + + private TutorValidator? _tutorValidator; + + public TutorServiceTest(TutorFixture tutorFixture) + { + _tutorFixture = tutorFixture; + _tutorRepositoryMock = new Mock(); + _uowMock = new Mock(); + } + + [Fact] + public async Task GivenCreateTutorAsync_WhenTutorIsValid_ThanReturnsCreatedTutor() + { + // Arrange + var expectedTutor = _tutorFixture.CreateManyTutores(1)[0]; + + _tutorRepositoryMock + .Setup(repo => repo.InsertAsync( + It.IsAny(), + CancellationToken.None)) + .ReturnsAsync(expectedTutor); + + InlineValidator _validator = new InlineValidator(); + //_validator.RuleFor(t => t.CPF).Must(t => t.Length == 11).WithMessage("CPF inválido"); + + ITutorService _tutorService = new TutorService(_uowMock.Object, _validator, _tutorRepositoryMock.Object); + + // Act + var createdTutor = await _tutorService.CreateAsync(expectedTutor); + + // Assert + Assert.NotNull(createdTutor); + Assert.Equal(expectedTutor, createdTutor); + + _tutorRepositoryMock.Verify(repo => repo.InsertAsync(It.IsAny(), + CancellationToken.None), Times.Once); + + // Assert using FluentAssertions + createdTutor.Should().NotBeNull(); + createdTutor.Should().BeEquivalentTo(expectedTutor); + } + + [Fact] + public async Task GivenCreateTutorAsync_WhenTutorIsNotValid_ThanReturnsCreatedTutor() + { + // Arrange + var expectedTutor = _tutorFixture.CreateManyTutoresWithERRORCPF(1)[0]; + + _tutorRepositoryMock.Setup(repo => repo.InsertAsync( + It.IsAny(), + CancellationToken.None)) + .ReturnsAsync(expectedTutor); + InlineValidator _validator = new InlineValidator(); + ITutorService _tutorService = new TutorService(_uowMock.Object, _validator, _tutorRepositoryMock.Object); + + // Act + var createdTutor = await _tutorService.CreateAsync(expectedTutor); + + // Assert + Assert.NotNull(createdTutor); + Assert.Equal(expectedTutor, createdTutor); + + var item = _validator.TestValidate(expectedTutor); + //item.ShouldHaveValidationErrorFor(t => t.CPF).WithErrorMessage("CPF inválido"); + + _tutorRepositoryMock.Verify(repo => repo.InsertAsync( + It.IsAny(), + CancellationToken.None), + Times.Once); + + // Assert using FluentAssertions + createdTutor.Should().NotBeNull(); + createdTutor.Should().BeEquivalentTo(expectedTutor); + } + } +} diff --git a/src/Tests/DEPLOY.Cachorro.Repository.Tests/CachorroRepositoryTest.cs b/src/Tests/DEPLOY.Cachorro.Repository.Tests/CachorroRepositoryTest.cs index 788aeac..6ed2dd4 100644 --- a/src/Tests/DEPLOY.Cachorro.Repository.Tests/CachorroRepositoryTest.cs +++ b/src/Tests/DEPLOY.Cachorro.Repository.Tests/CachorroRepositoryTest.cs @@ -6,19 +6,23 @@ using MockQueryable.Moq; using Moq; using Moq.EntityFrameworkCore; +using System.Diagnostics.CodeAnalysis; namespace DEPLOY.Cachorro.Repository.Tests { + [ExcludeFromCodeCoverage] public class CachorroRepositoryTest : IClassFixture { private readonly CachorroFixture _cachorroFixture; - public CachorroRepositoryTest() + + public CachorroRepositoryTest( + CachorroFixture cachorroFixture) { - _cachorroFixture = new(); + _cachorroFixture = cachorroFixture; } [Fact] - public async Task GetAllAsync_ReturnsAllCachorrosInMemoryDatabase() + public async Task GivenGetAllAsync_ReturnsAllCachorrosInMemoryDatabase() { // Arrange var dbContextOptions = new DbContextOptionsBuilder() @@ -118,5 +122,238 @@ public async Task GivenGetAllAsync_WhenRequestIsValid_ThenReturnNoItems() // Assert Assert.Equal(items.Count, result.Count()); } + + [Fact] + public async Task GivenGetByIdAsync_WhenCachorroExists_ThanReturnSuccess() + { + // Arrange + var items = _cachorroFixture.CreateManyCachorroWithTutor(1); + + var ContextMock = new Mock(); + + ContextMock.Setup>(x => x.Set()) + .ReturnsDbSet(items); + + var repository = new CachorroRepository(ContextMock.Object); + + // Act + var item = await repository.GetByIdAsync(items[0].Id, CancellationToken.None); + + // Assert + Assert.NotNull(item); + } + + [Fact] + public async Task GivenInsertAsync_WhenCachorroIsValid_ThanReturnSuccess() + { + // Arrange + var items = _cachorroFixture.CreateManyCachorroWithTutor(1); + + var ContextMock = new Mock(); + ContextMock.Setup>(x => x.Set()) + .ReturnsDbSet(items); + + var repository = new CachorroRepository(ContextMock.Object); + + // Act + var item = await repository.InsertAsync(items[0], CancellationToken.None); + + // Assert + Assert.NotNull(item); + + } + + [Fact] + public async Task GivenInsertAsync_WhenCachorroIsNotValid_ThanReturnException() + { + // Arrange + var items = _cachorroFixture.CreateManyCachorroWithNameError(1); + + var ContextMock = new Mock(); + ContextMock.Setup>(x => x.Set()) + .ReturnsDbSet(items); + + var repository = new CachorroRepository(ContextMock.Object); + + // Act + var item = await repository.InsertAsync(It.IsAny(), CancellationToken.None); + + // Assert + Assert.Null(item); + } + + [Fact] + public async Task GivenUpdateAsync_WhenCachorroExists_ThanSuccess() + { + // Arrange + var entities = _cachorroFixture.CreateManyCachorroWithoutTutor(1); + + var dbContextOptions = new DbContextOptionsBuilder() + .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) + .ConfigureWarnings(b => b.Ignore(InMemoryEventId.TransactionIgnoredWarning)) + .Options; + + var dbContext = new CachorroDbContext(dbContextOptions); + await dbContext.Database.EnsureCreatedAsync(); + + var repository = new Mock(dbContext) { CallBase = true }; + + var cachorros = _cachorroFixture.CreateManyCachorroWithTutor(2); + + await dbContext.AddRangeAsync(cachorros); + await dbContext.SaveChangesAsync(); + + // Act + await repository.Object.UpdateAsync(entities[0], default); + + // Assert + Assert.Equal(EntityState.Modified, dbContext.Entry(entities[0]).State); + + dbContext.Database.EnsureDeleted(); + } + + //[Fact] + //public async Task GivenUpdateAsync_WhenCachorroExists_ThanSuccess() + //{ + // // Assume you have an existing entity 'Cachorro' + // var entities = _cachorroFixture.CreateManyCachorroWithoutTutor(1); + // var dbSetMock = entities.AsQueryable().BuildMockDbSet(); + + // dbSetMock.Setup(x => x.FindAsync(1)) + // .ReturnsAsync(entities[0]); + + // var dbContextMock = new Mock(MockBehavior.Strict); + // var stateManager = new Mock(); + // var changeDetector = new Mock(); + // var model = new Mock(); + // var graphIterator = new Mock(); + // var mockEntry = new Mock>(); + + // dbSetMock + // .Setup(x => x.FindAsync(1)) + // .ReturnsAsync(_cachorroFixture.CreateManyCachorroWithTutor(1)[0]); + + // dbContextMock + // .Setup(c => c.Set()) + // .Returns(dbSetMock.Object); + + // //var changeTracker = new Mock(); + + // //dbContextMock + // // .Setup(ct => ct.ChangeTracker) + // // .Returns(new ChangeTracker(dbContextMock.Object, stateManager.Object, changeDetector.Object, model.Object, graphIterator.Object)); + + // dbContextMock + // .Setup>(x => x.Set()) + // .ReturnsDbSet(entities); + + // dbContextMock + // .Setup(x => x.Set() + // .FindAsync(new object[] { entities[0].Id }, default)) + // .ReturnsAsync(entities[0]); + + // // Create a custom EntityEntry mock + // mockEntry + // .Setup(e => e.State) + // .Returns(EntityState.Modified); + + // mockEntry + // .Setup(e => e.Entity) + // .Returns(entities[0]); // Set the tracked entity + + + // dbContextMock.Setup(c => c.Entry(entities[0])) + // .Returns(mockEntry.Object); + + // var repository = new CachorroRepository(dbContextMock.Object); + + // // Act + // await repository.UpdateAsync(entities[0], CancellationToken.None); + + // // Assert + // dbContextMock.Verify(x => x.Update(entities[0]), Times.Once); + // dbContextMock.Verify(x => x.SaveChangesAsync(CancellationToken.None), Times.Never); + //} + + [Fact] + public async Task GivenDeleteAsync_WhenCachorroExists_ThenReturnTrue() + { + // Arrange + var items = _cachorroFixture.CreateManyCachorroWithoutTutor(1); + + var ContextMock = new Mock(); + + ContextMock + .Setup>(x => x.Set()) + .ReturnsDbSet(items); + + ContextMock + .Setup(x => x.Set() + .FindAsync(new object[] { items[0].Id }, default)) + .ReturnsAsync(items[0]); + + var repository = new CachorroRepository(ContextMock.Object); + + // Act + var result = await repository.DeleteAsync(items[0].Id, CancellationToken.None); + + // Assert + Assert.True(result); + } + + [Fact] + public async Task GivenDeleteAsync_WhenCachorroNotExist_ThenReturnFalse() + { + // Arrange + var items = _cachorroFixture.CreateManyCachorroWithoutTutor(1); + + var ContextMock = new Mock(); + + ContextMock + .Setup>(x => x.Set()) + .ReturnsDbSet(items); + + ContextMock + .Setup(x => x.Set() + .FindAsync(new object[] { items[0].Id }, default)) + .ReturnsAsync(null as Domain.Aggregates.Cachorro.Entities.Cachorro); + + var repository = new CachorroRepository(ContextMock.Object); + + // Act + var result = await repository.DeleteAsync(Guid.NewGuid(), CancellationToken.None); + + // Assert + Assert.False(result); + } + + [Fact] + public async Task GivenGetByKeyAsync_WhenExitsCachorros_ThanCompareIdsAndFoundObject() + { + // Arrange + int qtdCachorros = 10; + var cachorros = _cachorroFixture.CreateManyCachorroWithoutTutor(qtdCachorros); + + var ContextMock = new Mock(); + + ContextMock + .Setup>(x => x.Set()) + .ReturnsDbSet(cachorros); + + var repository = new CachorroRepository(ContextMock.Object); + + int indiceAleatorio = new Random().Next(0, cachorros.Count); + + // Acessar o item correspondente ao índice aleatório na lista + var itemAleatorio = cachorros[indiceAleatorio - 1]; + + // Act + var result = await repository.GetByKeyAsync(x => x.Id == itemAleatorio.Id, CancellationToken.None); + + // Assert + Assert.NotNull(result); + Assert.Single(result); + Assert.Equal(itemAleatorio.Id, result[0].Id); + } } } diff --git a/src/Tests/DEPLOY.Cachorro.Repository.Tests/DEPLOY.Cachorro.Repository.Tests.csproj b/src/Tests/DEPLOY.Cachorro.Repository.Tests/DEPLOY.Cachorro.Repository.Tests.csproj index 7265693..e850d5e 100644 --- a/src/Tests/DEPLOY.Cachorro.Repository.Tests/DEPLOY.Cachorro.Repository.Tests.csproj +++ b/src/Tests/DEPLOY.Cachorro.Repository.Tests/DEPLOY.Cachorro.Repository.Tests.csproj @@ -1,39 +1,41 @@ - - net8.0 - enable - enable + + net8.0 + enable + enable - false - true - + false + true + - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + - - - - - + + + + + - - - + + + diff --git a/src/Tests/DEPLOY.Cachorro.Repository.Tests/TutorRepositoryTest.cs b/src/Tests/DEPLOY.Cachorro.Repository.Tests/TutorRepositoryTest.cs new file mode 100644 index 0000000..306d4a1 --- /dev/null +++ b/src/Tests/DEPLOY.Cachorro.Repository.Tests/TutorRepositoryTest.cs @@ -0,0 +1,125 @@ +using DEPLOY.Cachorro.Base.Tests; +using DEPLOY.Cachorro.Infra.Repository; +using DEPLOY.Cachorro.Infra.Repository.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; +using MockQueryable.Moq; +using Moq; +using Moq.EntityFrameworkCore; +using System.Diagnostics.CodeAnalysis; + +namespace DEPLOY.Cachorro.Repository.Tests +{ + [ExcludeFromCodeCoverage] + public class TutorRepositoryTest : IClassFixture + { + private readonly TutorFixture _tutorFixture; + + public TutorRepositoryTest(TutorFixture tutorFixture) + { + _tutorFixture = tutorFixture; + } + + [Fact] + public async Task GivenGetAllAsync_ReturnsAllTutoresInMemoryDatabase() + { + // Arrange + var dbContextOptions = new DbContextOptionsBuilder() + .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) + .ConfigureWarnings(b => b.Ignore(InMemoryEventId.TransactionIgnoredWarning)) + .Options; + + var dbContext = new CachorroDbContext(dbContextOptions); + await dbContext.Database.EnsureCreatedAsync(); + + var repository = new Mock(dbContext) { CallBase = true }; + + var tutores = _tutorFixture.CreateManyTutores(2); + + await dbContext.AddRangeAsync(tutores); + await dbContext.SaveChangesAsync(); + + // Act + var result = await repository.Object.GetAllAsync(); + + // Assert + Assert.Equal(tutores.Count, result.Count()); + + foreach (var tutor in tutores) + { + Assert.Contains(result, c => c.Id == tutor.Id); + } + + dbContext.Database.EnsureDeleted(); + } + + //MockQueryable.Moq + [Fact] + public async Task GivenGetAllAsync_WhenRequestIsValidWithMockQueryableMoq_ThenReturnItems() + { + // Arrange + var entities = _tutorFixture.CreateManyTutores(2); + var dbSetMock = entities.AsQueryable().BuildMockDbSet(); + + dbSetMock.Setup(x => x.FindAsync(1)) + .ReturnsAsync(_tutorFixture.CreateManyTutores(1)[0]); + + var dbContextMock = new Mock(MockBehavior.Strict); + dbContextMock + .Setup(c => c.Set()) + .Returns(dbSetMock.Object); + + var repository = new TutorRepository(dbContextMock.Object); + + // Act + var result = await repository.GetAllAsync(); + + // Assert + Assert.Equal(entities.Count, result.Count()); + } + + //Moq.EntityFrameworkCore + [Fact] + public async Task GivenGetAllAsync_WhenRequestIsValidWithMoqEntityFrameworkCore_ThenReturnItems() + { + // Arrange + var items = _tutorFixture.CreateManyTutores(2); + + var ContextMock = new Mock(); + ContextMock.Setup>(x => x.Set()) + .ReturnsDbSet(items); + + var repository = new TutorRepository(ContextMock.Object); + + // Act + var result = await repository.GetAllAsync(); + + // Assert + Assert.Equal(items.Count, result.Count()); + + foreach (var item in items) + { + Assert.Contains(result, c => c.Id == item.Id); + } + } + + [Fact] + public async Task GivenGetAllAsync_WhenRequestIsValid_ThenReturnNoItems() + { + // Arrange + var items = _tutorFixture.CreateManyTutores(0); + + var ContextMock = new Mock(); + ContextMock.Setup>(x => x.Set()) + .ReturnsDbSet(items); + + var repository = new TutorRepository(ContextMock.Object); + + // Act + var result = await repository.GetAllAsync(); + + // Assert + Assert.Equal(items.Count, result.Count()); + } + } +}