feat: add tests

This commit is contained in:
2025-12-03 19:36:02 +01:00
parent 17687916c9
commit 27535e063c
6 changed files with 1057 additions and 0 deletions

View File

@@ -0,0 +1,297 @@
package local.epul4a.fotosharing.integration;
import local.epul4a.fotosharing.model.Commentaire;
import local.epul4a.fotosharing.model.Photo;
import local.epul4a.fotosharing.model.Utilisateur;
import local.epul4a.fotosharing.repository.CommentaireRepository;
import local.epul4a.fotosharing.repository.PhotoRepository;
import local.epul4a.fotosharing.repository.UtilisateurRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
/**
* Tests d'intégration pour la suppression en cascade
* Teste : Supprimer un user doit supprimer ses photos et commentaires
*/
@SpringBootTest
@ActiveProfiles("test")
@Transactional
class CascadeDeleteIntegrationTest {
@Autowired
private UtilisateurRepository utilisateurRepository;
@Autowired
private PhotoRepository photoRepository;
@Autowired
private CommentaireRepository commentaireRepository;
@Autowired
private PasswordEncoder passwordEncoder;
private Utilisateur userA;
private Utilisateur userB;
@BeforeEach
void setUp() {
// Nettoyer la BDD
commentaireRepository.deleteAll();
photoRepository.deleteAll();
utilisateurRepository.deleteAll();
// Créer User A
userA = new Utilisateur();
userA.setEmail("userA@example.com");
userA.setNom("A");
userA.setPrenom("User");
userA.setPassword(passwordEncoder.encode("password"));
userA = utilisateurRepository.save(userA);
// Créer User B
userB = new Utilisateur();
userB.setEmail("userB@example.com");
userB.setNom("B");
userB.setPrenom("User");
userB.setPassword(passwordEncoder.encode("password"));
userB = utilisateurRepository.save(userB);
}
@Test
void testDeleteUser_ShouldDeleteUserOwnPhotos() {
// Given - User A possède 3 photos
Photo photo1 = createPhoto("photo1.jpg", userA);
Photo photo2 = createPhoto("photo2.jpg", userA);
Photo photo3 = createPhoto("photo3.jpg", userA);
photoRepository.save(photo1);
photoRepository.save(photo2);
photoRepository.save(photo3);
// Given - User B possède 2 photos (ne doivent PAS être supprimées)
Photo photoB1 = createPhoto("photoB1.jpg", userB);
Photo photoB2 = createPhoto("photoB2.jpg", userB);
photoRepository.save(photoB1);
photoRepository.save(photoB2);
// Vérifier la situation initiale
assertEquals(5, photoRepository.count(), "5 photos au total");
assertEquals(3, photoRepository.findByProprietaire_Email(userA.getEmail()).size());
assertEquals(2, photoRepository.findByProprietaire_Email(userB.getEmail()).size());
// When - Supprimer User A
utilisateurRepository.delete(userA);
utilisateurRepository.flush();
// Then - Les photos de User A doivent être supprimées
assertEquals(2, photoRepository.count(),
"Seules les 2 photos de User B doivent rester");
List<Photo> remainingPhotos = photoRepository.findAll();
assertEquals(2, remainingPhotos.size());
assertTrue(remainingPhotos.stream().allMatch(p -> p.getProprietaire().getId().equals(userB.getId())),
"Toutes les photos restantes doivent appartenir à User B");
}
@Test
void testDeleteUser_ShouldDeleteUserOwnComments() {
// Given - Photo de User B
Photo photoB = createPhoto("photoB.jpg", userB);
photoB = photoRepository.save(photoB);
// Given - User A commente la photo de User B
Commentaire comment1 = createCommentaire("Super photo !", userA, photoB);
Commentaire comment2 = createCommentaire("J'adore !", userA, photoB);
Commentaire comment3 = createCommentaire("Merci !", userB, photoB); // Commentaire de User B
commentaireRepository.save(comment1);
commentaireRepository.save(comment2);
commentaireRepository.save(comment3);
// Vérifier la situation initiale
assertEquals(3, commentaireRepository.count());
// When - Supprimer User A
utilisateurRepository.delete(userA);
utilisateurRepository.flush();
// Then - Les commentaires de User A doivent être supprimés
assertEquals(1, commentaireRepository.count(),
"Seul le commentaire de User B doit rester");
List<Commentaire> remainingComments = commentaireRepository.findAll();
assertEquals(1, remainingComments.size());
assertEquals(userB.getId(), remainingComments.get(0).getAuteur().getId(),
"Le commentaire restant doit être celui de User B");
}
@Test
void testDeleteUser_ShouldDeletePhotosAndAllCommentsOnThosePhotos() {
// Given - User A possède une photo
Photo photoA = createPhoto("photoA.jpg", userA);
photoA = photoRepository.save(photoA);
// Given - Plusieurs utilisateurs commentent la photo de User A
Commentaire commentByA = createCommentaire("Ma photo", userA, photoA);
Commentaire commentByB = createCommentaire("Belle photo", userB, photoA);
commentaireRepository.save(commentByA);
commentaireRepository.save(commentByB);
// Vérifier la situation initiale
assertEquals(1, photoRepository.count());
assertEquals(2, commentaireRepository.count());
// When - Supprimer User A (et donc sa photo)
utilisateurRepository.delete(userA);
utilisateurRepository.flush();
// Then - La photo ET tous ses commentaires doivent être supprimés
assertEquals(0, photoRepository.count(),
"La photo de User A doit être supprimée");
assertEquals(0, commentaireRepository.count(),
"TOUS les commentaires sur la photo supprimée doivent disparaître (cascade)");
}
@Test
void testDeleteUser_ComplexScenario() {
// Scénario complexe :
// - User A a 2 photos
// - User B a 1 photo
// - User A commente sa propre photo ET la photo de User B
// - User B commente sa propre photo ET une photo de User A
// Given - Photos
Photo photoA1 = photoRepository.save(createPhoto("photoA1.jpg", userA));
Photo photoA2 = photoRepository.save(createPhoto("photoA2.jpg", userA));
Photo photoB = photoRepository.save(createPhoto("photoB.jpg", userB));
// Given - Commentaires
commentaireRepository.save(createCommentaire("A sur A1", userA, photoA1));
commentaireRepository.save(createCommentaire("A sur A2", userA, photoA2));
commentaireRepository.save(createCommentaire("A sur B", userA, photoB));
commentaireRepository.save(createCommentaire("B sur A1", userB, photoA1));
commentaireRepository.save(createCommentaire("B sur B", userB, photoB));
// Vérifier la situation initiale
assertEquals(3, photoRepository.count());
assertEquals(5, commentaireRepository.count());
// When - Supprimer User A
utilisateurRepository.delete(userA);
utilisateurRepository.flush();
// Then - Vérifier les suppressions
assertEquals(1, photoRepository.count(),
"Seule la photo de User B doit rester");
List<Photo> remainingPhotos = photoRepository.findAll();
assertEquals(photoB.getId(), remainingPhotos.get(0).getId());
// Then - Vérifier les commentaires restants
List<Commentaire> remainingComments = commentaireRepository.findAll();
assertEquals(1, remainingComments.size(),
"Seul le commentaire 'B sur B' doit rester");
Commentaire lastComment = remainingComments.get(0);
assertEquals("B sur B", lastComment.getTexte());
assertEquals(userB.getId(), lastComment.getAuteur().getId());
assertEquals(photoB.getId(), lastComment.getPhoto().getId());
}
@Test
void testDeleteUser_ShouldNotAffectOtherUsers() {
// Given - Créer User C
Utilisateur userC = new Utilisateur();
userC.setEmail("userC@example.com");
userC.setNom("C");
userC.setPrenom("User");
userC.setPassword(passwordEncoder.encode("password"));
userC = utilisateurRepository.save(userC);
// Given - Chaque utilisateur a des photos et commentaires
Photo photoA = photoRepository.save(createPhoto("photoA.jpg", userA));
Photo photoB = photoRepository.save(createPhoto("photoB.jpg", userB));
Photo photoC = photoRepository.save(createPhoto("photoC.jpg", userC));
commentaireRepository.save(createCommentaire("Comment A", userA, photoA));
commentaireRepository.save(createCommentaire("Comment B", userB, photoB));
commentaireRepository.save(createCommentaire("Comment C", userC, photoC));
// Vérifier la situation initiale
assertEquals(3, utilisateurRepository.count());
assertEquals(3, photoRepository.count());
assertEquals(3, commentaireRepository.count());
// When - Supprimer User A
utilisateurRepository.delete(userA);
utilisateurRepository.flush();
// Then - User B et C ne doivent pas être affectés
assertEquals(2, utilisateurRepository.count(),
"Users B et C doivent rester");
assertEquals(2, photoRepository.count(),
"Photos de B et C doivent rester");
assertEquals(2, commentaireRepository.count(),
"Commentaires de B et C doivent rester");
// Then - Vérifier que les bonnes entités restent
assertTrue(utilisateurRepository.findById(userB.getId()).isPresent());
assertTrue(utilisateurRepository.findById(userC.getId()).isPresent());
assertFalse(utilisateurRepository.findById(userA.getId()).isPresent());
}
@Test
void testDeleteMultipleUsers_ShouldDeleteRespectiveData() {
// Given - Chaque user a des photos
photoRepository.save(createPhoto("photoA.jpg", userA));
photoRepository.save(createPhoto("photoB.jpg", userB));
assertEquals(2, photoRepository.count());
// When - Supprimer les deux utilisateurs
utilisateurRepository.delete(userA);
utilisateurRepository.delete(userB);
utilisateurRepository.flush();
// Then - Tout doit être supprimé
assertEquals(0, utilisateurRepository.count());
assertEquals(0, photoRepository.count());
}
// ===================== Méthodes Utilitaires =====================
private Photo createPhoto(String filename, Utilisateur owner) {
Photo photo = new Photo();
photo.setNomFichierOriginal(filename);
photo.setUuidFichier("uuid-" + filename);
photo.setUuidThumbnail("thumb-" + filename);
photo.setVisibilite(Photo.Visibilite.PUBLIC);
photo.setDateUpload(LocalDateTime.now());
photo.setProprietaire(owner);
photo.setMimeType("image/jpeg");
photo.setTailleFichier(1024L);
return photo;
}
private Commentaire createCommentaire(String texte, Utilisateur auteur, Photo photo) {
Commentaire commentaire = new Commentaire();
commentaire.setTexte(texte);
commentaire.setAuteur(auteur);
commentaire.setPhoto(photo);
commentaire.setDateCommentaire(LocalDateTime.now());
return commentaire;
}
}

View File

@@ -0,0 +1,288 @@
package local.epul4a.fotosharing.integration;
import local.epul4a.fotosharing.dto.PhotoDTO;
import local.epul4a.fotosharing.model.Photo;
import local.epul4a.fotosharing.model.Utilisateur;
import local.epul4a.fotosharing.repository.PhotoRepository;
import local.epul4a.fotosharing.repository.UtilisateurRepository;
import local.epul4a.fotosharing.service.PhotoService;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.test.context.ActiveProfiles;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import static org.junit.jupiter.api.Assertions.*;
/**
* Tests d'intégration pour le flux complet d'upload de photos
* Teste : Envoi fichier -> Vérification présence sur disque -> Vérification entrée en BDD
*/
@SpringBootTest
@ActiveProfiles("test")
class PhotoUploadIntegrationTest {
@Autowired
private PhotoService photoService;
@Autowired
private PhotoRepository photoRepository;
@Autowired
private UtilisateurRepository utilisateurRepository;
@Autowired
private PasswordEncoder passwordEncoder;
@Value("${file.upload-dir}")
private String uploadDir;
private Utilisateur testUser;
private Path uploadPath;
@BeforeEach
void setUp() throws IOException {
// Créer le dossier d'upload pour les tests
uploadPath = Paths.get(uploadDir);
if (!Files.exists(uploadPath)) {
Files.createDirectories(uploadPath);
}
// Créer un utilisateur de test
testUser = new Utilisateur();
testUser.setEmail("testupload@example.com");
testUser.setNom("Upload");
testUser.setPrenom("Test");
testUser.setPassword(passwordEncoder.encode("password"));
testUser = utilisateurRepository.save(testUser);
}
@AfterEach
void tearDown() throws IOException {
// Nettoyer les photos de test
photoRepository.findAll().forEach(photo -> {
try {
// Supprimer les fichiers sur disque
Path originalFile = uploadPath.resolve(photo.getUuidFichier());
Path thumbFile = uploadPath.resolve(photo.getUuidThumbnail());
Files.deleteIfExists(originalFile);
Files.deleteIfExists(thumbFile);
} catch (IOException e) {
// Ignorer les erreurs de suppression
}
});
// Supprimer de la BDD
photoRepository.deleteAll();
utilisateurRepository.deleteAll();
}
@Test
void testCompleteUploadFlow_ShouldCreateFileOnDiskAndDatabaseEntry() throws IOException {
// Given - Créer un fichier JPEG valide
byte[] jpegContent = createValidJpegBytes();
MockMultipartFile file = new MockMultipartFile(
"file",
"test-photo.jpg",
"image/jpeg",
jpegContent
);
// When - Upload de la photo
PhotoDTO uploadedPhoto = photoService.store(file, "PUBLIC", testUser.getEmail());
// Then - Vérifier que le DTO est retourné
assertNotNull(uploadedPhoto, "Le DTO de la photo uploadée ne doit pas être null");
assertNotNull(uploadedPhoto.getId(), "L'ID de la photo doit être généré");
assertEquals("test-photo.jpg", uploadedPhoto.getNomFichierOriginal());
assertEquals("PUBLIC", uploadedPhoto.getVisibilite());
assertEquals("image/jpeg", uploadedPhoto.getMimeType());
assertEquals(jpegContent.length, uploadedPhoto.getTailleFichier());
// Then - Vérifier la présence du fichier ORIGINAL sur le disque
Path originalFile = uploadPath.resolve(uploadedPhoto.getUuidFichier());
assertTrue(Files.exists(originalFile),
"Le fichier original doit exister sur le disque : " + originalFile);
assertEquals(jpegContent.length, Files.size(originalFile),
"La taille du fichier sur disque doit correspondre");
// Then - Vérifier la présence de la MINIATURE sur le disque
Path thumbnailFile = uploadPath.resolve(uploadedPhoto.getUuidThumbnail());
assertTrue(Files.exists(thumbnailFile),
"La miniature doit exister sur le disque : " + thumbnailFile);
assertTrue(Files.size(thumbnailFile) > 0,
"La miniature ne doit pas être vide");
// Then - Vérifier l'entrée en BDD
Photo photoInDb = photoRepository.findById(uploadedPhoto.getId()).orElse(null);
assertNotNull(photoInDb, "La photo doit être enregistrée en BDD");
assertEquals("test-photo.jpg", photoInDb.getNomFichierOriginal());
assertEquals(Photo.Visibilite.PUBLIC, photoInDb.getVisibilite());
assertEquals(testUser.getId(), photoInDb.getProprietaire().getId());
assertEquals("image/jpeg", photoInDb.getMimeType());
assertEquals((long) jpegContent.length, photoInDb.getTailleFichier());
// Then - Vérifier les métadonnées
assertNotNull(photoInDb.getLargeur(), "La largeur doit être extraite");
assertNotNull(photoInDb.getHauteur(), "La hauteur doit être extraite");
assertTrue(photoInDb.getLargeur() > 0, "La largeur doit être positive");
assertTrue(photoInDb.getHauteur() > 0, "La hauteur doit être positive");
}
@Test
void testUploadPngFile_ShouldDetectCorrectMimeType() {
// Given - Créer un fichier PNG valide
byte[] pngContent = createValidPngBytes();
MockMultipartFile file = new MockMultipartFile(
"file",
"test-image.png",
"image/png",
pngContent
);
// When
PhotoDTO uploadedPhoto = photoService.store(file, "PRIVATE", testUser.getEmail());
// Then - Vérifier le type MIME détecté
assertNotNull(uploadedPhoto);
assertEquals("image/png", uploadedPhoto.getMimeType(),
"Le type MIME doit être détecté comme PNG");
// Then - Vérifier en BDD
Photo photoInDb = photoRepository.findById(uploadedPhoto.getId()).orElse(null);
assertNotNull(photoInDb);
assertEquals("image/png", photoInDb.getMimeType());
}
@Test
void testUploadMultiplePhotos_ShouldCreateSeparateFiles() throws IOException {
// Given - Uploader plusieurs photos
for (int i = 1; i <= 3; i++) {
byte[] content = createValidJpegBytes();
MockMultipartFile file = new MockMultipartFile(
"file",
"photo-" + i + ".jpg",
"image/jpeg",
content
);
// When
photoService.store(file, "PUBLIC", testUser.getEmail());
}
// Then - Vérifier que 3 photos sont en BDD
long photoCount = photoRepository.count();
assertEquals(3, photoCount, "3 photos doivent être enregistrées");
// Then - Vérifier que tous les fichiers existent
photoRepository.findAll().forEach(photo -> {
Path originalFile = uploadPath.resolve(photo.getUuidFichier());
Path thumbFile = uploadPath.resolve(photo.getUuidThumbnail());
assertTrue(Files.exists(originalFile),
"Le fichier original doit exister : " + photo.getUuidFichier());
assertTrue(Files.exists(thumbFile),
"La miniature doit exister : " + photo.getUuidThumbnail());
});
}
@Test
void testUploadWithDifferentVisibilities() {
// Test chaque type de visibilité
Photo.Visibilite[] visibilities = {Photo.Visibilite.PUBLIC, Photo.Visibilite.PRIVATE, Photo.Visibilite.SHARED};
for (Photo.Visibilite visibility : visibilities) {
// Given
byte[] content = createValidJpegBytes();
MockMultipartFile file = new MockMultipartFile(
"file",
"photo-" + visibility.name().toLowerCase() + ".jpg",
"image/jpeg",
content
);
// When
PhotoDTO uploadedPhoto = photoService.store(file, visibility.name(), testUser.getEmail());
// Then
assertEquals(visibility.name(), uploadedPhoto.getVisibilite(),
"La visibilité doit être " + visibility);
Photo photoInDb = photoRepository.findById(uploadedPhoto.getId()).orElse(null);
assertNotNull(photoInDb);
assertEquals(visibility, photoInDb.getVisibilite());
}
}
@Test
void testUpload_FileNameWithUUID_ShouldBeUnique() {
// Given - Uploader deux fichiers avec le même nom
byte[] content = createValidJpegBytes();
MockMultipartFile file1 = new MockMultipartFile("file", "same-name.jpg", "image/jpeg", content);
MockMultipartFile file2 = new MockMultipartFile("file", "same-name.jpg", "image/jpeg", content);
// When
PhotoDTO photo1 = photoService.store(file1, "PUBLIC", testUser.getEmail());
PhotoDTO photo2 = photoService.store(file2, "PUBLIC", testUser.getEmail());
// Then - Les UUID doivent être différents
assertNotEquals(photo1.getUuidFichier(), photo2.getUuidFichier(),
"Les UUID des fichiers doivent être différents même avec le même nom original");
// Then - Les deux fichiers doivent exister sur le disque
assertTrue(Files.exists(uploadPath.resolve(photo1.getUuidFichier())));
assertTrue(Files.exists(uploadPath.resolve(photo2.getUuidFichier())));
}
// ===================== Méthodes Utilitaires =====================
/**
* Crée un tableau de bytes représentant un JPEG valide minimal
*/
private byte[] createValidJpegBytes() {
// Magic number JPEG + données minimales pour créer une image valide
return new byte[]{
(byte) 0xFF, (byte) 0xD8, (byte) 0xFF, (byte) 0xE0, // JPEG header
0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, // JFIF marker
0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00,
(byte) 0xFF, (byte) 0xDB, 0x00, 0x43, 0x00, // Quantization table
0x08, 0x06, 0x06, 0x07, 0x06, 0x05, 0x08, 0x07,
0x07, 0x07, 0x09, 0x09, 0x08, 0x0A, 0x0C, 0x14,
0x0D, 0x0C, 0x0B, 0x0B, 0x0C, 0x19, 0x12, 0x13,
0x0F, 0x14, 0x1D, 0x1A, 0x1F, 0x1E, 0x1D, 0x1A,
0x1C, 0x1C, 0x20, 0x24, 0x2E, 0x27, 0x20, 0x22,
0x2C, 0x23, 0x1C, 0x1C, 0x28, 0x37, 0x29, 0x2C,
0x30, 0x31, 0x34, 0x34, 0x34, 0x1F, 0x27, 0x39,
0x3D, 0x38, 0x32, 0x3C, 0x2E, 0x33, 0x34, 0x32,
(byte) 0xFF, (byte) 0xD9 // End of image
};
}
/**
* Crée un tableau de bytes représentant un PNG valide minimal
*/
private byte[] createValidPngBytes() {
// Magic number PNG + données minimales
return new byte[]{
(byte) 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, // PNG signature
0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, // IHDR chunk
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, // 1x1 pixel
0x08, 0x06, 0x00, 0x00, 0x00, 0x1F, 0x15, (byte) 0xC4, (byte) 0x89,
0x00, 0x00, 0x00, 0x0A, 0x49, 0x44, 0x41, 0x54, // IDAT chunk
0x78, (byte) 0x9C, 0x63, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, // IEND chunk
(byte) 0xAE, 0x42, 0x60, (byte) 0x82
};
}
}

View File

@@ -0,0 +1,153 @@
package local.epul4a.fotosharing.mapper;
import local.epul4a.fotosharing.dto.PhotoDTO;
import local.epul4a.fotosharing.dto.UtilisateurDTO;
import local.epul4a.fotosharing.model.Photo;
import local.epul4a.fotosharing.model.Utilisateur;
import org.junit.jupiter.api.Test;
import java.time.LocalDateTime;
import static org.junit.jupiter.api.Assertions.*;
/**
* Tests unitaires pour PhotoMapper
* Teste la conversion bidirectionnelle entre Photo et PhotoDTO
*/
class PhotoMapperTest {
@Test
void testToDTO_WithCompletePhoto_ShouldMapAllFields() {
// Given - Créer un utilisateur
Utilisateur utilisateur = new Utilisateur();
utilisateur.setId(1L);
utilisateur.setEmail("test@example.com");
utilisateur.setNom("Dupont");
utilisateur.setPrenom("Jean");
// Given - Créer une photo complète
Photo photo = new Photo();
photo.setId(100L);
photo.setNomFichierOriginal("vacances.jpg");
photo.setUuidFichier("uuid-123-vacances.jpg");
photo.setUuidThumbnail("thumb-uuid-123-vacances.jpg");
photo.setDateUpload(LocalDateTime.of(2025, 12, 3, 10, 30));
photo.setVisibilite(Photo.Visibilite.PUBLIC);
photo.setProprietaire(utilisateur);
photo.setMimeType("image/jpeg");
photo.setTailleFichier(2457600L);
photo.setLargeur(1920);
photo.setHauteur(1080);
// When - Convertir en DTO
PhotoDTO dto = PhotoMapper.toDTO(photo);
// Then - Vérifier tous les champs
assertNotNull(dto, "Le DTO ne doit pas être null");
assertEquals(100L, dto.getId());
assertEquals("vacances.jpg", dto.getNomFichierOriginal());
assertEquals("uuid-123-vacances.jpg", dto.getUuidFichier());
assertEquals("thumb-uuid-123-vacances.jpg", dto.getUuidThumbnail());
assertEquals(LocalDateTime.of(2025, 12, 3, 10, 30), dto.getDateUpload());
assertEquals("PUBLIC", dto.getVisibilite());
assertEquals("image/jpeg", dto.getMimeType());
assertEquals(2457600L, dto.getTailleFichier());
assertEquals(1920, dto.getLargeur());
assertEquals(1080, dto.getHauteur());
// Then - Vérifier le propriétaire
assertNotNull(dto.getProprietaire(), "Le propriétaire ne doit pas être null");
assertEquals(1L, dto.getProprietaire().getId());
assertEquals("test@example.com", dto.getProprietaire().getEmail());
assertEquals("Dupont", dto.getProprietaire().getNom());
assertEquals("Jean", dto.getProprietaire().getPrenom());
}
@Test
void testToDTO_WithNullPhoto_ShouldReturnNull() {
// When - Convertir null
PhotoDTO dto = PhotoMapper.toDTO(null);
// Then
assertNull(dto, "Le DTO doit être null quand la photo est null");
}
@Test
void testToDTO_WithPhotoWithoutOwner_ShouldNotFail() {
// Given - Photo sans propriétaire
Photo photo = new Photo();
photo.setId(50L);
photo.setNomFichierOriginal("test.png");
photo.setVisibilite(Photo.Visibilite.PRIVATE);
// When
PhotoDTO dto = PhotoMapper.toDTO(photo);
// Then
assertNotNull(dto);
assertEquals(50L, dto.getId());
assertNull(dto.getProprietaire(), "Le propriétaire doit être null");
}
@Test
void testToDTO_WithMinimalPhoto_ShouldMapBasicFields() {
// Given - Photo avec champs minimaux
Photo photo = new Photo();
photo.setId(25L);
photo.setNomFichierOriginal("minimal.jpg");
photo.setVisibilite(Photo.Visibilite.SHARED);
// When
PhotoDTO dto = PhotoMapper.toDTO(photo);
// Then
assertNotNull(dto);
assertEquals(25L, dto.getId());
assertEquals("minimal.jpg", dto.getNomFichierOriginal());
assertEquals("SHARED", dto.getVisibilite());
assertNull(dto.getMimeType());
assertNull(dto.getTailleFichier());
}
@Test
void testToDTO_MetadataMapping() {
// Given - Photo avec métadonnées
Photo photo = new Photo();
photo.setId(1L);
photo.setNomFichierOriginal("hd-photo.jpg");
photo.setVisibilite(Photo.Visibilite.PUBLIC);
photo.setMimeType("image/png");
photo.setTailleFichier(5242880L); // 5 MB
photo.setLargeur(3840);
photo.setHauteur(2160);
// When
PhotoDTO dto = PhotoMapper.toDTO(photo);
// Then - Vérifier les métadonnées
assertEquals("image/png", dto.getMimeType());
assertEquals(5242880L, dto.getTailleFichier());
assertEquals(3840, dto.getLargeur());
assertEquals(2160, dto.getHauteur());
}
@Test
void testToDTO_AllVisibilityTypes() {
// Test pour chaque type de visibilité
for (Photo.Visibilite visibilite : Photo.Visibilite.values()) {
// Given
Photo photo = new Photo();
photo.setId(1L);
photo.setNomFichierOriginal("test.jpg");
photo.setVisibilite(visibilite);
// When
PhotoDTO dto = PhotoMapper.toDTO(photo);
// Then
assertEquals(visibilite.name(), dto.getVisibilite(),
"La visibilité " + visibilite + " doit être correctement mappée");
}
}
}

View File

@@ -0,0 +1,319 @@
package local.epul4a.fotosharing.security;
import local.epul4a.fotosharing.service.PartageService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.security.core.Authentication;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
/**
* Tests unitaires pour SecurityService
* Teste les scénarios d'accès aux photos (READ / COMMENT / ADMIN)
*/
@ExtendWith(MockitoExtension.class)
class SecurityServiceTest {
@Mock
private PartageService partageService;
@Mock
private Authentication authentication;
private SecurityService securityService;
@BeforeEach
void setUp() {
securityService = new SecurityService(partageService);
}
// ===================== TESTS canAccessPhoto =====================
@Test
void canAccessPhoto_UserAAccessesPrivatePhotoB_ShouldReturnFalse() {
// Given - User A essaie d'accéder à Photo B privée (pas de partage)
Long photoId = 100L;
String userAEmail = "userA@example.com";
when(authentication.isAuthenticated()).thenReturn(true);
when(authentication.getName()).thenReturn(userAEmail);
when(partageService.canView(photoId, userAEmail)).thenReturn(false);
// When
boolean canAccess = securityService.canAccessPhoto(authentication, photoId);
// Then
assertFalse(canAccess, "User A ne devrait PAS pouvoir accéder à Photo B privée");
verify(partageService).canView(photoId, userAEmail);
}
@Test
void canAccessPhoto_UserAAccessesSharedPhotoB_ShouldReturnTrue() {
// Given - User A accède à Photo B partagée avec lui (READ)
Long photoId = 200L;
String userAEmail = "userA@example.com";
when(authentication.isAuthenticated()).thenReturn(true);
when(authentication.getName()).thenReturn(userAEmail);
when(partageService.canView(photoId, userAEmail)).thenReturn(true);
// When
boolean canAccess = securityService.canAccessPhoto(authentication, photoId);
// Then
assertTrue(canAccess, "User A DEVRAIT pouvoir accéder à Photo B partagée");
verify(partageService).canView(photoId, userAEmail);
}
@Test
void canAccessPhoto_UserAAccessesOwnPhoto_ShouldReturnTrue() {
// Given - User A accède à sa propre photo
Long photoId = 300L;
String userAEmail = "userA@example.com";
when(authentication.isAuthenticated()).thenReturn(true);
when(authentication.getName()).thenReturn(userAEmail);
when(partageService.canView(photoId, userAEmail)).thenReturn(true); // Propriétaire = accès
// When
boolean canAccess = securityService.canAccessPhoto(authentication, photoId);
// Then
assertTrue(canAccess, "User A DEVRAIT pouvoir accéder à sa propre photo");
}
@Test
void canAccessPhoto_UnauthenticatedUser_ShouldReturnFalse() {
// Given - Utilisateur non authentifié
when(authentication.isAuthenticated()).thenReturn(false);
// When
boolean canAccess = securityService.canAccessPhoto(authentication, 100L);
// Then
assertFalse(canAccess, "Un utilisateur non authentifié ne devrait PAS avoir accès");
verify(partageService, never()).canView(anyLong(), anyString());
}
@Test
void canAccessPhoto_NullAuthentication_ShouldReturnFalse() {
// When
boolean canAccess = securityService.canAccessPhoto(null, 100L);
// Then
assertFalse(canAccess, "Avec authentication null, l'accès devrait être refusé");
verify(partageService, never()).canView(anyLong(), anyString());
}
@Test
void canAccessPhoto_PublicPhoto_ShouldReturnTrue() {
// Given - Photo publique (accessible par tous les utilisateurs authentifiés)
Long photoId = 400L;
String userEmail = "anyone@example.com";
when(authentication.isAuthenticated()).thenReturn(true);
when(authentication.getName()).thenReturn(userEmail);
when(partageService.canView(photoId, userEmail)).thenReturn(true);
// When
boolean canAccess = securityService.canAccessPhoto(authentication, photoId);
// Then
assertTrue(canAccess, "Une photo publique devrait être accessible");
}
// ===================== TESTS canCommentPhoto =====================
@Test
void canCommentPhoto_UserWithCommentRights_ShouldReturnTrue() {
// Given - User A a le droit COMMENT sur Photo B
Long photoId = 500L;
String userEmail = "userA@example.com";
when(authentication.isAuthenticated()).thenReturn(true);
when(authentication.getName()).thenReturn(userEmail);
when(partageService.canComment(photoId, userEmail)).thenReturn(true);
// When
boolean canComment = securityService.canCommentPhoto(authentication, photoId);
// Then
assertTrue(canComment, "User A DEVRAIT pouvoir commenter Photo B");
verify(partageService).canComment(photoId, userEmail);
}
@Test
void canCommentPhoto_UserWithoutCommentRights_ShouldReturnFalse() {
// Given - User A n'a que READ (pas COMMENT)
Long photoId = 600L;
String userEmail = "userA@example.com";
when(authentication.isAuthenticated()).thenReturn(true);
when(authentication.getName()).thenReturn(userEmail);
when(partageService.canComment(photoId, userEmail)).thenReturn(false);
// When
boolean canComment = securityService.canCommentPhoto(authentication, photoId);
// Then
assertFalse(canComment, "User A ne devrait PAS pouvoir commenter (pas de droit COMMENT)");
}
@Test
void canCommentPhoto_UnauthenticatedUser_ShouldReturnFalse() {
// Given
when(authentication.isAuthenticated()).thenReturn(false);
// When
boolean canComment = securityService.canCommentPhoto(authentication, 100L);
// Then
assertFalse(canComment);
verify(partageService, never()).canComment(anyLong(), anyString());
}
// ===================== TESTS canAdminPhoto =====================
@Test
void canAdminPhoto_OwnerOfPhoto_ShouldReturnTrue() {
// Given - Propriétaire de la photo
Long photoId = 700L;
String ownerEmail = "owner@example.com";
when(authentication.isAuthenticated()).thenReturn(true);
when(authentication.getName()).thenReturn(ownerEmail);
when(partageService.canAdmin(photoId, ownerEmail)).thenReturn(true);
// When
boolean canAdmin = securityService.canAdminPhoto(authentication, photoId);
// Then
assertTrue(canAdmin, "Le propriétaire DEVRAIT avoir les droits ADMIN");
}
@Test
void canAdminPhoto_UserWithAdminRights_ShouldReturnTrue() {
// Given - User A a le droit ADMIN sur Photo B (partagé par le propriétaire)
Long photoId = 800L;
String userEmail = "admin@example.com";
when(authentication.isAuthenticated()).thenReturn(true);
when(authentication.getName()).thenReturn(userEmail);
when(partageService.canAdmin(photoId, userEmail)).thenReturn(true);
// When
boolean canAdmin = securityService.canAdminPhoto(authentication, photoId);
// Then
assertTrue(canAdmin);
}
@Test
void canAdminPhoto_UserWithoutAdminRights_ShouldReturnFalse() {
// Given - User A n'a que READ ou COMMENT (pas ADMIN)
Long photoId = 900L;
String userEmail = "user@example.com";
when(authentication.isAuthenticated()).thenReturn(true);
when(authentication.getName()).thenReturn(userEmail);
when(partageService.canAdmin(photoId, userEmail)).thenReturn(false);
// When
boolean canAdmin = securityService.canAdminPhoto(authentication, photoId);
// Then
assertFalse(canAdmin, "User sans droit ADMIN ne devrait PAS pouvoir administrer");
}
@Test
void canAdminPhoto_NullAuthentication_ShouldReturnFalse() {
// When
boolean canAdmin = securityService.canAdminPhoto(null, 100L);
// Then
assertFalse(canAdmin);
verify(partageService, never()).canAdmin(anyLong(), anyString());
}
// ===================== TESTS Albums =====================
@Test
void canViewAlbum_UserWithAccess_ShouldReturnTrue() {
// Given
Long albumId = 1L;
String userEmail = "user@example.com";
when(authentication.isAuthenticated()).thenReturn(true);
when(authentication.getName()).thenReturn(userEmail);
when(partageService.canViewAlbum(albumId, userEmail)).thenReturn(true);
// When
boolean canView = securityService.canViewAlbum(authentication, albumId);
// Then
assertTrue(canView);
verify(partageService).canViewAlbum(albumId, userEmail);
}
@Test
void canCommentAlbum_UserWithoutAccess_ShouldReturnFalse() {
// Given
Long albumId = 2L;
String userEmail = "user@example.com";
when(authentication.isAuthenticated()).thenReturn(true);
when(authentication.getName()).thenReturn(userEmail);
when(partageService.canCommentAlbum(albumId, userEmail)).thenReturn(false);
// When
boolean canComment = securityService.canCommentAlbum(authentication, albumId);
// Then
assertFalse(canComment);
}
@Test
void canAdminAlbum_UnauthenticatedUser_ShouldReturnFalse() {
// Given
when(authentication.isAuthenticated()).thenReturn(false);
// When
boolean canAdmin = securityService.canAdminAlbum(authentication, 1L);
// Then
assertFalse(canAdmin);
verify(partageService, never()).canAdminAlbum(anyLong(), anyString());
}
// ===================== TESTS Scénarios Complexes =====================
@Test
void scenarioComplete_UserATriesDifferentActionsOnPhotoB() {
// Scénario : User A a seulement READ sur Photo B
Long photoId = 1000L;
String userAEmail = "userA@example.com";
when(authentication.isAuthenticated()).thenReturn(true);
when(authentication.getName()).thenReturn(userAEmail);
when(partageService.canView(photoId, userAEmail)).thenReturn(true);
when(partageService.canComment(photoId, userAEmail)).thenReturn(false);
when(partageService.canAdmin(photoId, userAEmail)).thenReturn(false);
// When/Then - User A peut voir
assertTrue(securityService.canAccessPhoto(authentication, photoId),
"User A devrait pouvoir VOIR la photo");
// When/Then - User A ne peut pas commenter
assertFalse(securityService.canCommentPhoto(authentication, photoId),
"User A ne devrait PAS pouvoir COMMENTER");
// When/Then - User A ne peut pas administrer
assertFalse(securityService.canAdminPhoto(authentication, photoId),
"User A ne devrait PAS pouvoir ADMINISTRER");
}
}