Skip to content

Commit

Permalink
feat/i89 ✨ Adiciona suporte para criação e manipulação da entidade De…
Browse files Browse the repository at this point in the history
…posit

Inclui DepositDTO, DepositEntityService e implementação, uma fábrica de depósitos e testes. A implementação adiciona validações para dados do depósito e usa um repositório para persistência.
  • Loading branch information
diegosneves committed Aug 15, 2024
1 parent 689ff88 commit 1554bd2
Show file tree
Hide file tree
Showing 5 changed files with 333 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package diegosneves.github.conectardoacoes.adapters.rest.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

/**
* A classe {@link DepositDTO} é um Data Transfer Object (DTO) que fornece uma maneira simples de transportar dados entre processos.
* Ele é responsável por representar a entidade "Doação" em operações onde apenas uma transferência de dados simples é necessária,
* sem comportamentos adicionais.
* <p>
* <p>A classe {@link DepositDTO} inclui duas propriedades:</p>
* <ol>
* <li><code>description</code> - Uma string que descreve a doação.</li>
* <li><code>amount</code> - Um inteiro que representa a quantidade da doação.</li>
* </ol>
* <p>
* Ela contém os construtores {@code @AllArgsConstructor} e {@code @NoArgsConstructor}, que permitem a criação de instâncias com todos os atributos ou sem nenhum atributo, respectivamente.
* Além disso, possui os métodos {@code @getter} e {@code @setter} para cada propriedade, permitindo a recuperação e modificação de cada propriedade, respectivamente.
* A anotação {@code @Builder} implementa o padrão <b>Builder</b> para criar objetos da classe de maneira mais legível e segura.
*
* @author diegoneves
* @since 1.3.0
*/
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@Builder
public class DepositDTO {

private String description;
private Integer amount;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package diegosneves.github.conectardoacoes.adapters.rest.factory;

import diegosneves.github.conectardoacoes.adapters.rest.model.DepositEntity;
import diegosneves.github.conectardoacoes.core.utils.UuidUtils;

/**
* A classe {@code DepositFactory} é responsável por criar instâncias da {@link DepositEntity}.
* <p>
* Esta classe é utilitária e não pode ser instanciada, ou seja, todos os seus métodos são estáticos.
* O principal método desta classe é o {@code createDepositEntity}, que gera uma instância de {@link DepositEntity}
* utilizando um UUID gerado, uma descrição e um valor fornecidos.
* </p>
* <p>
* Uso típico:
* <pre>{@code
* DepositEntity deposit = DepositFactory.createDepositEntity("Depósito de exemplo", 100);
* }</pre>
*
* @author diegoneves
* @since 1.3.0
* @see DepositEntity
*/
public class DepositFactory {

private DepositFactory() {
}

/**
* Cria uma nova instância de {@link DepositEntity} com uma descrição fornecida e um valor.
* <p>
* Este método gera um novo UUID usando {@link UuidUtils#generateUuid()} e cria uma nova
* instância de {@link DepositEntity} com o UUID gerado, a descrição fornecida e o valor fornecido.
* </p>
*
* @param description Uma {@link String} representando a descrição do depósito.
* @param amount Um {@link Integer} representando o valor do depósito.
* @return A nova instância de {@link DepositEntity} criada com o UUID gerado, a descrição fornecida e o valor fornecido.
* @throws IllegalArgumentException se a descrição ou o valor forem nulos.
*/
public static DepositEntity createDepositEntity(String description, Integer amount) {
return new DepositEntity(UuidUtils.generateUuid(), description, amount);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package diegosneves.github.conectardoacoes.adapters.rest.service;

import diegosneves.github.conectardoacoes.adapters.rest.dto.DepositDTO;
import diegosneves.github.conectardoacoes.adapters.rest.model.DepositEntity;

/**
* Interface de serviço para operações relacionadas à entidade de depósito.
* <p>
* Esta interface define os métodos necessários para a criação e manipulação
* de objetos do tipo {@link DepositEntity}.
*
* @author diegoneves
* @since 1.3.0
*/
public interface DepositEntityService {

/**
* Cria uma nova instância de {@link DepositEntity} com base nos dados fornecidos
* pelo objeto {@link DepositDTO}.
*
* @param dto Objeto de transferência de dados contendo as informações necessárias
* para a criação de um novo depósito.
* @return A nova instância de {@link DepositEntity} criada com os dados fornecidos.
*/
DepositEntity create(DepositDTO dto);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package diegosneves.github.conectardoacoes.adapters.rest.service.impl;

import diegosneves.github.conectardoacoes.adapters.rest.dto.DepositDTO;
import diegosneves.github.conectardoacoes.adapters.rest.exception.DepositEntityFailuresException;
import diegosneves.github.conectardoacoes.adapters.rest.factory.DepositFactory;
import diegosneves.github.conectardoacoes.adapters.rest.model.DepositEntity;
import diegosneves.github.conectardoacoes.adapters.rest.repository.DepositRepository;
import diegosneves.github.conectardoacoes.adapters.rest.service.DepositEntityService;
import diegosneves.github.conectardoacoes.core.utils.ValidationUtils;
import org.springframework.stereotype.Service;

/**
* Classe de implementação do serviço de entidade de depósito.
*
* <p>
* Esta classe fornece a implementação das operações de criação relacionadas à entidade
* de depósito. Ela utiliza o repositório de depósitos ({@link DepositRepository})
* para salvar a nova entidade de depósito criada.
* </p>
*
* <p>
* A constante {@code DEPOSIT_VALIDATION_ERROR} representa um código específico de erro
* de validação dos depósitos.
* </p>
*
* <p>
* As operações de criação de depósitos utilizam a fábrica de depósitos
* ({@link DepositFactory}) para instanciar novos objetos de depósito a partir dos dados
* de transferência (DTO - Data Transfer Object).
* </p>
*
* <p>
* Esta classe é anotada com {@code @Service} para indicar que é um componente de serviço
* Spring, tornando-a detectável para a injeção de dependência.
* </p>
*
* @author diegoneves
* @see DepositEntityService
* @see DepositRepository
* @see DepositFactory
* @since 1.3.0
*/
@Service
public class DepositEntityServiceImpl implements DepositEntityService {

private static final int DEFAULT_AMOUNT = 1;
public static final Integer DEPOSIT_VALIDATION_ERROR = 39;

private final DepositRepository depositRepository;

public DepositEntityServiceImpl(DepositRepository depositRepository) {
this.depositRepository = depositRepository;
}

@Override
public DepositEntity create(DepositDTO dto) {
depositValidate(dto);
DepositEntity newDeposit = DepositFactory.createDepositEntity(dto.getDescription(), dto.getAmount());
return this.depositRepository.save(newDeposit);
}

/**
* Valida o objeto {@link DepositDTO} para garantir que está devidamente populado
* e contém dados válidos.
* <p>
* A validação inclui a verificação se o DTO e seus campos de descrição e valor
* não são nulos ou vazios. Também garante que o valor não seja menor que o valor
* padrão, atualizando-o se necessário.
* </p>
*
* @param dto o objeto {@link DepositDTO} a ser validado.
* Não deve ser nulo e deve conter uma descrição e um valor não nulos/não vazios.
* @throws DepositEntityFailuresException se algum dos campos (dto, descrição, valor)
* forem nulos ou vazios.
* @implNote O método {@link ValidationUtils#validateNotNullOrEmpty(Object, Integer, Class)} é utilizado
* para realizar as verificações de validação, lançando a exceção {@link DepositEntityFailuresException}
* com o código de erro específico {@code DEPOSIT_VALIDATION_ERROR} se alguma regra de validação for violada.
*/
private static void depositValidate(DepositDTO dto) {
ValidationUtils.validateNotNullOrEmpty(dto, DEPOSIT_VALIDATION_ERROR, DepositEntityFailuresException.class);
ValidationUtils.validateNotNullOrEmpty(dto.getDescription(), DEPOSIT_VALIDATION_ERROR, DepositEntityFailuresException.class);
ValidationUtils.validateNotNullOrEmpty(dto.getAmount(), DEPOSIT_VALIDATION_ERROR, DepositEntityFailuresException.class);
if (dto.getAmount() < DEFAULT_AMOUNT) {
dto.setAmount(DEFAULT_AMOUNT);
}
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package diegosneves.github.conectardoacoes.adapters.rest.service.impl;

import diegosneves.github.conectardoacoes.adapters.rest.dto.DepositDTO;
import diegosneves.github.conectardoacoes.adapters.rest.enums.ExceptionDetails;
import diegosneves.github.conectardoacoes.adapters.rest.exception.DepositEntityFailuresException;
import diegosneves.github.conectardoacoes.adapters.rest.model.DepositEntity;
import diegosneves.github.conectardoacoes.adapters.rest.repository.DepositRepository;
import diegosneves.github.conectardoacoes.core.utils.UuidUtils;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.springframework.test.context.junit.jupiter.SpringExtension;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@ExtendWith(SpringExtension.class)
class DepositEntityServiceImplTest {

public static final String DEPOSIT_UUID = "0f46f5b9-ab37-478b-86f5-b9ab37878b33";
public static final String DESCRIPTION = "Item 01";
public static final int AMOUNT = 1;


@InjectMocks
private DepositEntityServiceImpl service;

@Mock
private DepositRepository repository;

@Captor
private ArgumentCaptor<DepositEntity> depositCaptor;

private DepositDTO depositDTO;
private DepositEntity depositEntity;

@BeforeEach
void setUp() {
this.depositDTO = new DepositDTO(DESCRIPTION, AMOUNT);
this.depositEntity = new DepositEntity(DEPOSIT_UUID, DESCRIPTION, AMOUNT);
}

@Test
void shouldCreateAndSaveDepositEntitySuccessfully() {
when(this.repository.save(any(DepositEntity.class))).thenReturn(this.depositEntity);

DepositEntity actual = this.service.create(this.depositDTO);

verify(this.repository, times(1)).save(this.depositCaptor.capture());

assertNotNull(actual);
assertEquals(DEPOSIT_UUID, actual.getId());
assertEquals(DESCRIPTION, actual.getDescription());
assertEquals(AMOUNT, actual.getAmount());
DepositEntity capturedDeposit = this.depositCaptor.getValue();
assertNotNull(capturedDeposit);
assertNotNull(capturedDeposit.getId());
assertTrue(UuidUtils.isValidUUID(capturedDeposit.getId()));
assertEquals(DESCRIPTION, capturedDeposit.getDescription());
assertEquals(AMOUNT, capturedDeposit.getAmount());
}

@ParameterizedTest
@ValueSource(ints = {0, -3, -58})
void shouldSetMinimumAmountAndSaveDepositEntityWhenInvalidAmountProvided(Integer value) {
this.depositDTO.setAmount(value);
when(this.repository.save(any(DepositEntity.class))).thenReturn(this.depositEntity);

DepositEntity actual = this.service.create(this.depositDTO);

verify(this.repository, times(1)).save(this.depositCaptor.capture());

assertNotNull(actual);
assertEquals(DEPOSIT_UUID, actual.getId());
assertEquals(DESCRIPTION, actual.getDescription());
assertEquals(AMOUNT, actual.getAmount());
DepositEntity capturedDeposit = this.depositCaptor.getValue();
assertNotNull(capturedDeposit);
assertNotNull(capturedDeposit.getId());
assertTrue(UuidUtils.isValidUUID(capturedDeposit.getId()));
assertEquals(DESCRIPTION, capturedDeposit.getDescription());
assertEquals(AMOUNT, capturedDeposit.getAmount());
}

@Test
void shouldThrowExceptionWhenDescriptionIsNull(){
this.depositDTO.setDescription(null);

DepositEntityFailuresException exception = assertThrows(DepositEntityFailuresException.class, () -> this.service.create(this.depositDTO));

verify(repository, never()).save(any(DepositEntity.class));

assertNotNull(exception);
assertEquals(ExceptionDetails.getExceptionDetails(DepositEntityServiceImpl.DEPOSIT_VALIDATION_ERROR).formatErrorMessage(), exception.getMessage());
assertNull(exception.getCause());
}

@ParameterizedTest
@ValueSource(strings = {"", " "})
void shouldThrowExceptionWhenDescriptionIsEmptyOrBlank(String value){
this.depositDTO.setDescription(value);

DepositEntityFailuresException exception = assertThrows(DepositEntityFailuresException.class, () -> this.service.create(this.depositDTO));

verify(repository, never()).save(any(DepositEntity.class));

assertNotNull(exception);
assertEquals(ExceptionDetails.getExceptionDetails(DepositEntityServiceImpl.DEPOSIT_VALIDATION_ERROR).formatErrorMessage(), exception.getMessage());
assertNull(exception.getCause());
}

@Test
void shouldThrowExceptionWhenAmountIsNull(){
this.depositDTO.setAmount(null);

DepositEntityFailuresException exception = assertThrows(DepositEntityFailuresException.class, () -> this.service.create(this.depositDTO));

verify(repository, never()).save(any(DepositEntity.class));

assertNotNull(exception);
assertEquals(ExceptionDetails.getExceptionDetails(DepositEntityServiceImpl.DEPOSIT_VALIDATION_ERROR).formatErrorMessage(), exception.getMessage());
assertNull(exception.getCause());
}


}

0 comments on commit 1554bd2

Please sign in to comment.