Main #5
@@ -0,0 +1,12 @@
|
|||||||
|
package fr.iut_fbleau.but3.dev62.mylibrary.avis;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Builder
|
||||||
|
@Getter
|
||||||
|
public class AvisDTO {
|
||||||
|
private final UUID avisId;
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package fr.iut_fbleau.but3.dev62.mylibrary.avis;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public record AvisInfo(
|
||||||
|
UUID clientId,
|
||||||
|
UUID livreId,
|
||||||
|
int note,
|
||||||
|
String commentaire,
|
||||||
|
LocalDate dateAchat
|
||||||
|
) {
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package fr.iut_fbleau.but3.dev62.mylibrary.avis.converter;
|
||||||
|
|
||||||
|
import fr.iut_fbleau.but3.dev62.mylibrary.avis.AvisDTO;
|
||||||
|
import fr.iut_fbleau.but3.dev62.mylibrary.avis.AvisInfo;
|
||||||
|
import fr.iut_fbleau.but3.dev62.mylibrary.avis.entity.Avis;
|
||||||
|
|
||||||
|
public final class AvisConverter {
|
||||||
|
|
||||||
|
private AvisConverter() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Avis toDomain(AvisInfo avisInfo) {
|
||||||
|
return Avis.builder()
|
||||||
|
.clientId(avisInfo.clientId())
|
||||||
|
.livreId(avisInfo.livreId())
|
||||||
|
.note(avisInfo.note())
|
||||||
|
.commentaire(avisInfo.commentaire())
|
||||||
|
.dateAchat(avisInfo.dateAchat())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AvisDTO toDTO(Avis avis) {
|
||||||
|
return AvisDTO.builder()
|
||||||
|
.avisId(avis.getId())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
-76
@@ -1,76 +0,0 @@
|
|||||||
package fr.iut_fbleau.but3.dev62.mylibrary.avis.converter;
|
|
||||||
|
|
||||||
import fr.iut_fbleau.but3.dev62.mylibrary.avis.AvisDTO;
|
|
||||||
import fr.iut_fbleau.but3.dev62.mylibrary.avis.AvisInfo;
|
|
||||||
import fr.iut_fbleau.but3.dev62.mylibrary.avis.entity.Avis;
|
|
||||||
import org.junit.jupiter.api.DisplayName;
|
|
||||||
import org.junit.jupiter.api.Nested;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
@DisplayName("AvisConverter Unit Tests")
|
|
||||||
class AvisConverterTest {
|
|
||||||
|
|
||||||
private final UUID clientId = UUID.randomUUID();
|
|
||||||
private final UUID livreId = UUID.randomUUID();
|
|
||||||
|
|
||||||
@Nested
|
|
||||||
@DisplayName("toDomain() method tests")
|
|
||||||
class ToDomainTests {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("Should convert AvisInfo to Avis domain object")
|
|
||||||
void shouldConvertAvisInfoToDomain() {
|
|
||||||
AvisInfo avisInfo = new AvisInfo(
|
|
||||||
clientId, livreId, 5, "Excellent livre !", LocalDate.of(2024, 1, 15)
|
|
||||||
);
|
|
||||||
|
|
||||||
Avis result = AvisConverter.toDomain(avisInfo);
|
|
||||||
|
|
||||||
assertNotNull(result);
|
|
||||||
assertEquals(clientId, result.getClientId());
|
|
||||||
assertEquals(livreId, result.getLivreId());
|
|
||||||
assertEquals(5, result.getNote());
|
|
||||||
assertEquals("Excellent livre !", result.getCommentaire());
|
|
||||||
assertEquals(LocalDate.of(2024, 1, 15), result.getDateAchat());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("Should have null ID after toDomain (set by repository)")
|
|
||||||
void shouldHaveNullIdAfterToDomain() {
|
|
||||||
AvisInfo avisInfo = new AvisInfo(clientId, livreId, 3, "Commentaire", LocalDate.now());
|
|
||||||
|
|
||||||
Avis result = AvisConverter.toDomain(avisInfo);
|
|
||||||
|
|
||||||
assertNull(result.getId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nested
|
|
||||||
@DisplayName("toDTO() method tests")
|
|
||||||
class ToDTOTests {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("Should convert Avis domain object to AvisDTO")
|
|
||||||
void shouldConvertAvisToDTO() {
|
|
||||||
UUID avisId = UUID.randomUUID();
|
|
||||||
Avis avis = Avis.builder()
|
|
||||||
.id(avisId)
|
|
||||||
.clientId(clientId)
|
|
||||||
.livreId(livreId)
|
|
||||||
.note(5)
|
|
||||||
.commentaire("Excellent !")
|
|
||||||
.dateAchat(LocalDate.of(2024, 1, 15))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
AvisDTO result = AvisConverter.toDTO(avis);
|
|
||||||
|
|
||||||
assertNotNull(result);
|
|
||||||
assertEquals(avisId, result.getAvisId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package fr.iut_fbleau.but3.dev62.mylibrary.avis.entity;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Builder
|
||||||
|
@Getter
|
||||||
|
public class Avis {
|
||||||
|
private UUID id;
|
||||||
|
private UUID clientId;
|
||||||
|
private UUID livreId;
|
||||||
|
private int note;
|
||||||
|
private String commentaire;
|
||||||
|
private LocalDate dateAchat;
|
||||||
|
|
||||||
|
public void setRandomUUID() {
|
||||||
|
this.id = UUID.randomUUID();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
package fr.iut_fbleau.but3.dev62.mylibrary.avis.entity;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.DisplayName;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
class AvisTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("Builder should create a valid Avis instance")
|
|
||||||
void testAvisBuilder() {
|
|
||||||
UUID id = UUID.randomUUID();
|
|
||||||
UUID clientId = UUID.randomUUID();
|
|
||||||
UUID livreId = UUID.randomUUID();
|
|
||||||
|
|
||||||
Avis avis = Avis.builder()
|
|
||||||
.id(id)
|
|
||||||
.clientId(clientId)
|
|
||||||
.livreId(livreId)
|
|
||||||
.note(5)
|
|
||||||
.commentaire("Excellent livre !")
|
|
||||||
.dateAchat(LocalDate.of(2024, 1, 15))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
assertEquals(id, avis.getId());
|
|
||||||
assertEquals(clientId, avis.getClientId());
|
|
||||||
assertEquals(livreId, avis.getLivreId());
|
|
||||||
assertEquals(5, avis.getNote());
|
|
||||||
assertEquals("Excellent livre !", avis.getCommentaire());
|
|
||||||
assertEquals(LocalDate.of(2024, 1, 15), avis.getDateAchat());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("setRandomUUID should set a new non-null UUID")
|
|
||||||
void testSetRandomUUID() {
|
|
||||||
Avis avis = Avis.builder().build();
|
|
||||||
UUID originalId = avis.getId();
|
|
||||||
|
|
||||||
avis.setRandomUUID();
|
|
||||||
|
|
||||||
assertNotNull(avis.getId());
|
|
||||||
assertNotEquals(originalId, avis.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("Two setRandomUUID calls should produce different UUIDs")
|
|
||||||
void testSetRandomUUIDTwice() {
|
|
||||||
Avis avis = Avis.builder().build();
|
|
||||||
avis.setRandomUUID();
|
|
||||||
UUID firstId = avis.getId();
|
|
||||||
|
|
||||||
avis.setRandomUUID();
|
|
||||||
|
|
||||||
assertNotEquals(firstId, avis.getId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
package fr.iut_fbleau.but3.dev62.mylibrary.avis.exception;
|
||||||
|
|
||||||
|
import java.text.MessageFormat;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class AvisNotFoundException extends Exception {
|
||||||
|
|
||||||
|
public static final String THE_AVIS_WITH_ID_DOES_NOT_EXIST_MESSAGE = "The avis with id {0} does not exist";
|
||||||
|
|
||||||
|
public AvisNotFoundException(UUID uuid) {
|
||||||
|
super(MessageFormat.format(THE_AVIS_WITH_ID_DOES_NOT_EXIST_MESSAGE, uuid));
|
||||||
|
}
|
||||||
|
}
|
||||||
-47
@@ -1,47 +0,0 @@
|
|||||||
package fr.iut_fbleau.but3.dev62.mylibrary.avis.exception;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.DisplayName;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
||||||
|
|
||||||
class AvisNotFoundExceptionTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("Exception message should contain the UUID provided")
|
|
||||||
void testExceptionMessageContainsUUID() {
|
|
||||||
UUID uuid = UUID.randomUUID();
|
|
||||||
|
|
||||||
AvisNotFoundException exception = new AvisNotFoundException(uuid);
|
|
||||||
|
|
||||||
String expectedMessage = String.format("The avis with id %s does not exist", uuid);
|
|
||||||
assertEquals(expectedMessage, exception.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("Exception should use the correct constant message format")
|
|
||||||
void testExceptionUsesConstantMessageFormat() {
|
|
||||||
UUID uuid = UUID.randomUUID();
|
|
||||||
|
|
||||||
AvisNotFoundException exception = new AvisNotFoundException(uuid);
|
|
||||||
|
|
||||||
assertEquals("The avis with id {0} does not exist",
|
|
||||||
AvisNotFoundException.THE_AVIS_WITH_ID_DOES_NOT_EXIST_MESSAGE);
|
|
||||||
assertTrue(exception.getMessage().contains(uuid.toString()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("Exception should be properly thrown and caught")
|
|
||||||
void testExceptionCanBeThrownAndCaught() {
|
|
||||||
UUID uuid = UUID.randomUUID();
|
|
||||||
|
|
||||||
try {
|
|
||||||
throw new AvisNotFoundException(uuid);
|
|
||||||
} catch (AvisNotFoundException e) {
|
|
||||||
assertTrue(e.getMessage().contains(uuid.toString()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
package fr.iut_fbleau.but3.dev62.mylibrary.avis.exception;
|
||||||
|
|
||||||
|
public class NotValidAvisException extends Exception {
|
||||||
|
|
||||||
|
public NotValidAvisException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
-62
@@ -1,62 +0,0 @@
|
|||||||
package fr.iut_fbleau.but3.dev62.mylibrary.avis.exception;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.DisplayName;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
|
||||||
import org.junit.jupiter.params.provider.ValueSource;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
|
||||||
|
|
||||||
class NotValidAvisExceptionTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("Exception should be created with the provided message")
|
|
||||||
void testExceptionCreation() {
|
|
||||||
String errorMessage = "Note must be between 1 and 5";
|
|
||||||
|
|
||||||
NotValidAvisException exception = new NotValidAvisException(errorMessage);
|
|
||||||
|
|
||||||
assertEquals(errorMessage, exception.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
@ParameterizedTest
|
|
||||||
@ValueSource(strings = {
|
|
||||||
"Note must be between 1 and 5",
|
|
||||||
"Commentaire cannot be blank",
|
|
||||||
"Client id cannot be null",
|
|
||||||
"Livre id cannot be null",
|
|
||||||
"Date achat cannot be null"
|
|
||||||
})
|
|
||||||
@DisplayName("Exception should handle different validation messages")
|
|
||||||
void testExceptionWithDifferentMessages(String errorMessage) {
|
|
||||||
NotValidAvisException exception = new NotValidAvisException(errorMessage);
|
|
||||||
|
|
||||||
assertEquals(errorMessage, exception.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("Exception should be properly thrown and caught")
|
|
||||||
void testExceptionCanBeThrownAndCaught() {
|
|
||||||
String errorMessage = "Note must be between 1 and 5";
|
|
||||||
|
|
||||||
Exception exception = assertThrows(NotValidAvisException.class, () -> {
|
|
||||||
throw new NotValidAvisException(errorMessage);
|
|
||||||
});
|
|
||||||
|
|
||||||
assertEquals(errorMessage, exception.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("Exception should be catchable as a general Exception")
|
|
||||||
void testExceptionInheritance() {
|
|
||||||
String errorMessage = "Commentaire cannot be blank";
|
|
||||||
|
|
||||||
try {
|
|
||||||
throw new NotValidAvisException(errorMessage);
|
|
||||||
} catch (Exception e) {
|
|
||||||
assertEquals(NotValidAvisException.class, e.getClass());
|
|
||||||
assertEquals(errorMessage, e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package fr.iut_fbleau.but3.dev62.mylibrary.avis.repository;
|
||||||
|
|
||||||
|
import fr.iut_fbleau.but3.dev62.mylibrary.avis.entity.Avis;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@NoArgsConstructor
|
||||||
|
public final class AvisRepository {
|
||||||
|
|
||||||
|
private final List<Avis> avisList = new ArrayList<>();
|
||||||
|
|
||||||
|
public List<Avis> findAll() {
|
||||||
|
return avisList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteAll() {
|
||||||
|
avisList.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Avis save(Avis newAvis) {
|
||||||
|
Optional<Avis> existing = this.findById(newAvis.getId());
|
||||||
|
existing.ifPresentOrElse(avisList::remove, newAvis::setRandomUUID);
|
||||||
|
this.avisList.add(newAvis);
|
||||||
|
return newAvis;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Avis> findById(UUID uuid) {
|
||||||
|
return this.avisList.stream()
|
||||||
|
.filter(avis -> avis.getId().equals(uuid))
|
||||||
|
.findFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean existsById(UUID uuid) {
|
||||||
|
return this.avisList.stream()
|
||||||
|
.anyMatch(avis -> avis.getId().equals(uuid));
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Avis> findByLivreId(UUID livreId) {
|
||||||
|
return this.avisList.stream()
|
||||||
|
.filter(avis -> avis.getLivreId().equals(livreId))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Avis> findByClientId(UUID clientId) {
|
||||||
|
return this.avisList.stream()
|
||||||
|
.filter(avis -> avis.getClientId().equals(clientId))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void delete(Avis avis) {
|
||||||
|
avisList.remove(avis);
|
||||||
|
}
|
||||||
|
}
|
||||||
-184
@@ -1,184 +0,0 @@
|
|||||||
package fr.iut_fbleau.but3.dev62.mylibrary.avis.repository;
|
|
||||||
|
|
||||||
import fr.iut_fbleau.but3.dev62.mylibrary.avis.entity.Avis;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.DisplayName;
|
|
||||||
import org.junit.jupiter.api.Nested;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
class AvisRepositoryTest {
|
|
||||||
|
|
||||||
private AvisRepository repository;
|
|
||||||
private Avis avis1;
|
|
||||||
private Avis avis2;
|
|
||||||
private UUID clientId1;
|
|
||||||
private UUID livreId1;
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
void setUp() {
|
|
||||||
repository = new AvisRepository();
|
|
||||||
clientId1 = UUID.randomUUID();
|
|
||||||
livreId1 = UUID.randomUUID();
|
|
||||||
|
|
||||||
avis1 = Avis.builder()
|
|
||||||
.clientId(clientId1)
|
|
||||||
.livreId(livreId1)
|
|
||||||
.note(5)
|
|
||||||
.commentaire("Excellent livre !")
|
|
||||||
.dateAchat(LocalDate.of(2024, 1, 15))
|
|
||||||
.build();
|
|
||||||
avis1.setRandomUUID();
|
|
||||||
|
|
||||||
avis2 = Avis.builder()
|
|
||||||
.clientId(UUID.randomUUID())
|
|
||||||
.livreId(livreId1)
|
|
||||||
.note(3)
|
|
||||||
.commentaire("Pas mal")
|
|
||||||
.dateAchat(LocalDate.of(2024, 2, 10))
|
|
||||||
.build();
|
|
||||||
avis2.setRandomUUID();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("New repository should be empty")
|
|
||||||
void testNewRepositoryIsEmpty() {
|
|
||||||
assertTrue(repository.findAll().isEmpty());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nested
|
|
||||||
@DisplayName("Save operations")
|
|
||||||
class SaveOperations {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("Save should add a new avis")
|
|
||||||
void testSaveNewAvis() {
|
|
||||||
Avis saved = repository.save(avis1);
|
|
||||||
|
|
||||||
assertEquals(1, repository.findAll().size());
|
|
||||||
assertEquals(avis1.getId(), saved.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("Save should update existing avis with same ID")
|
|
||||||
void testSaveUpdatesExistingAvis() {
|
|
||||||
repository.save(avis1);
|
|
||||||
UUID id = avis1.getId();
|
|
||||||
|
|
||||||
Avis updated = Avis.builder()
|
|
||||||
.id(id)
|
|
||||||
.clientId(clientId1)
|
|
||||||
.livreId(livreId1)
|
|
||||||
.note(3)
|
|
||||||
.commentaire("Finalement moyen")
|
|
||||||
.dateAchat(LocalDate.of(2024, 1, 15))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
Avis saved = repository.save(updated);
|
|
||||||
|
|
||||||
assertEquals(1, repository.findAll().size());
|
|
||||||
assertEquals(id, saved.getId());
|
|
||||||
assertEquals(3, saved.getNote());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("Save multiple avis should add all of them")
|
|
||||||
void testSaveMultipleAvis() {
|
|
||||||
repository.save(avis1);
|
|
||||||
repository.save(avis2);
|
|
||||||
|
|
||||||
assertEquals(2, repository.findAll().size());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nested
|
|
||||||
@DisplayName("Find operations")
|
|
||||||
class FindOperations {
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
void setUpAvis() {
|
|
||||||
repository.save(avis1);
|
|
||||||
repository.save(avis2);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("FindById should return avis with matching ID")
|
|
||||||
void testFindById() {
|
|
||||||
Optional<Avis> found = repository.findById(avis1.getId());
|
|
||||||
|
|
||||||
assertTrue(found.isPresent());
|
|
||||||
assertEquals(avis1.getId(), found.get().getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("FindById should return empty Optional when ID doesn't exist")
|
|
||||||
void testFindByIdNotFound() {
|
|
||||||
Optional<Avis> found = repository.findById(UUID.randomUUID());
|
|
||||||
|
|
||||||
assertTrue(found.isEmpty());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("FindByLivreId should return all avis for a book")
|
|
||||||
void testFindByLivreId() {
|
|
||||||
List<Avis> found = repository.findByLivreId(livreId1);
|
|
||||||
|
|
||||||
assertEquals(2, found.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("FindByClientId should return all avis for a client")
|
|
||||||
void testFindByClientId() {
|
|
||||||
List<Avis> found = repository.findByClientId(clientId1);
|
|
||||||
|
|
||||||
assertEquals(1, found.size());
|
|
||||||
assertEquals(avis1.getId(), found.getFirst().getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("ExistsById should return true when ID exists")
|
|
||||||
void testExistsByIdExists() {
|
|
||||||
assertTrue(repository.existsById(avis1.getId()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("ExistsById should return false when ID doesn't exist")
|
|
||||||
void testExistsByIdNotExists() {
|
|
||||||
assertFalse(repository.existsById(UUID.randomUUID()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nested
|
|
||||||
@DisplayName("Delete operations")
|
|
||||||
class DeleteOperations {
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
void setUpAvis() {
|
|
||||||
repository.save(avis1);
|
|
||||||
repository.save(avis2);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("Delete should remove the specified avis")
|
|
||||||
void testDelete() {
|
|
||||||
repository.delete(avis1);
|
|
||||||
|
|
||||||
assertEquals(1, repository.findAll().size());
|
|
||||||
assertFalse(repository.findAll().contains(avis1));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("DeleteAll should remove all avis")
|
|
||||||
void testDeleteAll() {
|
|
||||||
repository.deleteAll();
|
|
||||||
|
|
||||||
assertTrue(repository.findAll().isEmpty());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package fr.iut_fbleau.but3.dev62.mylibrary.avis.usecase;
|
||||||
|
|
||||||
|
import fr.iut_fbleau.but3.dev62.mylibrary.avis.AvisDTO;
|
||||||
|
import fr.iut_fbleau.but3.dev62.mylibrary.avis.AvisInfo;
|
||||||
|
import fr.iut_fbleau.but3.dev62.mylibrary.avis.converter.AvisConverter;
|
||||||
|
import fr.iut_fbleau.but3.dev62.mylibrary.avis.entity.Avis;
|
||||||
|
import fr.iut_fbleau.but3.dev62.mylibrary.avis.exception.NotValidAvisException;
|
||||||
|
import fr.iut_fbleau.but3.dev62.mylibrary.avis.repository.AvisRepository;
|
||||||
|
import fr.iut_fbleau.but3.dev62.mylibrary.avis.validator.AvisValidator;
|
||||||
|
import fr.iut_fbleau.but3.dev62.mylibrary.customer.exception.CustomerNotFoundException;
|
||||||
|
import fr.iut_fbleau.but3.dev62.mylibrary.customer.repository.CustomerRepository;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public final class AvisUseCase {
|
||||||
|
|
||||||
|
private final AvisRepository avisRepository;
|
||||||
|
private final CustomerRepository customerRepository;
|
||||||
|
|
||||||
|
public AvisUseCase(AvisRepository avisRepository, CustomerRepository customerRepository) {
|
||||||
|
this.avisRepository = avisRepository;
|
||||||
|
this.customerRepository = customerRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AvisDTO gererAvis(AvisInfo avisInfo) throws NotValidAvisException, CustomerNotFoundException {
|
||||||
|
AvisValidator.validate(avisInfo);
|
||||||
|
|
||||||
|
customerRepository.findById(avisInfo.clientId())
|
||||||
|
.orElseThrow(() -> new CustomerNotFoundException(avisInfo.clientId()));
|
||||||
|
|
||||||
|
Avis avis = AvisConverter.toDomain(avisInfo);
|
||||||
|
Avis savedAvis = avisRepository.save(avis);
|
||||||
|
|
||||||
|
return AvisConverter.toDTO(savedAvis);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<AvisDTO> findAvisById(UUID uuid) {
|
||||||
|
return avisRepository.findById(uuid).map(AvisConverter::toDTO);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,169 +0,0 @@
|
|||||||
package fr.iut_fbleau.but3.dev62.mylibrary.avis.usecase;
|
|
||||||
|
|
||||||
import fr.iut_fbleau.but3.dev62.mylibrary.avis.AvisDTO;
|
|
||||||
import fr.iut_fbleau.but3.dev62.mylibrary.avis.AvisInfo;
|
|
||||||
import fr.iut_fbleau.but3.dev62.mylibrary.avis.entity.Avis;
|
|
||||||
import fr.iut_fbleau.but3.dev62.mylibrary.avis.exception.NotValidAvisException;
|
|
||||||
import fr.iut_fbleau.but3.dev62.mylibrary.avis.repository.AvisRepository;
|
|
||||||
import fr.iut_fbleau.but3.dev62.mylibrary.customer.entity.Customer;
|
|
||||||
import fr.iut_fbleau.but3.dev62.mylibrary.customer.exception.CustomerNotFoundException;
|
|
||||||
import fr.iut_fbleau.but3.dev62.mylibrary.customer.repository.CustomerRepository;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.DisplayName;
|
|
||||||
import org.junit.jupiter.api.Nested;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
|
||||||
import org.mockito.InjectMocks;
|
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
|
||||||
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
|
||||||
import static org.mockito.Mockito.*;
|
|
||||||
|
|
||||||
@ExtendWith(MockitoExtension.class)
|
|
||||||
class AvisUseCaseTest {
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private AvisRepository avisRepository;
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private CustomerRepository customerRepository;
|
|
||||||
|
|
||||||
@InjectMocks
|
|
||||||
private AvisUseCase avisUseCase;
|
|
||||||
|
|
||||||
private UUID clientId;
|
|
||||||
private UUID livreId;
|
|
||||||
private Customer testCustomer;
|
|
||||||
private AvisInfo validAvisInfo;
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
void setUp() {
|
|
||||||
clientId = UUID.randomUUID();
|
|
||||||
livreId = UUID.randomUUID();
|
|
||||||
|
|
||||||
testCustomer = Customer.builder()
|
|
||||||
.id(clientId)
|
|
||||||
.firstName("Marie")
|
|
||||||
.lastName("Dupont")
|
|
||||||
.phoneNumber("0612345678")
|
|
||||||
.loyaltyPoints(100)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
validAvisInfo = new AvisInfo(clientId, livreId, 5, "Excellent livre !", LocalDate.of(2024, 1, 15));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nested
|
|
||||||
@DisplayName("GererAvis tests")
|
|
||||||
class GererAvisTests {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("Should create avis when valid data is provided")
|
|
||||||
void testGererAvisWithValidData() throws NotValidAvisException, CustomerNotFoundException {
|
|
||||||
when(customerRepository.findById(clientId)).thenReturn(Optional.of(testCustomer));
|
|
||||||
|
|
||||||
UUID avisId = UUID.randomUUID();
|
|
||||||
Avis savedAvis = Avis.builder()
|
|
||||||
.id(avisId)
|
|
||||||
.clientId(clientId)
|
|
||||||
.livreId(livreId)
|
|
||||||
.note(5)
|
|
||||||
.commentaire("Excellent livre !")
|
|
||||||
.dateAchat(LocalDate.of(2024, 1, 15))
|
|
||||||
.build();
|
|
||||||
when(avisRepository.save(any(Avis.class))).thenReturn(savedAvis);
|
|
||||||
|
|
||||||
AvisDTO result = avisUseCase.gererAvis(validAvisInfo);
|
|
||||||
|
|
||||||
assertNotNull(result);
|
|
||||||
assertEquals(avisId, result.getAvisId());
|
|
||||||
verify(avisRepository, times(1)).save(any(Avis.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("Should throw exception when customer does not exist")
|
|
||||||
void testGererAvisWithUnknownCustomer() {
|
|
||||||
when(customerRepository.findById(clientId)).thenReturn(Optional.empty());
|
|
||||||
|
|
||||||
assertThrows(CustomerNotFoundException.class,
|
|
||||||
() -> avisUseCase.gererAvis(validAvisInfo));
|
|
||||||
|
|
||||||
verify(avisRepository, never()).save(any(Avis.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("Should throw exception when note is invalid")
|
|
||||||
void testGererAvisWithInvalidNote() {
|
|
||||||
AvisInfo invalidAvis = new AvisInfo(clientId, livreId, 6, "Commentaire", LocalDate.now());
|
|
||||||
|
|
||||||
assertThrows(NotValidAvisException.class,
|
|
||||||
() -> avisUseCase.gererAvis(invalidAvis));
|
|
||||||
|
|
||||||
verify(avisRepository, never()).save(any(Avis.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("Should throw exception when commentaire is blank")
|
|
||||||
void testGererAvisWithBlankCommentaire() {
|
|
||||||
AvisInfo invalidAvis = new AvisInfo(clientId, livreId, 5, "", LocalDate.now());
|
|
||||||
|
|
||||||
assertThrows(NotValidAvisException.class,
|
|
||||||
() -> avisUseCase.gererAvis(invalidAvis));
|
|
||||||
|
|
||||||
verify(avisRepository, never()).save(any(Avis.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("Should throw exception when clientId is null")
|
|
||||||
void testGererAvisWithNullClientId() {
|
|
||||||
AvisInfo invalidAvis = new AvisInfo(null, livreId, 5, "Commentaire", LocalDate.now());
|
|
||||||
|
|
||||||
assertThrows(NotValidAvisException.class,
|
|
||||||
() -> avisUseCase.gererAvis(invalidAvis));
|
|
||||||
|
|
||||||
verify(avisRepository, never()).save(any(Avis.class));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nested
|
|
||||||
@DisplayName("FindAvis tests")
|
|
||||||
class FindAvisTests {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("Should return avis when ID exists")
|
|
||||||
void testFindAvisById() {
|
|
||||||
UUID avisId = UUID.randomUUID();
|
|
||||||
Avis avis = Avis.builder()
|
|
||||||
.id(avisId)
|
|
||||||
.clientId(clientId)
|
|
||||||
.livreId(livreId)
|
|
||||||
.note(5)
|
|
||||||
.commentaire("Excellent !")
|
|
||||||
.dateAchat(LocalDate.now())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
when(avisRepository.findById(avisId)).thenReturn(Optional.of(avis));
|
|
||||||
|
|
||||||
Optional<AvisDTO> result = avisUseCase.findAvisById(avisId);
|
|
||||||
|
|
||||||
assertTrue(result.isPresent());
|
|
||||||
assertEquals(avisId, result.get().getAvisId());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("Should return empty Optional when ID does not exist")
|
|
||||||
void testFindAvisByIdNotFound() {
|
|
||||||
UUID nonExistentId = UUID.randomUUID();
|
|
||||||
when(avisRepository.findById(nonExistentId)).thenReturn(Optional.empty());
|
|
||||||
|
|
||||||
Optional<AvisDTO> result = avisUseCase.findAvisById(nonExistentId);
|
|
||||||
|
|
||||||
assertTrue(result.isEmpty());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package fr.iut_fbleau.but3.dev62.mylibrary.avis.validator;
|
||||||
|
|
||||||
|
import fr.iut_fbleau.but3.dev62.mylibrary.avis.AvisInfo;
|
||||||
|
import fr.iut_fbleau.but3.dev62.mylibrary.avis.exception.NotValidAvisException;
|
||||||
|
|
||||||
|
public final class AvisValidator {
|
||||||
|
|
||||||
|
public static final String CLIENT_ID_CANNOT_BE_NULL = "Client id cannot be null";
|
||||||
|
public static final String LIVRE_ID_CANNOT_BE_NULL = "Livre id cannot be null";
|
||||||
|
public static final String NOTE_MUST_BE_BETWEEN_1_AND_5 = "Note must be between 1 and 5";
|
||||||
|
public static final String COMMENTAIRE_CANNOT_BE_BLANK = "Commentaire cannot be blank";
|
||||||
|
public static final String DATE_ACHAT_CANNOT_BE_NULL = "Date achat cannot be null";
|
||||||
|
|
||||||
|
private AvisValidator() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void validate(AvisInfo avisInfo) throws NotValidAvisException {
|
||||||
|
validateClientId(avisInfo);
|
||||||
|
validateLivreId(avisInfo);
|
||||||
|
validateNote(avisInfo);
|
||||||
|
validateCommentaire(avisInfo);
|
||||||
|
validateDateAchat(avisInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void validateClientId(AvisInfo avisInfo) throws NotValidAvisException {
|
||||||
|
if (avisInfo.clientId() == null) {
|
||||||
|
throw new NotValidAvisException(CLIENT_ID_CANNOT_BE_NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void validateLivreId(AvisInfo avisInfo) throws NotValidAvisException {
|
||||||
|
if (avisInfo.livreId() == null) {
|
||||||
|
throw new NotValidAvisException(LIVRE_ID_CANNOT_BE_NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void validateNote(AvisInfo avisInfo) throws NotValidAvisException {
|
||||||
|
if (avisInfo.note() < 1 || avisInfo.note() > 5) {
|
||||||
|
throw new NotValidAvisException(NOTE_MUST_BE_BETWEEN_1_AND_5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void validateCommentaire(AvisInfo avisInfo) throws NotValidAvisException {
|
||||||
|
if (avisInfo.commentaire() == null || avisInfo.commentaire().isBlank()) {
|
||||||
|
throw new NotValidAvisException(COMMENTAIRE_CANNOT_BE_BLANK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void validateDateAchat(AvisInfo avisInfo) throws NotValidAvisException {
|
||||||
|
if (avisInfo.dateAchat() == null) {
|
||||||
|
throw new NotValidAvisException(DATE_ACHAT_CANNOT_BE_NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
-131
@@ -1,131 +0,0 @@
|
|||||||
package fr.iut_fbleau.but3.dev62.mylibrary.avis.validator;
|
|
||||||
|
|
||||||
import fr.iut_fbleau.but3.dev62.mylibrary.avis.AvisInfo;
|
|
||||||
import fr.iut_fbleau.but3.dev62.mylibrary.avis.exception.NotValidAvisException;
|
|
||||||
import org.junit.jupiter.api.DisplayName;
|
|
||||||
import org.junit.jupiter.api.Nested;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
|
||||||
import org.junit.jupiter.params.provider.ValueSource;
|
|
||||||
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
class AvisValidatorTest {
|
|
||||||
|
|
||||||
private final UUID clientId = UUID.randomUUID();
|
|
||||||
private final UUID livreId = UUID.randomUUID();
|
|
||||||
|
|
||||||
private AvisInfo validAvis() {
|
|
||||||
return new AvisInfo(clientId, livreId, 5, "Excellent livre !", LocalDate.of(2024, 1, 15));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("Should validate avis with valid data")
|
|
||||||
void testValidateValidAvis() {
|
|
||||||
assertDoesNotThrow(() -> AvisValidator.validate(validAvis()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nested
|
|
||||||
@DisplayName("ClientId validation tests")
|
|
||||||
class ClientIdValidationTests {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("Should throw exception when clientId is null")
|
|
||||||
void testValidateNullClientId() {
|
|
||||||
AvisInfo avis = new AvisInfo(null, livreId, 5, "Commentaire", LocalDate.now());
|
|
||||||
|
|
||||||
NotValidAvisException exception = assertThrows(NotValidAvisException.class,
|
|
||||||
() -> AvisValidator.validate(avis));
|
|
||||||
|
|
||||||
assertEquals(AvisValidator.CLIENT_ID_CANNOT_BE_NULL, exception.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nested
|
|
||||||
@DisplayName("LivreId validation tests")
|
|
||||||
class LivreIdValidationTests {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("Should throw exception when livreId is null")
|
|
||||||
void testValidateNullLivreId() {
|
|
||||||
AvisInfo avis = new AvisInfo(clientId, null, 5, "Commentaire", LocalDate.now());
|
|
||||||
|
|
||||||
NotValidAvisException exception = assertThrows(NotValidAvisException.class,
|
|
||||||
() -> AvisValidator.validate(avis));
|
|
||||||
|
|
||||||
assertEquals(AvisValidator.LIVRE_ID_CANNOT_BE_NULL, exception.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nested
|
|
||||||
@DisplayName("Note validation tests")
|
|
||||||
class NoteValidationTests {
|
|
||||||
|
|
||||||
@ParameterizedTest
|
|
||||||
@ValueSource(ints = {1, 2, 3, 4, 5})
|
|
||||||
@DisplayName("Should validate when note is between 1 and 5")
|
|
||||||
void testValidateValidNote(int validNote) {
|
|
||||||
AvisInfo avis = new AvisInfo(clientId, livreId, validNote, "Commentaire", LocalDate.now());
|
|
||||||
assertDoesNotThrow(() -> AvisValidator.validate(avis));
|
|
||||||
}
|
|
||||||
|
|
||||||
@ParameterizedTest
|
|
||||||
@ValueSource(ints = {0, -1, 6, 10})
|
|
||||||
@DisplayName("Should throw exception when note is out of range")
|
|
||||||
void testValidateInvalidNote(int invalidNote) {
|
|
||||||
AvisInfo avis = new AvisInfo(clientId, livreId, invalidNote, "Commentaire", LocalDate.now());
|
|
||||||
|
|
||||||
NotValidAvisException exception = assertThrows(NotValidAvisException.class,
|
|
||||||
() -> AvisValidator.validate(avis));
|
|
||||||
|
|
||||||
assertEquals(AvisValidator.NOTE_MUST_BE_BETWEEN_1_AND_5, exception.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nested
|
|
||||||
@DisplayName("Commentaire validation tests")
|
|
||||||
class CommentaireValidationTests {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("Should throw exception when commentaire is blank")
|
|
||||||
void testValidateBlankCommentaire() {
|
|
||||||
AvisInfo avis = new AvisInfo(clientId, livreId, 5, "", LocalDate.now());
|
|
||||||
|
|
||||||
NotValidAvisException exception = assertThrows(NotValidAvisException.class,
|
|
||||||
() -> AvisValidator.validate(avis));
|
|
||||||
|
|
||||||
assertEquals(AvisValidator.COMMENTAIRE_CANNOT_BE_BLANK, exception.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
@ParameterizedTest
|
|
||||||
@ValueSource(strings = {" ", " ", "\t", "\n"})
|
|
||||||
@DisplayName("Should throw exception when commentaire contains only whitespace")
|
|
||||||
void testValidateWhitespaceCommentaire(String whitespace) {
|
|
||||||
AvisInfo avis = new AvisInfo(clientId, livreId, 5, whitespace, LocalDate.now());
|
|
||||||
|
|
||||||
NotValidAvisException exception = assertThrows(NotValidAvisException.class,
|
|
||||||
() -> AvisValidator.validate(avis));
|
|
||||||
|
|
||||||
assertEquals(AvisValidator.COMMENTAIRE_CANNOT_BE_BLANK, exception.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nested
|
|
||||||
@DisplayName("DateAchat validation tests")
|
|
||||||
class DateAchatValidationTests {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisplayName("Should throw exception when dateAchat is null")
|
|
||||||
void testValidateNullDateAchat() {
|
|
||||||
AvisInfo avis = new AvisInfo(clientId, livreId, 5, "Commentaire", null);
|
|
||||||
|
|
||||||
NotValidAvisException exception = assertThrows(NotValidAvisException.class,
|
|
||||||
() -> AvisValidator.validate(avis));
|
|
||||||
|
|
||||||
assertEquals(AvisValidator.DATE_ACHAT_CANNOT_BE_NULL, exception.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user