🔧 RESTRUCTURATION - UserResource déplacé de adapter.http vers application.rest - FournisseurResource déplacé vers application.rest - Suppression des contrôleurs obsolètes (presentation.controller) - Suppression de MaterielFournisseurService en doublon 📝 STANDARDISATION DOCUMENTATION - Annotations OpenAPI uniformes (@Operation, @APIResponse, @Parameter) - Descriptions concises et cohérentes pour tous les endpoints - Codes de réponse HTTP standards (200, 201, 400, 404, 500) 🛠️ ENDPOINTS USERS STANDARDISÉS - GET /api/v1/users - Liste tous les utilisateurs - GET /api/v1/users/{id} - Détails d'un utilisateur - GET /api/v1/users/stats - Statistiques globales - GET /api/v1/users/count - Comptage - GET /api/v1/users/pending - Utilisateurs en attente - POST /api/v1/users - Création - PUT /api/v1/users/{id} - Mise à jour - DELETE /api/v1/users/{id} - Suppression - POST /api/v1/users/{id}/approve - Approbation - POST /api/v1/users/{id}/reject - Rejet - PUT /api/v1/users/{id}/status - Changement de statut - PUT /api/v1/users/{id}/role - Changement de rôle ⚠️ GESTION D'ERREURS - Format uniforme: Map.of("error", "message") - Codes HTTP cohérents avec les autres ressources - Validation des entrées standardisée ✅ VALIDATION - Compilation réussie: mvn clean compile -DskipTests - Pattern conforme aux autres ressources (PhaseTemplate, Fournisseur) - Documentation OpenAPI/Swagger complète et cohérente
320 lines
11 KiB
Java
320 lines
11 KiB
Java
package dev.lions.btpxpress.application.service;
|
|
|
|
import static org.junit.jupiter.api.Assertions.*;
|
|
import static org.mockito.ArgumentMatchers.*;
|
|
import static org.mockito.Mockito.*;
|
|
|
|
import dev.lions.btpxpress.domain.core.entity.*;
|
|
import dev.lions.btpxpress.domain.infrastructure.repository.*;
|
|
import java.math.BigDecimal;
|
|
import java.time.LocalDate;
|
|
import java.time.LocalDateTime;
|
|
import java.util.*;
|
|
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.Mockito;
|
|
import org.mockito.junit.jupiter.MockitoExtension;
|
|
|
|
/**
|
|
* Tests complets pour StatisticsService Couverture exhaustive de toutes les méthodes et cas d'usage
|
|
*/
|
|
@ExtendWith(MockitoExtension.class)
|
|
@DisplayName("📊 Tests StatisticsService - Calculs de Statistiques")
|
|
class StatisticsServiceCompletTest {
|
|
|
|
@InjectMocks StatisticsService statisticsService;
|
|
|
|
@Mock ChantierRepository chantierRepository;
|
|
|
|
@Mock PhaseChantierRepository phaseChantierRepository;
|
|
|
|
@Mock EmployeRepository employeRepository;
|
|
|
|
@Mock EquipeRepository equipeRepository;
|
|
|
|
@Mock FournisseurRepository fournisseurRepository;
|
|
|
|
@Mock StockRepository stockRepository;
|
|
|
|
@Mock BonCommandeRepository bonCommandeRepository;
|
|
|
|
private UUID chantierId;
|
|
private UUID equipeId;
|
|
private UUID employeId;
|
|
private UUID fournisseurId;
|
|
private Chantier testChantier;
|
|
private Equipe testEquipe;
|
|
private Stock testStock;
|
|
private BonCommande testCommande;
|
|
|
|
@BeforeEach
|
|
void setUp() {
|
|
Mockito.reset(
|
|
chantierRepository,
|
|
phaseChantierRepository,
|
|
employeRepository,
|
|
equipeRepository,
|
|
fournisseurRepository,
|
|
stockRepository,
|
|
bonCommandeRepository);
|
|
|
|
chantierId = UUID.randomUUID();
|
|
equipeId = UUID.randomUUID();
|
|
employeId = UUID.randomUUID();
|
|
fournisseurId = UUID.randomUUID();
|
|
|
|
testChantier = new Chantier();
|
|
testChantier.setId(chantierId);
|
|
testChantier.setNom("Chantier Test");
|
|
testChantier.setStatut(StatutChantier.EN_COURS);
|
|
testChantier.setMontantPrevu(new BigDecimal("100000"));
|
|
testChantier.setMontantReel(new BigDecimal("80000"));
|
|
testChantier.setDateDebutPrevue(LocalDate.now().minusDays(30));
|
|
testChantier.setDateFinPrevue(LocalDate.now().plusDays(30));
|
|
|
|
testEquipe = new Equipe();
|
|
testEquipe.setId(equipeId);
|
|
testEquipe.setNom("Équipe Test");
|
|
testEquipe.setStatut(StatutEquipe.ACTIVE);
|
|
|
|
testStock = new Stock();
|
|
testStock.setId(UUID.randomUUID());
|
|
testStock.setDesignation("Article Test");
|
|
testStock.setQuantiteStock(new BigDecimal("100"));
|
|
testStock.setQuantiteMinimum(new BigDecimal("10"));
|
|
testStock.setCoutMoyenPondere(new BigDecimal("50"));
|
|
testStock.setCategorie(CategorieStock.OUTILLAGE);
|
|
testStock.setDateDerniereSortie(LocalDateTime.now().minusDays(5));
|
|
|
|
testCommande = new BonCommande();
|
|
testCommande.setId(UUID.randomUUID());
|
|
testCommande.setNumero("CMD-001");
|
|
testCommande.setMontantTTC(new BigDecimal("15000"));
|
|
testCommande.setDateCommande(LocalDate.now().minusDays(10));
|
|
testCommande.setDateLivraisonReelle(LocalDate.now().minusDays(3));
|
|
testCommande.setStatut(StatutBonCommande.LIVREE);
|
|
}
|
|
|
|
@Nested
|
|
@DisplayName("🏗️ Statistiques de Performance des Chantiers")
|
|
class PerformanceChantierTests {
|
|
|
|
@Test
|
|
@DisplayName("Calculer performance chantiers - période valide")
|
|
void testCalculerPerformanceChantiers_Valid() {
|
|
// Arrange
|
|
LocalDate dateDebut = LocalDate.now().minusMonths(1);
|
|
LocalDate dateFin = LocalDate.now();
|
|
List<Chantier> chantiers = Arrays.asList(testChantier);
|
|
|
|
when(chantierRepository.findChantiersParPeriode(dateDebut, dateFin)).thenReturn(chantiers);
|
|
|
|
// Act
|
|
Map<String, Object> result =
|
|
statisticsService.calculerPerformanceChantiers(dateDebut, dateFin);
|
|
|
|
// Assert
|
|
assertNotNull(result);
|
|
assertTrue(result.containsKey("chantiersParStatut"));
|
|
assertTrue(result.containsKey("tauxRespectDelais"));
|
|
assertTrue(result.containsKey("rentabiliteMoyenne"));
|
|
verify(chantierRepository).findChantiersParPeriode(dateDebut, dateFin);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("Calculer performance chantiers - aucun chantier")
|
|
void testCalculerPerformanceChantiers_Empty() {
|
|
// Arrange
|
|
LocalDate dateDebut = LocalDate.now().minusMonths(1);
|
|
LocalDate dateFin = LocalDate.now();
|
|
|
|
when(chantierRepository.findChantiersParPeriode(dateDebut, dateFin))
|
|
.thenReturn(Collections.emptyList());
|
|
|
|
// Act
|
|
Map<String, Object> result =
|
|
statisticsService.calculerPerformanceChantiers(dateDebut, dateFin);
|
|
|
|
// Assert
|
|
assertNotNull(result);
|
|
assertTrue(result.containsKey("chantiersParStatut"));
|
|
assertEquals(100.0, result.get("tauxRespectDelais"));
|
|
assertEquals(0.0, result.get("rentabiliteMoyenne"));
|
|
verify(chantierRepository).findChantiersParPeriode(dateDebut, dateFin);
|
|
}
|
|
}
|
|
|
|
@Nested
|
|
@DisplayName("👥 Statistiques de Performance des Équipes")
|
|
class PerformanceEquipeTests {
|
|
|
|
@Test
|
|
@DisplayName("Calculer productivité équipes")
|
|
void testCalculerProductiviteEquipes() {
|
|
// Arrange
|
|
List<Equipe> equipes = Arrays.asList(testEquipe);
|
|
List<PhaseChantier> phases =
|
|
Arrays.asList(
|
|
createPhaseChantier(StatutPhaseChantier.TERMINEE),
|
|
createPhaseChantier(StatutPhaseChantier.EN_COURS));
|
|
|
|
when(equipeRepository.listAll()).thenReturn(equipes);
|
|
when(phaseChantierRepository.findPhasesByEquipe(equipeId)).thenReturn(phases);
|
|
|
|
// Act
|
|
Map<String, Object> result = statisticsService.calculerProductiviteEquipes();
|
|
|
|
// Assert
|
|
assertNotNull(result);
|
|
assertTrue(result.containsKey("productiviteParEquipe"));
|
|
verify(equipeRepository).listAll();
|
|
verify(phaseChantierRepository).findPhasesByEquipe(equipeId);
|
|
}
|
|
|
|
private PhaseChantier createPhaseChantier(StatutPhaseChantier statut) {
|
|
PhaseChantier phase = new PhaseChantier();
|
|
phase.setId(UUID.randomUUID());
|
|
phase.setStatut(statut);
|
|
return phase;
|
|
}
|
|
}
|
|
|
|
@Nested
|
|
@DisplayName("📦 Statistiques de Rotation des Stocks")
|
|
class RotationStockTests {
|
|
|
|
@Test
|
|
@DisplayName("Calculer rotation stocks")
|
|
void testCalculerRotationStocks() {
|
|
// Arrange
|
|
List<Stock> stocks = Arrays.asList(testStock);
|
|
|
|
when(stockRepository.listAll()).thenReturn(stocks);
|
|
|
|
// Act
|
|
Map<String, Object> result = statisticsService.calculerRotationStocks();
|
|
|
|
// Assert
|
|
assertNotNull(result);
|
|
assertTrue(result.containsKey("articlesLesPlusActifs"));
|
|
assertTrue(result.containsKey("articlesSansMouvement"));
|
|
assertTrue(result.containsKey("valeurStocksDormants"));
|
|
assertTrue(result.containsKey("rotationParCategorie"));
|
|
verify(stockRepository).listAll();
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("Calculer rotation stocks - stocks vides")
|
|
void testCalculerRotationStocks_Empty() {
|
|
// Arrange
|
|
when(stockRepository.listAll()).thenReturn(Collections.emptyList());
|
|
|
|
// Act
|
|
Map<String, Object> result = statisticsService.calculerRotationStocks();
|
|
|
|
// Assert
|
|
assertNotNull(result);
|
|
assertEquals(0, ((List<?>) result.get("articlesLesPlusActifs")).size());
|
|
assertEquals(0, result.get("articlesSansMouvement"));
|
|
assertEquals(BigDecimal.ZERO, result.get("valeurStocksDormants"));
|
|
verify(stockRepository).listAll();
|
|
}
|
|
}
|
|
|
|
@Nested
|
|
@DisplayName("🛒 Analyse des Tendances d'Achat")
|
|
class TendancesAchatTests {
|
|
|
|
@Test
|
|
@DisplayName("Calculer tendances")
|
|
void testCalculerTendances() {
|
|
// Arrange
|
|
LocalDate dateDebut = LocalDate.now().minusMonths(1);
|
|
LocalDate dateFin = LocalDate.now();
|
|
List<BonCommande> commandes = Arrays.asList(testCommande);
|
|
|
|
when(bonCommandeRepository.findCommandesParPeriode(dateDebut, dateFin)).thenReturn(commandes);
|
|
|
|
// Act
|
|
Map<String, Object> result =
|
|
statisticsService.calculerTendances(dateDebut, dateFin, "MENSUEL");
|
|
|
|
// Assert
|
|
assertNotNull(result);
|
|
assertTrue(result.containsKey("chantiers"));
|
|
assertTrue(result.containsKey("achats"));
|
|
verify(bonCommandeRepository).findCommandesParPeriode(dateDebut, dateFin);
|
|
}
|
|
}
|
|
|
|
@Nested
|
|
@DisplayName("⭐ Évaluation de la Qualité des Fournisseurs")
|
|
class QualiteFournisseurTests {
|
|
|
|
@Test
|
|
@DisplayName("Calculer qualité fournisseurs")
|
|
void testCalculerQualiteFournisseurs() {
|
|
// Arrange
|
|
Fournisseur fournisseur = new Fournisseur();
|
|
fournisseur.setId(fournisseurId);
|
|
fournisseur.setNom("Fournisseur Test");
|
|
fournisseur.setEmail("test@fournisseur.com");
|
|
fournisseur.setTelephone("0123456789");
|
|
fournisseur.setAdresse("123 Rue Test");
|
|
fournisseur.setActif(true);
|
|
|
|
List<Fournisseur> fournisseurs = Arrays.asList(fournisseur);
|
|
List<BonCommande> commandesEnCours = Collections.emptyList();
|
|
|
|
when(fournisseurRepository.listAll()).thenReturn(fournisseurs);
|
|
when(bonCommandeRepository.findByFournisseurAndStatut(
|
|
fournisseurId, StatutBonCommande.ENVOYEE))
|
|
.thenReturn(commandesEnCours);
|
|
|
|
// Act
|
|
Map<String, Object> result = statisticsService.calculerQualiteFournisseurs();
|
|
|
|
// Assert
|
|
assertNotNull(result);
|
|
assertTrue(result.containsKey("qualiteParFournisseur"));
|
|
assertTrue(result.containsKey("meilleursFournisseurs"));
|
|
assertTrue(result.containsKey("fournisseursASurveiller"));
|
|
verify(fournisseurRepository).listAll();
|
|
}
|
|
}
|
|
|
|
@Nested
|
|
@DisplayName("📈 Calculs de Tendances")
|
|
class TendancesTests {
|
|
|
|
@Test
|
|
@DisplayName("Calculer tendances avec granularité mensuelle")
|
|
void testCalculerTendancesMensuel() {
|
|
// Arrange
|
|
LocalDate dateDebut = LocalDate.now().minusMonths(3);
|
|
LocalDate dateFin = LocalDate.now();
|
|
List<Chantier> chantiers = Arrays.asList(testChantier);
|
|
List<BonCommande> commandes = Arrays.asList(testCommande);
|
|
|
|
when(chantierRepository.findChantiersParPeriode(dateDebut, dateFin)).thenReturn(chantiers);
|
|
when(bonCommandeRepository.findCommandesParPeriode(dateDebut, dateFin)).thenReturn(commandes);
|
|
|
|
// Act
|
|
Map<String, Object> result =
|
|
statisticsService.calculerTendances(dateDebut, dateFin, "MENSUEL");
|
|
|
|
// Assert
|
|
assertNotNull(result);
|
|
assertTrue(result.containsKey("chantiers"));
|
|
assertTrue(result.containsKey("achats"));
|
|
verify(chantierRepository).findChantiersParPeriode(dateDebut, dateFin);
|
|
verify(bonCommandeRepository).findCommandesParPeriode(dateDebut, dateFin);
|
|
}
|
|
}
|
|
}
|