Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
3013027
Move registration endpoint to new RegisterController
Foqsz Apr 7, 2026
42bcc0f
Update password and email validation in client use cases
Foqsz Apr 7, 2026
54ed0e3
Update login error messages to cover email and password
Foqsz Apr 7, 2026
da1174a
Refactor product use cases to use authenticated user ID
Foqsz Apr 8, 2026
f725125
Add unit tests for UpdateClientUseCase and refactor helpers
Foqsz Apr 8, 2026
b353c73
Integrate RabbitMQ messaging for client creation events
Foqsz Apr 9, 2026
8e76df4
#
Foqsz Apr 11, 2026
7af0a1c
#
Foqsz Apr 13, 2026
b6f1b0b
Merge branch 'master' into develop
Foqsz Apr 13, 2026
03daf8b
#
Foqsz Apr 14, 2026
68f5577
Merge branch 'develop' of https://github.com/Foqsz/ProductClientHub i…
Foqsz Apr 14, 2026
91cba1d
#
Foqsz Apr 15, 2026
f350e18
Merge branch 'master' into develop
Foqsz Apr 15, 2026
a57d179
#
Foqsz Apr 15, 2026
5359eb5
Merge branch 'develop' of https://github.com/Foqsz/ProductClientHub i…
Foqsz Apr 15, 2026
9c4ad17
#
Foqsz Apr 16, 2026
e08b4a6
Merge branch 'master' into develop
Foqsz Apr 16, 2026
8111877
feat: remove open api
Foqsz Apr 19, 2026
1b9657a
feat: update client integration test.
Foqsz Apr 20, 2026
7e38534
feat: Moq realizado para a integração test
Foqsz Apr 20, 2026
fee32f4
feat: teste para caso ja exista um e-mail na tentativa de update.
Foqsz Apr 20, 2026
ffa46d8
Merge branch 'master' into develop
Foqsz Apr 20, 2026
00cb12a
feat: adicionando logged user ao teste
Foqsz Apr 22, 2026
5c6fe04
feat: agora nao preciso mais do clientid no endpoint, ja recebo via l…
Foqsz Apr 22, 2026
dcb366e
Merge branch 'develop' of https://github.com/Foqsz/ProductClientHub i…
Foqsz Apr 22, 2026
adeeacb
fix: fiz esse ajuste pq mesmo se o usuario tentasse trocar seu e-mail…
Foqsz Apr 24, 2026
68a0b64
feat: teste de integração para o update do usuario. Tudo ok, devidame…
Foqsz Apr 24, 2026
7a77730
Merge branch 'master' into develop
Foqsz Apr 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions CommonTestUtilities/LoggedUser/LoggedUserBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using Moq;
using ProductClientHub.Domain.Entities;
using ProductClientHub.Domain.Services.LoggedUser;

namespace CommonTestUtilities.LoggedUser;

public class LoggedUserBuilder
{
public static ILoggedUser Build(Client client)
{
var mock = new Mock<ILoggedUser>();

mock.Setup(c => c.User()).ReturnsAsync(client);

return mock.Object;
}
}
9 changes: 9 additions & 0 deletions CommonTestUtilities/Token/JwtTokenGeneratorBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using ProductClientHub.Domain.Security.Tokens;
using ProductClientHub.Infrastructure.Security.Tokens.Acess.Generator;

namespace CommonTestUtilities.Token;

public class JwtTokenGeneratorBuilder
{
public static IAccessTokenGenerator Build() => new JwtTokenGenerator(expirationTimeMinutes: 5, signingKey: "ttttttttttttttttttttttttttttttttttttttttt");
}
5 changes: 2 additions & 3 deletions ProductClientHub.API/Controllers/ClientsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,12 @@ public async Task<IActionResult> ChangePassword([FromRoute] Guid clientId, [From
}

[HttpPut]
[Route("{clientId:guid}")]
[ProducesResponseType(typeof(ResponseClientUpdatedJson), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ResponseErrorMessagesJson), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(ResponseErrorMessagesJson), StatusCodes.Status400BadRequest)]
public async Task<IActionResult> Update([FromRoute] Guid clientId, [FromBody] RequestShortClientJson request, [FromServices] IUpdateClientUseCase useCase)
public async Task<IActionResult> Update([FromBody] RequestShortClientJson request, [FromServices] IUpdateClientUseCase useCase)
{
var response = await useCase.Execute(clientId, request);
var response = await useCase.Execute(request);

return Ok(response);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ namespace ProductClientHub.Application.UseCases.Users.Update;

public interface IUpdateClientUseCase
{
Task<ResponseClientUpdatedJson> Execute(Guid clientId, RequestShortClientJson request);
Task<ResponseClientUpdatedJson> Execute(RequestShortClientJson request);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using ProductClientHub.Domain.Extensions;
using ProductClientHub.Domain.Repositories.Client;
using ProductClientHub.Domain.Repositories.UnitOfWork;
using ProductClientHub.Domain.Services.LoggedUser;
using ProductClientHub.Exceptions.ExceptionsBase;

namespace ProductClientHub.Application.UseCases.Users.Update;
Expand All @@ -14,25 +15,30 @@ public class UpdateClientUseCase : IUpdateClientUseCase
private readonly IClientWriteOnlyRepository _clientWriteOnlyRepository;
private readonly IClientReadOnlyRepository _clientReadOnlyRepository;
private readonly IUnitOfWork _unitOfWork;
private readonly ILoggedUser _loggedUser;

public UpdateClientUseCase(IClientWriteOnlyRepository clientWriteOnlyRepository,
IClientReadOnlyRepository clientReadOnlyRepository,
IUnitOfWork unitOfWork)
IUnitOfWork unitOfWork,
ILoggedUser loggedUser)
{
_clientWriteOnlyRepository = clientWriteOnlyRepository;
_clientReadOnlyRepository = clientReadOnlyRepository;
_unitOfWork = unitOfWork;
_loggedUser = loggedUser;
}

public async Task<ResponseClientUpdatedJson> Execute(Guid clientId, RequestShortClientJson request)
public async Task<ResponseClientUpdatedJson> Execute(RequestShortClientJson request)
{
Validate(request);

var client = await _clientReadOnlyRepository.GetById(clientId) ?? throw new NotFoundException(ResourceMessagesExceptions.CLIENT_NOCONTENT);
var userLogged = await _loggedUser.User();

var client = await _clientReadOnlyRepository.GetById(userLogged.Id) ?? throw new NotFoundException(ResourceMessagesExceptions.CLIENT_NOCONTENT);

var emailExist = await _clientReadOnlyRepository.EmailAlreadyExists(request.Email);

if(emailExist is not null)
if(emailExist is not null && emailExist.Id != userLogged.Id)
throw new EmailAlreadyExistsException(ResourceMessagesExceptions.EMAIL_INVALID);

client.Name = request.Name;
Expand Down
13 changes: 8 additions & 5 deletions UseCase.Test/Client/Update/UpdateClientUseCaseTest.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
using CommonTestUtilities.Cryptografhy;
using CommonTestUtilities.Entities;
using CommonTestUtilities.LoggedUser;
using CommonTestUtilities.Repositories;
using CommonTestUtilities.Requests;
using ProductClientHub.Application.UseCases.Users.Update;
using ProductClientHub.Domain.Extensions;
using ProductClientHub.Exceptions.ExceptionsBase;
using ProductClientHub.Infrastructure.Services;
using Shouldly;

namespace UseCase.Test.Client.Update;
Expand All @@ -20,7 +22,7 @@ public async Task UpdateClient_Sucess()

var useCase = CreateUseCase(client, emailExistsTest: false, clientExist: true);

var result = await useCase.Execute(client.Id, clientRequest);
var result = await useCase.Execute(clientRequest);

result.ShouldNotBeNull();
result.ShouldSatisfyAllConditions(
Expand All @@ -38,7 +40,7 @@ public async Task UpdateClient_Error_ClientNotExists()

var useCase = CreateUseCase(client, emailExistsTest: false, clientExist: false);

var resultException = await Should.ThrowAsync<NotFoundException>(async () => await useCase.Execute(client.Id, clientRequest));
var resultException = await Should.ThrowAsync<NotFoundException>(async () => await useCase.Execute(clientRequest));

resultException.ShouldNotBeNull();
resultException.ShouldSatisfyAllConditions(() => resultException.Message.ShouldBe(ResourceMessagesExceptions.CLIENT_NOCONTENT));
Expand All @@ -53,7 +55,7 @@ public async Task UpdateClient_Error_EmailExist()

var useCase = CreateUseCase(client, emailExistsTest: true, clientExist: true);

var resultException = await Should.ThrowAsync<EmailAlreadyExistsException>(async () => await useCase.Execute(client.Id, clientRequest));
var resultException = await Should.ThrowAsync<EmailAlreadyExistsException>(async () => await useCase.Execute(clientRequest));

resultException.ShouldNotBeNull();
resultException.ShouldSatisfyAllConditions(() => resultException.Message.ShouldBe(ResourceMessagesExceptions.EMAIL_INVALID));
Expand All @@ -64,13 +66,14 @@ private static UpdateClientUseCase CreateUseCase(ProductClientHub.Domain.Entitie
var clientWriteOnlyRepository = ClientWriteOnlyRepositoryBuilder.Build();
var clientReadOnlyRepository = new ClientReadOnlyRepositoryBuilder();
var unitOfWork = UnitOfWorkBuilder.Build();
var loggedUser = LoggedUserBuilder.Build(client!);

if(client is not null && clientExist.IsTrue())
if (client is not null && clientExist.IsTrue())
clientReadOnlyRepository.GetById(client);

if(emailExistsTest.IsTrue())
clientReadOnlyRepository.EmailAlreadyExists(client);

return new UpdateClientUseCase(clientWriteOnlyRepository, clientReadOnlyRepository.Build(), unitOfWork);
return new UpdateClientUseCase(clientWriteOnlyRepository, clientReadOnlyRepository.Build(), unitOfWork, loggedUser);
}
}
54 changes: 38 additions & 16 deletions WebApi.Test/Client/Update/UpdateClientIntegrationTest.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
using Microsoft.AspNetCore.Mvc.Testing;
using CommonTestUtilities.Token;
using Microsoft.AspNetCore.Mvc.Testing;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Shouldly;
using System.Net;
using System.Net.Http.Headers;
using System.Text;
using ClientEntity = ProductClientHub.Domain.Entities.Client;

namespace WebApi.Test.Client.Update;
Expand All @@ -18,8 +22,6 @@ public UpdateClientIntegrationTest(CustomWebApplicationFactory factory)
{
BaseAddress = new Uri("https://localhost")
});

_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "fake-token");
}

[Fact]
Expand All @@ -30,13 +32,17 @@ public async Task UpdateClientTest_Sucess()
new ClientEntity
{
Name = "Update Client",
Email = "updateclient@gmail.com"
}
Email = "updateclientTEST@gmail.com"
},
];

client[0].Id = Guid.NewGuid();

var response = await _httpClient.PutAsync($"/api/clients/{client[0].Id}", new StringContent(JsonConvert.SerializeObject(client[0]), System.Text.Encoding.UTF8, "application/json"));
var token = JwtTokenGeneratorBuilder.Build().Generate(client[0].Id);

_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

var response = await _httpClient.PutAsync($"/api/clients", new StringContent(JsonConvert.SerializeObject(client[0]), System.Text.Encoding.UTF8, "application/json"));

response.StatusCode.ShouldBe(System.Net.HttpStatusCode.OK);
}
Expand All @@ -47,23 +53,39 @@ public async Task UpdateClientTest_Error_EmailExist()
var client = _factory.ClientsToReturn =
[
new ClientEntity
{
Name = "Update Client",
Email = "updateclient@gmail.com"
},
{
Name = "User Logado",
Email = "user1@gmail.com"
},

new ClientEntity
{
Name = "Update Client 2",
Email = "updateclient@gmail.com"
}
{
Name = "Outro User",
Email = "user2@gmail.com"
}
];

client[0].Id = Guid.NewGuid();
client[1].Id = Guid.NewGuid();

var response = await _httpClient.PutAsync($"/api/clients/{client[1].Id}", new StringContent(JsonConvert.SerializeObject(client[1]), System.Text.Encoding.UTF8, "application/json"));
var token = JwtTokenGeneratorBuilder.Build().Generate(client[0].Id);

_httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", token);

var updateRequest = new ClientEntity
{
Id = client[0].Id,
Name = "Update",
Email = client[1].Email
};

var response = await _httpClient.PutAsync(
"/api/clients",
new StringContent(JsonConvert.SerializeObject(updateRequest),
Encoding.UTF8,
"application/json"));

response.StatusCode.ShouldBe(System.Net.HttpStatusCode.BadRequest);
response.StatusCode.ShouldBe(HttpStatusCode.BadRequest);
}
}
19 changes: 19 additions & 0 deletions WebApi.Test/CustomWebApplicationFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using ProductClientHub.Domain.Repositories.Client;
using ProductClientHub.Domain.Repositories.UnitOfWork;
using ProductClientHub.Domain.Security.Tokens;
using ProductClientHub.Domain.Services.LoggedUser;
using ClientEntity = ProductClientHub.Domain.Entities.Client;

namespace WebApi.Test;
Expand Down Expand Up @@ -45,6 +46,7 @@ protected override void ConfigureWebHost(IWebHostBuilder builder)
services.RemoveAll<IAccessTokenValidator>();
services.RemoveAll<IDeleteClientRepository>();
services.RemoveAll<IUnitOfWork>();
services.RemoveAll<ILoggedUser>();

// Remove all hosted services to prevent background services from running during tests
services.RemoveAll(typeof(IHostedService));
Expand All @@ -54,6 +56,7 @@ protected override void ConfigureWebHost(IWebHostBuilder builder)
services.AddScoped<IAccessTokenValidator, FakeAccessTokenValidator>();
services.AddScoped<IDeleteClientRepository, FakeDeleteClientRepository>();
services.AddScoped<IUnitOfWork, FakeUnitOfWork>();
services.AddScoped<ILoggedUser, FakeLoggedUser>();
});
}

Expand Down Expand Up @@ -137,6 +140,22 @@ public Task Add(ClientEntity client)
}
}

private sealed class FakeLoggedUser : ILoggedUser
{
private readonly TestClientStore _clientStore;

public FakeLoggedUser(TestClientStore clientStore)
{
_clientStore = clientStore;
}

public Task<ClientEntity> User()
{
var user = _clientStore.Clients.First();
return Task.FromResult(user);
}
}

private sealed class FakeUnitOfWork : IUnitOfWork
{
public Task Commit() => Task.CompletedTask;
Expand Down
1 change: 1 addition & 0 deletions WebApi.Test/WebApi.Test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\CommonTestUtilities\CommonTestUtilities.csproj" />
<ProjectReference Include="..\ProductClientHub.API\ProductClientHub.API.csproj" />
</ItemGroup>

Expand Down
Loading