get(ValidatorInterface::class); $this->validator = $validator; /** @var EntityManagerInterface $em */ $em = $container->get(EntityManagerInterface::class); $this->em = $em; } protected function tearDown(): void { // Liberation explicite des handles pour eviter les fuites inter-tests // (pattern recommande par Symfony lorsque l'on capture le container). $this->em->clear(); parent::tearDown(); } public function testValidSitePassesValidation(): void { $site = $this->makeSite(); $violations = $this->validator->validate($site); self::assertCount(0, $violations, (string) $violations); } public function testValidSiteWithComplementPassesValidation(): void { $site = $this->makeSite(complement: 'Batiment C'); $violations = $this->validator->validate($site); self::assertCount(0, $violations, (string) $violations); } #[DataProvider('invalidColorProvider')] public function testColorMustBeHexRrggbb(string $color): void { $site = $this->makeSite(color: $color); $violations = $this->validator->validate($site); self::assertGreaterThan(0, $violations->count(), sprintf('La couleur "%s" devrait etre rejetee.', $color)); } /** * @return iterable */ public static function invalidColorProvider(): iterable { yield 'nom CSS' => ['red']; yield 'hex court' => ['#FFF']; yield 'hex sans diese' => ['FFFFFF']; yield 'rgb()' => ['rgb(255, 0, 0)']; yield 'hex trop long' => ['#1234567']; yield 'caractere non hex' => ['#12345G']; yield 'vide' => ['']; } #[DataProvider('validColorProvider')] public function testValidColorsAreAccepted(string $color): void { $site = $this->makeSite(color: $color); $violations = $this->validator->validate($site); self::assertCount(0, $violations, sprintf('La couleur "%s" devrait etre acceptee.', $color)); } /** * @return iterable */ public static function validColorProvider(): iterable { yield 'majuscules' => ['#ABCDEF']; yield 'minuscules' => ['#abcdef']; yield 'mixte' => ['#0a1B2c']; yield 'noir' => ['#000000']; yield 'blanc' => ['#FFFFFF']; } #[DataProvider('invalidPostalCodeProvider')] public function testPostalCodeMustMatchFrFormat(string $postalCode): void { $site = $this->makeSite(postalCode: $postalCode); $violations = $this->validator->validate($site); self::assertGreaterThan(0, $violations->count(), sprintf('Le CP "%s" devrait etre rejete.', $postalCode)); } /** * @return iterable */ public static function invalidPostalCodeProvider(): iterable { yield 'trop court' => ['1234']; yield 'trop long' => ['123456']; yield 'alphanumerique' => ['8610A']; yield 'avec tiret' => ['86-100']; yield 'vide' => ['']; yield 'avec espace' => ['86 100']; } #[DataProvider('validPostalCodeProvider')] public function testValidPostalCodesAreAccepted(string $postalCode): void { $site = $this->makeSite(postalCode: $postalCode); $violations = $this->validator->validate($site); self::assertCount(0, $violations, (string) $violations); } /** * @return iterable */ public static function validPostalCodeProvider(): iterable { yield 'metropole' => ['86100']; yield 'paris' => ['75001']; yield 'dom' => ['97100']; yield 'corse' => ['20000']; } public function testBlankNameIsRejected(): void { $site = $this->makeSite(name: ''); $violations = $this->validator->validate($site); self::assertGreaterThan(0, $violations->count()); } public function testBlankStreetIsRejected(): void { $site = $this->makeSite(street: ''); $violations = $this->validator->validate($site); self::assertGreaterThan(0, $violations->count()); } public function testBlankCityIsRejected(): void { $site = $this->makeSite(city: ''); $violations = $this->validator->validate($site); self::assertGreaterThan(0, $violations->count()); } public function testNameLongerThan100CharsIsRejected(): void { $site = $this->makeSite(name: str_repeat('a', 101)); $violations = $this->validator->validate($site); self::assertGreaterThan(0, $violations->count()); } public function testCityLongerThan100CharsIsRejected(): void { $site = $this->makeSite(city: str_repeat('a', 101)); $violations = $this->validator->validate($site); self::assertGreaterThan(0, $violations->count()); } public function testStreetLongerThan255CharsIsRejected(): void { $site = $this->makeSite(street: str_repeat('a', 256)); $violations = $this->validator->validate($site); self::assertGreaterThan(0, $violations->count()); } public function testComplementLongerThan255CharsIsRejected(): void { $site = $this->makeSite(complement: str_repeat('a', 256)); $violations = $this->validator->validate($site); self::assertGreaterThan(0, $violations->count()); } /** * Verifie que la contrainte UniqueEntity(name) est effectivement appliquee * par le validator Symfony (via le validateur Doctrine sous-jacent). * * Le test est auto-suffisant : il persiste lui-meme un site porteur d'un * nom unique, puis tente de valider un second Site avec le meme nom. Le * site cree est supprime en fin de test pour ne pas laisser de trace * inter-tests (pattern transactionnel non utilise ici car un seul test * persiste, un cleanup explicite suffit). */ public function testDuplicateNameIsRejected(): void { $name = 'Test-Duplicate-'.uniqid('', true); $original = $this->makeSite(name: $name); $this->em->persist($original); $this->em->flush(); try { $duplicate = $this->makeSite(name: $name, city: 'Autre'); $violations = $this->validator->validate($duplicate); self::assertGreaterThan(0, $violations->count(), 'Un site homonyme doit lever au moins une violation.'); // Assertion precise : on veut s'assurer que la violation levee // est bien UniqueEntity sur `name`, pas une autre contrainte // qui passerait par hasard (matching de message trop laxe). $found = false; foreach ($violations as $violation) { if (UniqueEntity::NOT_UNIQUE_ERROR === $violation->getCode() && 'name' === $violation->getPropertyPath()) { $found = true; break; } } self::assertTrue($found, 'Violation UniqueEntity(name) attendue (code NOT_UNIQUE_ERROR sur property `name`).'); } finally { $this->em->remove($original); $this->em->flush(); } } /** * Helper : construit un Site valide avec un nom unique, sur lequel on * peut superposer un seul champ invalide pour tester une contrainte. */ private function makeSite( ?string $name = null, string $street = '1 rue Test', ?string $complement = null, string $postalCode = '12345', string $city = 'Poitiers', string $color = '#000000', ): Site { return new Site( $name ?? 'Test-'.uniqid('', true), $street, $complement, $postalCode, $city, $color, ); } }