From df217bc9239d867d642dce1bd75d4a7e067245fc Mon Sep 17 00:00:00 2001 From: Youssef Date: Thu, 30 Jul 2026 10:12:21 +0200 Subject: [PATCH] fix: update equipment size matching pattern recognition and add integration tests - Prevent false positive regex matches by enforcing strict digit boundaries in Dotation.isSizeMatch - Fix issue where 11/12ANS (Taille 152cm) mapped to 5/6ANS (Taille 116cm) due to 'Taille 1' tag matching '116cm' - Add comprehensive integration test (DotationIntegrationTest) covering all Junior/Adult sizes, shoe sizes, and format variations --- .../com/astalange/core/entity/Dotation.java | 114 ++++++---- .../astalange/core/DotationSizeMatchTest.java | 56 +++++ .../core/service/DotationIntegrationTest.java | 214 ++++++++++++++++++ 3 files changed, 334 insertions(+), 50 deletions(-) create mode 100644 as-talange-core/src/test/java/com/astalange/core/DotationSizeMatchTest.java create mode 100644 as-talange-core/src/test/java/com/astalange/core/service/DotationIntegrationTest.java diff --git a/as-talange-core/src/main/java/com/astalange/core/entity/Dotation.java b/as-talange-core/src/main/java/com/astalange/core/entity/Dotation.java index 54db44b..da8e07b 100644 --- a/as-talange-core/src/main/java/com/astalange/core/entity/Dotation.java +++ b/as-talange-core/src/main/java/com/astalange/core/entity/Dotation.java @@ -73,69 +73,83 @@ public class Dotation { if (opt.equalsIgnoreCase(adh)) return true; - String optLower = opt.toLowerCase(); - String adhLower = adh.toLowerCase(); + String optNorm = opt.toLowerCase().replaceAll("\\s+", " "); + String adhNorm = adh.toLowerCase().replaceAll("\\s+", " "); - // Extract height numbers (116, 128, 140, 152, 164) - java.util.regex.Pattern numPattern = java.util.regex.Pattern.compile("\\b(116|128|140|152|164)\\b"); - java.util.regex.Matcher adhNumMatcher = numPattern.matcher(adhLower); - java.util.regex.Matcher optNumMatcher = numPattern.matcher(optLower); - if (adhNumMatcher.find()) { - String num = adhNumMatcher.group(1); - if (optLower.contains(num)) { - return true; - } - } else if (optNumMatcher.find()) { - String num = optNumMatcher.group(1); - if (adhLower.contains(num)) { - return true; - } - } + if (optNorm.equals(adhNorm)) return true; - // Extract age patterns (5/6, 7/8, 9/10, 11/12, 13/14) or (5-6, 7-8, 9-10, 11-12, 13-14) - java.util.regex.Pattern agePattern = java.util.regex.Pattern.compile("\\b(5[/\\-]6|7[/\\-]8|9[/\\-]10|11[/\\-]12|13[/\\-]14)\\b"); - java.util.regex.Matcher adhAge = agePattern.matcher(adhLower); - if (adhAge.find()) { - String ageKey = adhAge.group(1).replace("-", "/"); + // 1. Age bracket matching: 5/6, 7/8, 9/10, 11/12, 13/14, 15/16 + java.util.regex.Pattern agePattern = java.util.regex.Pattern.compile("(?= 2 && adhLower.contains(optLower)) return true; - if (adh.length() >= 2 && optLower.contains(adhLower)) return true; + // 5. Letter size matching: XXL, XL, L, M, S, XS + java.util.regex.Pattern letterPattern = java.util.regex.Pattern.compile("(?= 2 && adhNorm.contains(optNorm)) return true; + if (adhNorm.length() >= 2 && optNorm.contains(adhNorm)) return true; + } return false; } diff --git a/as-talange-core/src/test/java/com/astalange/core/DotationSizeMatchTest.java b/as-talange-core/src/test/java/com/astalange/core/DotationSizeMatchTest.java new file mode 100644 index 0000000..cba6af6 --- /dev/null +++ b/as-talange-core/src/test/java/com/astalange/core/DotationSizeMatchTest.java @@ -0,0 +1,56 @@ +package com.astalange.core; + +import com.astalange.core.entity.Dotation; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class DotationSizeMatchTest { + + @Test + public void testAdherent60SizeMatchBugFix() { + String adherentTaille = "11/12ANS (Taille 152cm)"; + String adherentPointure = "35/38 (Taille 2)"; + + // Equipment options for Junior Maillot / Shorts / Tracksuits + String opt56 = "5/6ANS (Taille 116cm)"; + String opt78 = "7/8ANS (Taille 128cm)"; + String opt910 = "9/10ANS (Taille 140cm)"; + String opt1112 = "11/12ANS (Taille 152cm)"; + String opt1314 = "13/14ANS (Taille 164cm)"; + + assertFalse(Dotation.isSizeMatch(opt56, adherentTaille), "5/6ANS should NOT match 11/12ANS"); + assertFalse(Dotation.isSizeMatch(opt78, adherentTaille), "7/8ANS should NOT match 11/12ANS"); + assertFalse(Dotation.isSizeMatch(opt910, adherentTaille), "9/10ANS should NOT match 11/12ANS"); + assertTrue(Dotation.isSizeMatch(opt1112, adherentTaille), "11/12ANS MUST match 11/12ANS"); + assertFalse(Dotation.isSizeMatch(opt1314, adherentTaille), "13/14ANS should NOT match 11/12ANS"); + + // Sock sizes + String sock2730 = "27/30 (Taille 0)"; + String sock3134 = "31/34 (Taille 1)"; + String sock3538 = "35/38 (Taille 2)"; + String sock3942 = "39/42 (Taille 3)"; + + assertFalse(Dotation.isSizeMatch(sock2730, adherentPointure), "27/30 should NOT match 35/38"); + assertFalse(Dotation.isSizeMatch(sock3134, adherentPointure), "31/34 should NOT match 35/38"); + assertTrue(Dotation.isSizeMatch(sock3538, adherentPointure), "35/38 MUST match 35/38"); + assertFalse(Dotation.isSizeMatch(sock3942, adherentPointure), "39/42 should NOT match 35/38"); + + // Ensure sock sizes do not match clothing size 152cm due to "Taille 1" + assertFalse(Dotation.isSizeMatch(sock3134, adherentTaille), "Sock 31/34 (Taille 1) should NOT match clothing 152cm"); + } + + @Test + public void testLetterSizes() { + assertFalse(Dotation.isSizeMatch("S", "XL")); + assertFalse(Dotation.isSizeMatch("M", "XL")); + assertTrue(Dotation.isSizeMatch("XL", "XL")); + assertTrue(Dotation.isSizeMatch("xxl", "XXL")); + } + + @Test + public void testSpaceVariations() { + assertTrue(Dotation.isSizeMatch("5/6 ANS (Taille 116cm)", "5/6ANS")); + assertTrue(Dotation.isSizeMatch("11/12 ANS (Taille 152cm)", "11/12ANS (Taille 152 cm)")); + } +} diff --git a/as-talange-core/src/test/java/com/astalange/core/service/DotationIntegrationTest.java b/as-talange-core/src/test/java/com/astalange/core/service/DotationIntegrationTest.java new file mode 100644 index 0000000..6914d9a --- /dev/null +++ b/as-talange-core/src/test/java/com/astalange/core/service/DotationIntegrationTest.java @@ -0,0 +1,214 @@ +package com.astalange.core.service; + +import com.astalange.core.entity.*; +import com.astalange.core.repository.CategorieRepository; +import com.astalange.core.repository.DotationRepository; +import com.astalange.core.repository.EquipementRepository; +import com.astalange.core.repository.SaisonRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +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.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +@ExtendWith(MockitoExtension.class) +public class DotationIntegrationTest { + + @Mock + private CategorieRepository categorieRepository; + + @Mock + private SaisonRepository saisonRepository; + + @Mock + private EquipementRepository equipementRepository; + + @Mock + private com.astalange.core.repository.LicenceRepository licenceRepository; + + @InjectMocks + private CategorieService categorieService; + + private Equipement maillotJunior; + private Equipement chaussettesJunior; + private Equipement maillotAdulte; + private Equipement chaussettesAdulte; + private Categorie catJunior; + private Categorie catAdulte; + + @BeforeEach + public void setUp() { + // 1. Junior Equipment + maillotJunior = new Equipement(); + maillotJunior.setId(1L); + maillotJunior.setNom("Maillot de match Puma (Junior)"); + maillotJunior.setTypePublic("TOUS"); + maillotJunior.setTaillesDisponibles("5/6ANS (Taille 116cm),7/8ANS (Taille 128cm),9/10ANS (Taille 140cm),11/12ANS (Taille 152cm),13/14ANS (Taille 164cm),15/16ANS (Taille 176cm)"); + + chaussettesJunior = new Equipement(); + chaussettesJunior.setId(2L); + chaussettesJunior.setNom("Chaussettes puma (Junior)"); + chaussettesJunior.setTypePublic("TOUS"); + chaussettesJunior.setTaillesDisponibles("27/30 (Taille 0),31/34 (Taille 1),35/38 (Taille 2),39/42 (Taille 3)"); + + catJunior = new Categorie(); + catJunior.setId(10L); + catJunior.setNom("U11"); + + CategorieEquipement ce1 = new CategorieEquipement(); + ce1.setCategorie(catJunior); + ce1.setEquipement(maillotJunior); + ce1.setObligatoire(true); + + CategorieEquipement ce2 = new CategorieEquipement(); + ce2.setCategorie(catJunior); + ce2.setEquipement(chaussettesJunior); + ce2.setObligatoire(true); + + catJunior.setCategorieEquipements(List.of(ce1, ce2)); + + // 2. Adult Equipment + maillotAdulte = new Equipement(); + maillotAdulte.setId(3L); + maillotAdulte.setNom("Maillot de match Puma (Adulte)"); + maillotAdulte.setTypePublic("TOUS"); + maillotAdulte.setTaillesDisponibles("XS,S,M,L,XL,XXL"); + + chaussettesAdulte = new Equipement(); + chaussettesAdulte.setId(4L); + chaussettesAdulte.setNom("Chaussettes puma (Adulte)"); + chaussettesAdulte.setTypePublic("TOUS"); + chaussettesAdulte.setTaillesDisponibles("35/38 (Taille 2),39/42 (Taille 3),43/46 (Taille 4)"); + + catAdulte = new Categorie(); + catAdulte.setId(20L); + catAdulte.setNom("Seniors"); + + CategorieEquipement ce3 = new CategorieEquipement(); + ce3.setCategorie(catAdulte); + ce3.setEquipement(maillotAdulte); + ce3.setObligatoire(true); + + CategorieEquipement ce4 = new CategorieEquipement(); + ce4.setCategorie(catAdulte); + ce4.setEquipement(chaussettesAdulte); + ce4.setObligatoire(true); + + catAdulte.setCategorieEquipements(List.of(ce3, ce4)); + } + + private Licence createLicence(Categorie cat, String tailleVetement, String pointure) { + Adherent adherent = new Adherent(); + adherent.setTypeMaillot("JOUEUR"); + adherent.setTailleVetement(tailleVetement); + adherent.setPointure(pointure); + + Licence licence = new Licence(); + licence.setId(100L); + licence.setAdherent(adherent); + licence.setCategorie(cat); + licence.setDotations(new ArrayList<>()); + return licence; + } + + @Test + @DisplayName("Junior 11/12ANS (Bug initial Adhérent 60) -> Doit reporter 11/12ANS et 35/38") + public void testAdherent60Synchronization() { + Licence licence = createLicence(catJunior, "11/12ANS (Taille 152cm)", "35/38 (Taille 2)"); + + categorieService.syncDotationsForLicence(licence); + + List dotations = licence.getDotations(); + assertEquals(2, dotations.size()); + Dotation dMaillot = dotations.stream().filter(d -> d.getEquipement().getId().equals(1L)).findFirst().orElseThrow(); + Dotation dChaussettes = dotations.stream().filter(d -> d.getEquipement().getId().equals(2L)).findFirst().orElseThrow(); + + assertEquals("11/12ANS (Taille 152cm)", dMaillot.getTaille(), "Le maillot doit être 11/12ANS et non 5/6ANS"); + assertEquals("35/38 (Taille 2)", dChaussettes.getTaille(), "Les chaussettes doivent être 35/38 et non 31/34"); + } + + @Test + @DisplayName("Junior - Test de toutes les tailles (5/6, 7/8, 9/10, 11/12, 13/14, 15/16)") + public void testAllJuniorSizes() { + String[][] cases = { + {"5/6ANS (Taille 116cm)", "27/30 (Taille 0)", "5/6ANS (Taille 116cm)", "27/30 (Taille 0)"}, + {"7/8ANS (Taille 128cm)", "31/34 (Taille 1)", "7/8ANS (Taille 128cm)", "31/34 (Taille 1)"}, + {"9/10ANS (Taille 140cm)", "31/34 (Taille 1)", "9/10ANS (Taille 140cm)", "31/34 (Taille 1)"}, + {"11/12ANS (Taille 152cm)", "35/38 (Taille 2)", "11/12ANS (Taille 152cm)", "35/38 (Taille 2)"}, + {"13/14ANS (Taille 164cm)", "35/38 (Taille 2)", "13/14ANS (Taille 164cm)", "35/38 (Taille 2)"}, + {"15/16ANS (Taille 176cm)", "39/42 (Taille 3)", "15/16ANS (Taille 176cm)", "39/42 (Taille 3)"} + }; + + for (String[] testCase : cases) { + String tv = testCase[0]; + String pt = testCase[1]; + String expectedMaillot = testCase[2]; + String expectedChaussettes = testCase[3]; + + Licence licence = createLicence(catJunior, tv, pt); + categorieService.syncDotationsForLicence(licence); + + List dotations = licence.getDotations(); + Dotation dMaillot = dotations.stream().filter(d -> d.getEquipement().getId().equals(1L)).findFirst().orElseThrow(); + Dotation dChaussettes = dotations.stream().filter(d -> d.getEquipement().getId().equals(2L)).findFirst().orElseThrow(); + + assertEquals(expectedMaillot, dMaillot.getTaille(), "Erreur pour la taille vêtement: " + tv); + assertEquals(expectedChaussettes, dChaussettes.getTaille(), "Erreur pour la pointure: " + pt); + } + } + + @Test + @DisplayName("Adulte - Test de toutes les tailles (XS, S, M, L, XL, XXL)") + public void testAllAdultSizes() { + String[][] cases = { + {"XS", "35/38 (Taille 2)", "XS", "35/38 (Taille 2)"}, + {"S", "39/42 (Taille 3)", "S", "39/42 (Taille 3)"}, + {"M", "39/42 (Taille 3)", "M", "39/42 (Taille 3)"}, + {"L", "43/46 (Taille 4)", "L", "43/46 (Taille 4)"}, + {"XL", "43/46 (Taille 4)", "XL", "43/46 (Taille 4)"}, + {"XXL", "43/46 (Taille 4)", "XXL", "43/46 (Taille 4)"} + }; + + for (String[] testCase : cases) { + String tv = testCase[0]; + String pt = testCase[1]; + String expectedMaillot = testCase[2]; + String expectedChaussettes = testCase[3]; + + Licence licence = createLicence(catAdulte, tv, pt); + categorieService.syncDotationsForLicence(licence); + + List dotations = licence.getDotations(); + Dotation dMaillot = dotations.stream().filter(d -> d.getEquipement().getId().equals(3L)).findFirst().orElseThrow(); + Dotation dChaussettes = dotations.stream().filter(d -> d.getEquipement().getId().equals(4L)).findFirst().orElseThrow(); + + assertEquals(expectedMaillot, dMaillot.getTaille(), "Erreur pour la taille adulte vêtement: " + tv); + assertEquals(expectedChaussettes, dChaussettes.getTaille(), "Erreur pour la pointure adulte: " + pt); + } + } + + @Test + @DisplayName("Formats alternatifs (Espaces, sans mention de taille)") + public void testAlternativeFormats() { + Licence licence1 = createLicence(catJunior, "11/12 ANS (Taille 152 cm)", "35/38 (Taille 2)"); + categorieService.syncDotationsForLicence(licence1); + + Dotation dMaillot1 = licence1.getDotations().stream().filter(d -> d.getEquipement().getId().equals(1L)).findFirst().orElseThrow(); + assertEquals("11/12ANS (Taille 152cm)", dMaillot1.getTaille()); + + Licence licence2 = createLicence(catJunior, "11/12ANS", "35/38"); + categorieService.syncDotationsForLicence(licence2); + + Dotation dMaillot2 = licence2.getDotations().stream().filter(d -> d.getEquipement().getId().equals(1L)).findFirst().orElseThrow(); + Dotation dChaussettes2 = licence2.getDotations().stream().filter(d -> d.getEquipement().getId().equals(2L)).findFirst().orElseThrow(); + assertEquals("11/12ANS (Taille 152cm)", dMaillot2.getTaille()); + assertEquals("35/38 (Taille 2)", dChaussettes2.getTaille()); + } +}