diff --git a/CommonTestUtilities/CommonTestUtilities.csproj b/CommonTestUtilities/CommonTestUtilities.csproj
index e32584d..32cf5e6 100644
--- a/CommonTestUtilities/CommonTestUtilities.csproj
+++ b/CommonTestUtilities/CommonTestUtilities.csproj
@@ -18,4 +18,8 @@
+
+
+
+
\ No newline at end of file
diff --git a/CommonTestUtilities/Repositories/ProductsWriteOnlyRepositoryBuild.cs b/CommonTestUtilities/Repositories/ProductsWriteOnlyRepositoryBuild.cs
new file mode 100644
index 0000000..1d46752
--- /dev/null
+++ b/CommonTestUtilities/Repositories/ProductsWriteOnlyRepositoryBuild.cs
@@ -0,0 +1,13 @@
+using Moq;
+using ProductClientHub.Domain.Repositories.Product;
+
+namespace CommonTestUtilities.Repositories;
+
+public class ProductsWriteOnlyRepositoryBuild
+{
+ public static IProductsWriteOnlyRepository Build()
+ {
+ var mock = new Mock();
+ return mock.Object;
+ }
+}
diff --git a/CommonTestUtilities/Repositories/UpdateProductWriteOnlyRepository.cs b/CommonTestUtilities/Repositories/UpdateProductWriteOnlyRepository.cs
new file mode 100644
index 0000000..fcf8bf4
--- /dev/null
+++ b/CommonTestUtilities/Repositories/UpdateProductWriteOnlyRepository.cs
@@ -0,0 +1,14 @@
+using Moq;
+using ProductClientHub.Domain.Repositories.Product;
+
+namespace CommonTestUtilities.Repositories;
+
+public class UpdateProductWriteOnlyRepository
+{
+ public static IUpdateProductOnlyRepository Build()
+ {
+ var mock = new Mock();
+
+ return mock.Object;
+ }
+}
diff --git a/CommonTestUtilities/Repositories/UpdateProductsWriteOnlyRepositoryBuilder.cs b/CommonTestUtilities/Repositories/UpdateProductsWriteOnlyRepositoryBuilder.cs
new file mode 100644
index 0000000..4c28e15
--- /dev/null
+++ b/CommonTestUtilities/Repositories/UpdateProductsWriteOnlyRepositoryBuilder.cs
@@ -0,0 +1,13 @@
+using Moq;
+using ProductClientHub.Domain.Repositories.Product;
+
+namespace CommonTestUtilities.Repositories;
+
+public class UpdateProductsWriteOnlyRepositoryBuilder
+{
+ public static IUpdateProductOnlyRepository Build()
+ {
+ var mock = new Mock();
+ return mock.Object;
+ }
+}
diff --git a/CommonTestUtilities/Requests/RequestProductJsonBuilder.cs b/CommonTestUtilities/Requests/RequestProductJsonBuilder.cs
new file mode 100644
index 0000000..78215ac
--- /dev/null
+++ b/CommonTestUtilities/Requests/RequestProductJsonBuilder.cs
@@ -0,0 +1,16 @@
+using ProductClientHub.Communication.Requests;
+
+namespace CommonTestUtilities.Requests;
+
+public class RequestProductJsonBuilder
+{
+ public static RequestProductJson Build(string Name, string Brand, decimal Price)
+ {
+ return new RequestProductJson
+ {
+ Name = Name,
+ Brand = Brand,
+ Price = Price
+ };
+ }
+}
diff --git a/ProductClientHub.API/Controllers/ProductsController.cs b/ProductClientHub.API/Controllers/ProductsController.cs
index e4b82f7..face490 100644
--- a/ProductClientHub.API/Controllers/ProductsController.cs
+++ b/ProductClientHub.API/Controllers/ProductsController.cs
@@ -34,7 +34,7 @@ public async Task Register([FromBody] RequestProductJson request,
[ProducesResponseType(typeof(ResponseErrorMessagesJson), StatusCodes.Status404NotFound)]
public async Task Update([FromBody] RequestProductJson request,
[FromRoute] Guid productId,
- [FromServices] IUploadProductUseCase useCase)
+ [FromServices] IUpdateProductUseCase useCase)
{
var response = await useCase.Execute(productId, request);
diff --git a/ProductClientHub.Application/DependencyInjectionExtension.cs b/ProductClientHub.Application/DependencyInjectionExtension.cs
index c40d808..ae28e30 100644
--- a/ProductClientHub.Application/DependencyInjectionExtension.cs
+++ b/ProductClientHub.Application/DependencyInjectionExtension.cs
@@ -43,7 +43,7 @@ private static void AddUseCases(IServiceCollection services)
services.AddScoped();
services.AddScoped();
services.AddScoped();
- services.AddScoped();
+ services.AddScoped();
//login
services.AddScoped();
diff --git a/ProductClientHub.Application/UseCases/Products/Update/IUploadProductUseCase.cs b/ProductClientHub.Application/UseCases/Products/Update/IUpdateProductUseCase.cs
similarity index 86%
rename from ProductClientHub.Application/UseCases/Products/Update/IUploadProductUseCase.cs
rename to ProductClientHub.Application/UseCases/Products/Update/IUpdateProductUseCase.cs
index 64957df..dee4296 100644
--- a/ProductClientHub.Application/UseCases/Products/Update/IUploadProductUseCase.cs
+++ b/ProductClientHub.Application/UseCases/Products/Update/IUpdateProductUseCase.cs
@@ -3,7 +3,7 @@
namespace ProductClientHub.Application.UseCases.Products.Update;
-public interface IUploadProductUseCase
+public interface IUpdateProductUseCase
{
Task Execute(Guid productId, RequestProductJson request);
}
diff --git a/ProductClientHub.Application/UseCases/Products/Update/UploadProductUseCase.cs b/ProductClientHub.Application/UseCases/Products/Update/UpdateProductUseCase.cs
similarity index 91%
rename from ProductClientHub.Application/UseCases/Products/Update/UploadProductUseCase.cs
rename to ProductClientHub.Application/UseCases/Products/Update/UpdateProductUseCase.cs
index e83d55d..e34f893 100644
--- a/ProductClientHub.Application/UseCases/Products/Update/UploadProductUseCase.cs
+++ b/ProductClientHub.Application/UseCases/Products/Update/UpdateProductUseCase.cs
@@ -11,16 +11,16 @@
namespace ProductClientHub.Application.UseCases.Products.Update;
-public class UploadProductUseCase : IUploadProductUseCase
+public class UpdateProductUseCase : IUpdateProductUseCase
{
private readonly IUnitOfWork _unitOfWork;
private readonly IClientReadOnlyRepository _clientReadOnlyRepository;
private readonly IProductsReadOnlyRepository _productsReadOnlyRepository;
- private readonly IUploadProductOnlyRepository _productWriteOnlyRepository;
+ private readonly IUpdateProductOnlyRepository _productWriteOnlyRepository;
private readonly ILoggedClient _loggedClient;
- public UploadProductUseCase(IUnitOfWork unitOfWork,
- IUploadProductOnlyRepository productWriteOnlyRepository,
+ public UpdateProductUseCase(IUnitOfWork unitOfWork,
+ IUpdateProductOnlyRepository productWriteOnlyRepository,
IClientReadOnlyRepository clientReadOnlyRepository,
IProductsReadOnlyRepository productsReadOnlyRepository,
ILoggedClient loggedClient)
diff --git a/ProductClientHub.Domain/Repositories/Product/IUploadProductOnlyRepository.cs b/ProductClientHub.Domain/Repositories/Product/IUploadProductOnlyRepository.cs
index ef123f3..c1e085b 100644
--- a/ProductClientHub.Domain/Repositories/Product/IUploadProductOnlyRepository.cs
+++ b/ProductClientHub.Domain/Repositories/Product/IUploadProductOnlyRepository.cs
@@ -1,6 +1,6 @@
namespace ProductClientHub.Domain.Repositories.Product;
-public interface IUploadProductOnlyRepository
+public interface IUpdateProductOnlyRepository
{
Task Update(Guid clientId, Guid productId, Domain.Entities.Product product);
}
diff --git a/ProductClientHub.Infrastructure/DataAcess/Repositories/Products/ProductsWriteOnlyRepository.cs b/ProductClientHub.Infrastructure/DataAcess/Repositories/Products/ProductsWriteOnlyRepository.cs
index ca84360..e74aa3a 100644
--- a/ProductClientHub.Infrastructure/DataAcess/Repositories/Products/ProductsWriteOnlyRepository.cs
+++ b/ProductClientHub.Infrastructure/DataAcess/Repositories/Products/ProductsWriteOnlyRepository.cs
@@ -5,7 +5,7 @@
namespace ProductClientHub.Infrastructure.DataAcess.Repositories.Products;
-public class ProductsWriteOnlyRepository : IProductsWriteOnlyRepository, IDeleteProductWriteOnlyRepository, IUploadProductOnlyRepository
+public class ProductsWriteOnlyRepository : IProductsWriteOnlyRepository, IDeleteProductWriteOnlyRepository, IUpdateProductOnlyRepository
{
private readonly ProductClientHubDbContext _dbContext;
@@ -32,9 +32,9 @@ public async Task Delete(Guid clientId, Guid productId)
public async Task Update(Guid clientId, Guid productId, Product product)
{
- var productUpload = await _dbContext.Products.Where(p => p.ClientId == clientId && p.Id == productId).FirstOrDefaultAsync();
+ var productUpdate = await _dbContext.Products.Where(p => p.ClientId == clientId && p.Id == productId).FirstOrDefaultAsync();
- if (productUpload is null) return;
+ if (productUpdate is null) return;
var createdOn = await _dbContext.Products
.Where(x => x.Id == product.Id)
@@ -43,7 +43,7 @@ public async Task Update(Guid clientId, Guid productId, Product product)
product.CreatedOn = DateTime.SpecifyKind(createdOn, DateTimeKind.Utc);
- _dbContext.Products.Update(productUpload);
+ _dbContext.Products.Update(productUpdate);
_dbContext.Entry(product).Property(x => x.CreatedOn).IsModified = false;
}
diff --git a/ProductClientHub.Infrastructure/DependencyInjectionExtension.cs b/ProductClientHub.Infrastructure/DependencyInjectionExtension.cs
index a38c024..d660b9b 100644
--- a/ProductClientHub.Infrastructure/DependencyInjectionExtension.cs
+++ b/ProductClientHub.Infrastructure/DependencyInjectionExtension.cs
@@ -64,7 +64,7 @@ private static void AddRepositories(IServiceCollection services)
services.AddScoped();
services.AddScoped();
services.AddScoped();
- services.AddScoped();
+ services.AddScoped();
}
private static void AddTokens(IServiceCollection services, IConfiguration configuration)
diff --git a/UseCase.Test/Product/Register/RegisterProductUseCaseTest.cs b/UseCase.Test/Product/Register/RegisterProductUseCaseTest.cs
new file mode 100644
index 0000000..b3be1a8
--- /dev/null
+++ b/UseCase.Test/Product/Register/RegisterProductUseCaseTest.cs
@@ -0,0 +1,60 @@
+using CommonTestUtilities.Entities;
+using CommonTestUtilities.loggedClient;
+using CommonTestUtilities.Repositories;
+using CommonTestUtilities.Requests;
+using ProductClientHub.Application.UseCases.Products.Register;
+using ProductClientHub.Domain.Extensions;
+using ProductClientHub.Exceptions.ExceptionsBase;
+using Shouldly;
+
+namespace UseCase.Test.Product.Register;
+
+public class RegisterProductUseCaseTest
+{
+ [Fact]
+ public async Task RegisterProductUseCaseTest_Success()
+ {
+ (var client, _) = ClientBuilder.Build();
+ var product = ProductBuilder.Build(client);
+
+ var productJson =RequestProductJsonBuilder.Build(product.Name, product.Brand, product.Price);
+
+ var useCase = CreateUseCase(product, client, ClientNull: false);
+
+ var result = await useCase.Execute(productJson);
+
+ result.ShouldNotBeNull();
+ result.Name.ShouldBe(product.Name);
+ result.Brand.ShouldBe(product.Brand);
+ result.Price.ShouldBe(product.Price);
+ }
+
+ [Fact]
+ public async Task RegisterProductUseCaseTest_Client_NotFound_Error()
+ {
+ (var client, _) = ClientBuilder.Build();
+ var product = ProductBuilder.Build(client);
+
+ var productJson = RequestProductJsonBuilder.Build(product.Name, product.Brand, product.Price);
+
+ var useCase = CreateUseCase(product, client, ClientNull: true);
+
+ var result = await Should.ThrowAsync(async () => await useCase.Execute(productJson));
+
+ result.Message.ShouldBe(ResourceMessagesExceptions.CLIENT_NOCONTENT);
+ result.GetHttpStatusCode().ShouldBe(System.Net.HttpStatusCode.NotFound);
+ }
+
+ private static RegisterProductUseCase CreateUseCase(ProductClientHub.Domain.Entities.Product product, ProductClientHub.Domain.Entities.Client client, bool ClientNull)
+ {
+ var productsWriteOnly = ProductsWriteOnlyRepositoryBuild.Build();
+ var clientReadOnly = new ClientReadOnlyRepositoryBuilder();
+ var unitOfWork = UnitOfWorkBuilder.Build();
+ var loggedClient = LoggedClientBuilder.Build(client);
+
+ if(ClientNull.IsFalse())
+ clientReadOnly.GetById(client);
+
+ return new RegisterProductUseCase(productsWriteOnly, unitOfWork, clientReadOnly.Build(), loggedClient);
+ }
+}
diff --git a/UseCase.Test/Product/Update/UpdateProductsUseCaseTest.cs b/UseCase.Test/Product/Update/UpdateProductsUseCaseTest.cs
new file mode 100644
index 0000000..567ae62
--- /dev/null
+++ b/UseCase.Test/Product/Update/UpdateProductsUseCaseTest.cs
@@ -0,0 +1,97 @@
+using CommonTestUtilities.Entities;
+using CommonTestUtilities.loggedClient;
+using CommonTestUtilities.Repositories;
+using CommonTestUtilities.Requests;
+using ProductClientHub.Application.UseCases.Products.Update;
+using ProductClientHub.Domain.Extensions;
+using ProductClientHub.Exceptions.ExceptionsBase;
+using Shouldly;
+
+namespace UseCase.Test.Product.Update;
+
+public class UpdateProductsUseCaseTest
+{
+ [Fact]
+ public async Task UpdateProductUseCaseTest_Success()
+ {
+ (var client, _) = ClientBuilder.Build();
+
+ var product = ProductBuilder.Build(client);
+
+ var productUpdate = RequestProductJsonBuilder.Build(
+ Name: "New Name",
+ Brand: "New Brand",
+ Price: 100);
+
+ var useCase = CreateUseCase(product, client, clientNull: false, productNull: false);
+
+ var result = await useCase.Execute(product.Id, productUpdate);
+
+ result.ShouldNotBeNull();
+ result.Brand.ShouldBe(product.Brand);
+ result.Name.ShouldBe(product.Name);
+ result.Price.ShouldBe(product.Price);
+ }
+
+ [Fact]
+ public async Task UpdateProductUseCaseTest_Client_NotFound()
+ {
+ (var client, _) = ClientBuilder.Build();
+
+ var product = ProductBuilder.Build(client);
+
+ var productUpdate = RequestProductJsonBuilder.Build(
+ Name: "New Name",
+ Brand: "New Brand",
+ Price: 100);
+
+ var useCase = CreateUseCase(product, client, clientNull: true, productNull: false);
+
+ var exception = await Should.ThrowAsync(() => useCase.Execute(product.Id, productUpdate));
+
+ exception.Message.ShouldBe(ResourceMessagesExceptions.CLIENT_NOCONTENT);
+ exception.GetHttpStatusCode().ShouldBe(System.Net.HttpStatusCode.NotFound);
+ }
+
+ [Fact]
+ public async Task UpdateProductUseCaseTest_Product_NotFound()
+ {
+ (var client, _) = ClientBuilder.Build();
+
+ var product = ProductBuilder.Build(client);
+
+ var productUpdate = RequestProductJsonBuilder.Build(
+ Name: "New Name",
+ Brand: "New Brand",
+ Price: 100);
+
+ var useCase = CreateUseCase(product, client, clientNull: false, productNull: true);
+
+ var exception = await Should.ThrowAsync(() => useCase.Execute(product.Id, productUpdate));
+
+ exception.Message.ShouldBe(ResourceMessagesExceptions.PRODUCT_NOTFOUND);
+ exception.GetHttpStatusCode().ShouldBe(System.Net.HttpStatusCode.NotFound);
+ }
+
+ private static UpdateProductUseCase CreateUseCase(ProductClientHub.Domain.Entities.Product product, ProductClientHub.Domain.Entities.Client client, bool clientNull, bool productNull)
+ {
+ var unitOfWork = UnitOfWorkBuilder.Build();
+ var clientReadOnlyRepository = new ClientReadOnlyRepositoryBuilder();
+ var productUpdateWriteOnlyRepository = UpdateProductsWriteOnlyRepositoryBuilder.Build();
+ var productReadOnlyRepository = new ProductsReadOnlyRepositoryBuild();
+ var loggedClient = LoggedClientBuilder.Build(client);
+
+ if (clientNull.IsFalse())
+ {
+ clientReadOnlyRepository.GetById(client);
+ }
+
+ if (productNull.IsFalse())
+ {
+ productReadOnlyRepository.GetById(product);
+ }
+
+
+ return new UpdateProductUseCase(unitOfWork, productUpdateWriteOnlyRepository, clientReadOnlyRepository.Build(), productReadOnlyRepository.Build(), loggedClient);
+ }
+}
diff --git a/Validators.Test/Client/RegisterClientValidatorTest.cs b/Validators.Test/Client/RegisterClientValidatorTest.cs
new file mode 100644
index 0000000..3226541
--- /dev/null
+++ b/Validators.Test/Client/RegisterClientValidatorTest.cs
@@ -0,0 +1,68 @@
+using CommonTestUtilities.Requests;
+using ProductClientHub.Application.UseCases.Users.SharedValidator;
+using ProductClientHub.Communication.Requests;
+using Shouldly;
+
+namespace Validators.Test.Client;
+
+public class RegisterClientValidatorTest
+{
+ [Fact]
+ public void Success()
+ {
+ var validate = new RequestClientValidator();
+
+ var request = RequestClientJsonBuilder.Build("John Carlos", "john@gmail.com", "123456a");
+
+ var result = validate.Validate(request);
+
+ result.IsValid.ShouldBeTrue();
+ }
+
+ [Fact]
+ public void Error_Email_Empty()
+ {
+ var validate = new RequestClientValidator();
+
+ var request = RequestClientJsonBuilder.Build("John Carlos", "", "123456a");
+
+ var result = validate.Validate(request);
+
+ result.IsValid.ShouldBeFalse();
+ }
+
+ [Fact]
+ public void Error_Email_Invalid()
+ {
+ var validate = new RequestClientValidator();
+
+ var request = RequestClientJsonBuilder.Build("John Carlos", "john", "123456a");
+
+ var result = validate.Validate(request);
+
+ result.IsValid.ShouldBeFalse();
+ }
+
+ [Fact]
+ public void Error_Password_Empty()
+ {
+ var validate = new RequestClientValidator();
+
+ var request = RequestClientJsonBuilder.Build("John Carlos", "john@gmail.com", "");
+
+ var result = validate.Validate(request);
+
+ result.IsValid.ShouldBeFalse();
+ }
+
+ [Fact]
+ public void Error_Password_Short()
+ {
+ var validate = new RequestClientValidator();
+ var request = RequestClientJsonBuilder.Build("John Carlos", "john@gmail.com", "123");
+
+ var result = validate.Validate(request);
+
+ result.IsValid.ShouldBeFalse();
+ }
+}
\ No newline at end of file
diff --git a/Validators.Test/Validators.Test.csproj b/Validators.Test/Validators.Test.csproj
index 9c57052..ee91285 100644
--- a/Validators.Test/Validators.Test.csproj
+++ b/Validators.Test/Validators.Test.csproj
@@ -15,6 +15,11 @@
+
+
+
+
+
diff --git a/WebApi.Test/CustomWebApplicationFactory.cs b/WebApi.Test/CustomWebApplicationFactory.cs
index 413ae11..5ed0686 100644
--- a/WebApi.Test/CustomWebApplicationFactory.cs
+++ b/WebApi.Test/CustomWebApplicationFactory.cs
@@ -5,16 +5,20 @@
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using ProductClientHub.Domain.Repositories.Client;
+using ProductClientHub.Domain.Repositories.Product;
using ProductClientHub.Domain.Repositories.UnitOfWork;
using ProductClientHub.Domain.Security.Tokens;
using ProductClientHub.Domain.Services.loggedClient;
+using ProductClientHub.Exceptions.ExceptionsBase;
using ClientEntity = ProductClientHub.Domain.Entities.Client;
+using ProductEntity = ProductClientHub.Domain.Entities.Product;
namespace WebApi.Test;
public class CustomWebApplicationFactory : WebApplicationFactory
{
private readonly TestClientStore _clientStore = new();
+ private readonly TestProductStore _productStore = new();
public IList ClientsToReturn
{
@@ -22,6 +26,12 @@ public IList ClientsToReturn
set => _clientStore.Clients = value;
}
+ public IList ProductsToReturn
+ {
+ get => _productStore.Products;
+ set => _productStore.Products = value;
+ }
+
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.UseEnvironment("Testing");
@@ -40,6 +50,7 @@ protected override void ConfigureWebHost(IWebHostBuilder builder)
builder.ConfigureServices(services =>
{
services.AddSingleton(_clientStore);
+ services.AddSingleton(_productStore);
services.RemoveAll();
services.RemoveAll();
@@ -47,6 +58,10 @@ protected override void ConfigureWebHost(IWebHostBuilder builder)
services.RemoveAll();
services.RemoveAll();
services.RemoveAll();
+ services.RemoveAll();
+ services.RemoveAll();
+ services.RemoveAll();
+ services.RemoveAll();
// Remove all hosted services to prevent background services from running during tests
services.RemoveAll(typeof(IHostedService));
@@ -57,6 +72,10 @@ protected override void ConfigureWebHost(IWebHostBuilder builder)
services.AddScoped();
services.AddScoped();
services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
});
}
@@ -140,6 +159,81 @@ public Task Add(ClientEntity client)
}
}
+ private sealed class FakeProductsReadOnlyRepository : IProductsReadOnlyRepository
+ {
+ private readonly TestProductStore _productStore;
+
+ public FakeProductsReadOnlyRepository(TestProductStore productStore)
+ {
+ _productStore = productStore;
+ }
+
+ public Task> GetAll()
+ => Task.FromResult>(_productStore.Products);
+
+ public Task GetById(Guid productId)
+ {
+ var product = _productStore.Products.FirstOrDefault(p => p.Id == productId);
+ return Task.FromResult(product);
+ }
+ }
+
+ private sealed class FakeProductsWriteOnlyRepository : IProductsWriteOnlyRepository, IDeleteProductWriteOnlyRepository, IUpdateProductOnlyRepository
+ {
+ private readonly TestProductStore _productStore;
+ private readonly TestClientStore _clientStore;
+
+ public FakeProductsWriteOnlyRepository(TestProductStore productStore, TestClientStore clientStore)
+ {
+ _productStore = productStore;
+ _clientStore = clientStore;
+ }
+
+ public Task Add(ProductEntity product)
+ {
+ if (product.Client is null)
+ {
+ var client = _clientStore.Clients.FirstOrDefault(c => c.Id == product.ClientId);
+ if (client is not null)
+ {
+ product.Client = client;
+ client.Products.Add(product);
+ }
+ }
+
+ _productStore.Products.Add(product);
+ return Task.CompletedTask;
+ }
+
+ public Task Delete(Guid clientId, Guid productId)
+ {
+ var product = _productStore.Products.FirstOrDefault(p => p.Id == productId && p.ClientId == clientId);
+ if (product is null)
+ {
+ return Task.FromResult(false);
+ }
+
+ _productStore.Products.Remove(product);
+ var client = _clientStore.Clients.FirstOrDefault(c => c.Id == clientId);
+ client?.Products.Remove(product);
+ return Task.FromResult(true);
+ }
+
+ public Task Update(Guid clientId, Guid productId, ProductEntity product)
+ {
+ var existingProduct = _productStore.Products.FirstOrDefault(p => p.Id == productId && p.ClientId == clientId);
+ if (existingProduct is null)
+ {
+ return Task.CompletedTask;
+ }
+
+ existingProduct.Name = product.Name;
+ existingProduct.Brand = product.Brand;
+ existingProduct.Price = product.Price;
+ return Task.CompletedTask;
+ }
+ }
+
private sealed class FakeloggedClient : ILoggedClient
{
private readonly TestClientStore _clientStore;
@@ -151,7 +245,13 @@ public FakeloggedClient(TestClientStore clientStore)
public Task User()
{
- var user = _clientStore.Clients.First();
+ var user = _clientStore.Clients.FirstOrDefault();
+
+ if (user is null)
+ {
+ throw new NotFoundException(ResourceMessagesExceptions.CLIENT_NOCONTENT);
+ }
+
return Task.FromResult(user);
}
}
@@ -165,4 +265,9 @@ private sealed class TestClientStore
{
public IList Clients { get; set; } = [];
}
+
+ private sealed class TestProductStore
+ {
+ public IList Products { get; set; } = [];
+ }
}
diff --git a/WebApi.Test/Product/Delete/DeleteProductIntegrationTest.cs b/WebApi.Test/Product/Delete/DeleteProductIntegrationTest.cs
new file mode 100644
index 0000000..7204acb
--- /dev/null
+++ b/WebApi.Test/Product/Delete/DeleteProductIntegrationTest.cs
@@ -0,0 +1,90 @@
+using Microsoft.AspNetCore.Mvc.Testing;
+using Shouldly;
+using System.Net;
+using System.Net.Http.Headers;
+using ClientEntity = ProductClientHub.Domain.Entities.Client;
+using ProductEntity = ProductClientHub.Domain.Entities.Product;
+
+namespace WebApi.Test.Product.Delete;
+
+public class DeleteProductIntegrationTest : IClassFixture
+{
+ private readonly CustomWebApplicationFactory _factory;
+ private readonly HttpClient _httpClient;
+
+ public DeleteProductIntegrationTest(CustomWebApplicationFactory factory)
+ {
+ _factory = factory;
+ _httpClient = factory.CreateClient(new WebApplicationFactoryClientOptions
+ {
+ BaseAddress = new Uri("https://localhost")
+ });
+ }
+
+ [Fact]
+ public async Task DeleteProduct_ShouldReturnNoContent_WhenProductExists()
+ {
+ var clientId = Guid.Parse("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa");
+ var productId = Guid.Parse("bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb");
+ var token = CommonTestUtilities.Token.JwtTokenGeneratorBuilder.Build().Generate(clientId);
+
+ _factory.ClientsToReturn =
+ [
+ new ClientEntity
+ {
+ Id = clientId,
+ Name = "Client 1",
+ Email = "client1@email.com",
+ Password = "password123",
+ Products = []
+ }
+ ];
+
+ _factory.ProductsToReturn =
+ [
+ new ProductEntity
+ {
+ Id = productId,
+ Name = "Product 1",
+ Brand = "Brand 1",
+ Price = 10.5m,
+ ClientId = clientId,
+ Client = _factory.ClientsToReturn[0]
+ }
+ ];
+
+ _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
+
+ var response = await _httpClient.DeleteAsync($"/api/products/{productId}");
+
+ response.StatusCode.ShouldBe(HttpStatusCode.NoContent);
+ }
+
+ [Fact]
+ public async Task DeleteProduct_ShouldReturnNoContent_WhenProductDoesNotExist()
+ {
+ var clientId = Guid.Parse("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa");
+ var productId = Guid.Parse("bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb");
+ var token = CommonTestUtilities.Token.JwtTokenGeneratorBuilder.Build().Generate(clientId);
+
+ _factory.ClientsToReturn =
+ [
+ new ClientEntity
+ {
+ Id = clientId,
+ Name = "Client 1",
+ Email = "client1@email.com",
+ Password = "password123",
+ Products = []
+ }
+ ];
+
+ _factory.ProductsToReturn = [];
+
+ _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
+
+ var response = await _httpClient.DeleteAsync($"/api/products/{productId}");
+
+ response.StatusCode.ShouldBe(HttpStatusCode.NoContent);
+ }
+}
diff --git a/WebApi.Test/Product/GetAll/GetAllProductsIntegrationTest.cs b/WebApi.Test/Product/GetAll/GetAllProductsIntegrationTest.cs
new file mode 100644
index 0000000..2b43a77
--- /dev/null
+++ b/WebApi.Test/Product/GetAll/GetAllProductsIntegrationTest.cs
@@ -0,0 +1,92 @@
+using Microsoft.AspNetCore.Mvc.Testing;
+using ProductClientHub.Communication.Responses;
+using Shouldly;
+using System.Net;
+using System.Net.Http.Headers;
+using System.Net.Http.Json;
+using ClientEntity = ProductClientHub.Domain.Entities.Client;
+using ProductEntity = ProductClientHub.Domain.Entities.Product;
+
+namespace WebApi.Test.Product.GetAll;
+
+public class GetAllProductsIntegrationTest : IClassFixture
+{
+ private readonly CustomWebApplicationFactory _factory;
+ private readonly HttpClient _httpClient;
+
+ public GetAllProductsIntegrationTest(CustomWebApplicationFactory factory)
+ {
+ _factory = factory;
+ _httpClient = factory.CreateClient(new WebApplicationFactoryClientOptions
+ {
+ BaseAddress = new Uri("https://localhost")
+ });
+
+ _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "fake-token");
+ }
+
+ [Fact]
+ public async Task GetAll_ShouldReturnSuccess_WhenThereAreProductsInTheSystem()
+ {
+ var clientId = Guid.Parse("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa");
+
+ _factory.ClientsToReturn =
+ [
+ new ClientEntity
+ {
+ Id = clientId,
+ Name = "Client 1",
+ Email = "client1@email.com",
+ Password = "password123",
+ Products = []
+ }
+ ];
+
+ _factory.ProductsToReturn =
+ [
+ new ProductEntity
+ {
+ Id = Guid.Parse("bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"),
+ Name = "Product 1",
+ Brand = "Brand 1",
+ Price = 10.5m,
+ ClientId = clientId,
+ Client = _factory.ClientsToReturn[0]
+ },
+ new ProductEntity
+ {
+ Id = Guid.Parse("cccccccc-cccc-cccc-cccc-cccccccccccc"),
+ Name = "Product 2",
+ Brand = "Brand 2",
+ Price = 25m,
+ ClientId = clientId,
+ Client = _factory.ClientsToReturn[0]
+ }
+ ];
+
+ var response = await _httpClient.GetAsync("/api/products");
+
+ response.StatusCode.ShouldBe(HttpStatusCode.OK);
+
+ var body = await response.Content.ReadFromJsonAsync();
+
+ body.ShouldNotBeNull();
+ body!.Products.Count.ShouldBe(2);
+ body.Products[0].Id.ShouldBe(Guid.Parse("bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"));
+ body.Products[0].Name.ShouldBe("Product 1");
+ body.Products[0].Client.Id.ShouldBe(clientId);
+ body.Products[1].Id.ShouldBe(Guid.Parse("cccccccc-cccc-cccc-cccc-cccccccccccc"));
+ body.Products[1].Name.ShouldBe("Product 2");
+ body.Products[1].Client.Id.ShouldBe(clientId);
+ }
+
+ [Fact]
+ public async Task GetAll_ShouldReturnNotFound_WhenThereAreNoProducts()
+ {
+ _factory.ProductsToReturn = [];
+
+ var response = await _httpClient.GetAsync("/api/products");
+
+ response.StatusCode.ShouldBe(HttpStatusCode.NotFound);
+ }
+}
diff --git a/WebApi.Test/Product/GetById/GetProductByIdIntegrationTest.cs b/WebApi.Test/Product/GetById/GetProductByIdIntegrationTest.cs
new file mode 100644
index 0000000..421b01b
--- /dev/null
+++ b/WebApi.Test/Product/GetById/GetProductByIdIntegrationTest.cs
@@ -0,0 +1,72 @@
+using Microsoft.AspNetCore.Mvc.Testing;
+using Shouldly;
+using System.Net;
+using System.Net.Http.Headers;
+using ClientEntity = ProductClientHub.Domain.Entities.Client;
+using ProductEntity = ProductClientHub.Domain.Entities.Product;
+
+namespace WebApi.Test.Product.GetById;
+
+public class GetProductByIdIntegrationTest : IClassFixture
+{
+ private readonly CustomWebApplicationFactory _factory;
+ private readonly HttpClient _httpClient;
+
+ public GetProductByIdIntegrationTest(CustomWebApplicationFactory factory)
+ {
+ _factory = factory;
+ _httpClient = factory.CreateClient(new WebApplicationFactoryClientOptions
+ {
+ BaseAddress = new Uri("https://localhost")
+ });
+
+ _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "fake-token");
+ }
+
+ [Fact]
+ public async Task GetById_ShouldReturnSuccess_WhenProductExists()
+ {
+ var clientId = Guid.Parse("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa");
+ var productId = Guid.Parse("bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb");
+
+ _factory.ClientsToReturn =
+ [
+ new ClientEntity
+ {
+ Id = clientId,
+ Name = "Client 1",
+ Email = "client1@email.com",
+ Password = "password123",
+ Products = []
+ }
+ ];
+
+ _factory.ProductsToReturn =
+ [
+ new ProductEntity
+ {
+ Id = productId,
+ Name = "Product 1",
+ Brand = "Brand 1",
+ Price = 10.5m,
+ ClientId = clientId,
+ Client = _factory.ClientsToReturn[0]
+ }
+ ];
+
+ var response = await _httpClient.GetAsync($"/api/products/{productId}");
+
+ response.StatusCode.ShouldBe(HttpStatusCode.OK);
+ }
+
+ [Fact]
+ public async Task GetById_ShouldReturnNotFound_WhenProductDoesNotExist()
+ {
+ var productId = Guid.Parse("bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb");
+ _factory.ProductsToReturn = [];
+
+ var response = await _httpClient.GetAsync($"/api/products/{productId}");
+
+ response.StatusCode.ShouldBe(HttpStatusCode.NotFound);
+ }
+}
diff --git a/WebApi.Test/Product/Register/RegisterProductIntegrationTest.cs b/WebApi.Test/Product/Register/RegisterProductIntegrationTest.cs
new file mode 100644
index 0000000..443d6a0
--- /dev/null
+++ b/WebApi.Test/Product/Register/RegisterProductIntegrationTest.cs
@@ -0,0 +1,93 @@
+using Microsoft.AspNetCore.Mvc.Testing;
+using Newtonsoft.Json;
+using ProductClientHub.Communication.Requests;
+using ProductClientHub.Communication.Responses;
+using Shouldly;
+using System.Net;
+using System.Net.Http.Headers;
+using System.Text;
+using ClientEntity = ProductClientHub.Domain.Entities.Client;
+
+namespace WebApi.Test.Product.Register;
+
+public class RegisterProductIntegrationTest : IClassFixture
+{
+ private readonly CustomWebApplicationFactory _factory;
+ private readonly HttpClient _httpClient;
+
+ public RegisterProductIntegrationTest(CustomWebApplicationFactory factory)
+ {
+ _factory = factory;
+ _httpClient = factory.CreateClient(new WebApplicationFactoryClientOptions
+ {
+ BaseAddress = new Uri("https://localhost")
+ });
+ }
+
+ [Fact]
+ public async Task RegisterProduct_ShouldReturnCreated_WhenRequestIsValid()
+ {
+ var clientId = Guid.Parse("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa");
+ var token = CommonTestUtilities.Token.JwtTokenGeneratorBuilder.Build().Generate(clientId);
+
+ _factory.ClientsToReturn =
+ [
+ new ClientEntity
+ {
+ Id = clientId,
+ Name = "Client 1",
+ Email = "client1@email.com",
+ Password = "password123",
+ Products = []
+ }
+ ];
+
+ _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
+
+ var request = new RequestProductJson
+ {
+ Name = "Product 1",
+ Brand = "Brand 1",
+ Price = 10.5m
+ };
+
+ var response = await _httpClient.PostAsync(
+ "/api/products",
+ new StringContent(JsonConvert.SerializeObject(request), Encoding.UTF8, "application/json"));
+
+ response.StatusCode.ShouldBe(HttpStatusCode.Created);
+
+ var body = JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync());
+ body.ShouldNotBeNull();
+ body!.Name.ShouldBe(request.Name);
+ body.Brand.ShouldBe(request.Brand);
+ body.Price.ShouldBe(request.Price);
+ }
+
+ [Fact]
+ public async Task RegisterProduct_ShouldReturnNotFound_WhenClientDoesNotExist()
+ {
+ var clientId = Guid.Parse("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa");
+ var token = CommonTestUtilities.Token.JwtTokenGeneratorBuilder.Build().Generate(clientId);
+
+ _factory.ClientsToReturn = [];
+
+ _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
+
+ var request = new RequestProductJson
+ {
+ Name = "Product 1",
+ Brand = "Brand 1",
+ Price = 10.5m
+ };
+
+ var response = await _httpClient.PostAsync(
+ "/api/products",
+ new StringContent(JsonConvert.SerializeObject(request), Encoding.UTF8, "application/json"));
+
+ response.StatusCode.ShouldBe(HttpStatusCode.NotFound);
+
+ var body = await response.Content.ReadAsStringAsync();
+ body.ShouldNotBeNullOrWhiteSpace();
+ }
+}
diff --git a/WebApi.Test/Product/Update/UpdateProductIntegrationTest.cs b/WebApi.Test/Product/Update/UpdateProductIntegrationTest.cs
new file mode 100644
index 0000000..1efea41
--- /dev/null
+++ b/WebApi.Test/Product/Update/UpdateProductIntegrationTest.cs
@@ -0,0 +1,111 @@
+using Microsoft.AspNetCore.Mvc.Testing;
+using Newtonsoft.Json;
+using ProductClientHub.Communication.Requests;
+using Shouldly;
+using System.Net;
+using System.Net.Http.Headers;
+using System.Text;
+using ClientEntity = ProductClientHub.Domain.Entities.Client;
+using ProductEntity = ProductClientHub.Domain.Entities.Product;
+
+namespace WebApi.Test.Product.Update;
+
+public class UpdateProductIntegrationTest : IClassFixture
+{
+ private readonly CustomWebApplicationFactory _factory;
+ private readonly HttpClient _httpClient;
+
+ public UpdateProductIntegrationTest(CustomWebApplicationFactory factory)
+ {
+ _factory = factory;
+ _httpClient = factory.CreateClient(new WebApplicationFactoryClientOptions
+ {
+ BaseAddress = new Uri("https://localhost")
+ });
+ }
+
+ [Fact]
+ public async Task UpdateProduct_ShouldReturnOk_WhenRequestIsValid()
+ {
+ var clientId = Guid.Parse("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa");
+ var productId = Guid.Parse("bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb");
+ var token = CommonTestUtilities.Token.JwtTokenGeneratorBuilder.Build().Generate(clientId);
+
+ _factory.ClientsToReturn =
+ [
+ new ClientEntity
+ {
+ Id = clientId,
+ Name = "Client 1",
+ Email = "client1@email.com",
+ Password = "password123",
+ Products = []
+ }
+ ];
+
+ _factory.ProductsToReturn =
+ [
+ new ProductEntity
+ {
+ Id = productId,
+ Name = "Product 1",
+ Brand = "Brand 1",
+ Price = 10.5m,
+ ClientId = clientId,
+ Client = _factory.ClientsToReturn[0]
+ }
+ ];
+
+ _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
+
+ var request = new RequestProductJson
+ {
+ Name = "Product Updated",
+ Brand = "Brand Updated",
+ Price = 20m
+ };
+
+ var response = await _httpClient.PutAsync(
+ $"/api/products/{productId}",
+ new StringContent(JsonConvert.SerializeObject(request), Encoding.UTF8, "application/json"));
+
+ response.StatusCode.ShouldBe(HttpStatusCode.OK);
+ }
+
+ [Fact]
+ public async Task UpdateProduct_ShouldReturnNotFound_WhenProductDoesNotExist()
+ {
+ var clientId = Guid.Parse("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa");
+ var productId = Guid.Parse("bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb");
+ var token = CommonTestUtilities.Token.JwtTokenGeneratorBuilder.Build().Generate(clientId);
+
+ _factory.ClientsToReturn =
+ [
+ new ClientEntity
+ {
+ Id = clientId,
+ Name = "Client 1",
+ Email = "client1@email.com",
+ Password = "password123",
+ Products = []
+ }
+ ];
+
+ _factory.ProductsToReturn = [];
+
+ _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
+
+ var request = new RequestProductJson
+ {
+ Name = "Product Updated",
+ Brand = "Brand Updated",
+ Price = 20m
+ };
+
+ var response = await _httpClient.PutAsync(
+ $"/api/products/{productId}",
+ new StringContent(JsonConvert.SerializeObject(request), Encoding.UTF8, "application/json"));
+
+ response.StatusCode.ShouldBe(HttpStatusCode.NotFound);
+ }
+}