diff --git a/src/Controller/AttestationController.php b/src/Controller/AttestationController.php index 55a0228..ec9b19c 100644 --- a/src/Controller/AttestationController.php +++ b/src/Controller/AttestationController.php @@ -104,12 +104,7 @@ class AttestationController extends AbstractController [$signature, $jsonPayload] = $parts; $expectedSignature = hash_hmac('sha256', $jsonPayload, $this->appSecret); - - if (!hash_equals($expectedSignature, $signature)) { - return null; - } - - $data = json_decode($jsonPayload, true); + $data = hash_equals($expectedSignature, $signature) ? json_decode($jsonPayload, true) : null; return $data ? ['data' => $data, 'signatureHash' => $expectedSignature] : null; } diff --git a/tests/Controller/AccountControllerTest.php b/tests/Controller/AccountControllerTest.php index aa2d339..ac33a0b 100644 --- a/tests/Controller/AccountControllerTest.php +++ b/tests/Controller/AccountControllerTest.php @@ -2226,6 +2226,501 @@ class AccountControllerTest extends WebTestCase self::assertResponseIsSuccessful(); } + // ---- downloadTicket tests ---- + + public function testDownloadTicketSuccess(): void + { + $client = static::createClient(); + $em = static::getContainer()->get(EntityManagerInterface::class); + $user = $this->createUser(['ROLE_ORGANIZER'], true); + + $event = $this->createEvent($em, $user); + $category = $this->createCategory($em, $event); + $billet = $this->createBillet($em, $category); + + $order = new \App\Entity\BilletBuyer(); + $order->setEvent($event); + $order->setFirstName('Jean'); + $order->setLastName('Dupont'); + $order->setEmail('jean@test.fr'); + $order->setOrderNumber('2026-03-21-'.random_int(100, 999)); + $order->setTotalHT(1000); + $order->setStatus(\App\Entity\BilletBuyer::STATUS_PAID); + + $item = new \App\Entity\BilletBuyerItem(); + $item->setBillet($billet); + $item->setBilletName('Test'); + $item->setQuantity(1); + $item->setUnitPriceHT(1000); + $order->addItem($item); + $em->persist($order); + $em->flush(); + + $ticket = new \App\Entity\BilletOrder(); + $ticket->setBilletBuyer($order); + $ticket->setBillet($billet); + $ticket->setBilletName('Test'); + $ticket->setUnitPriceHT(1000); + $em->persist($ticket); + $em->flush(); + + $billetOrderService = $this->createMock(\App\Service\BilletOrderService::class); + $billetOrderService->expects(self::once())->method('generatePdf')->willReturn('%PDF-1.4 fake'); + static::getContainer()->set(\App\Service\BilletOrderService::class, $billetOrderService); + + $client->loginUser($user); + $client->request('GET', '/mon-compte/evenement/'.$event->getId().'/ticket/'.$ticket->getId().'/telecharger'); + + self::assertResponseIsSuccessful(); + self::assertResponseHeaderSame('Content-Type', 'application/pdf'); + } + + public function testDownloadTicketDeniedForOtherUser(): void + { + $client = static::createClient(); + $em = static::getContainer()->get(EntityManagerInterface::class); + $owner = $this->createUser(['ROLE_ORGANIZER'], true); + $other = $this->createUser(['ROLE_ORGANIZER'], true); + + $event = $this->createEvent($em, $owner); + $category = $this->createCategory($em, $event); + $billet = $this->createBillet($em, $category); + + $order = new \App\Entity\BilletBuyer(); + $order->setEvent($event); + $order->setFirstName('Jean'); + $order->setLastName('Dupont'); + $order->setEmail('jean@test.fr'); + $order->setOrderNumber('2026-03-21-'.random_int(100, 999)); + $order->setTotalHT(1000); + $order->setStatus(\App\Entity\BilletBuyer::STATUS_PAID); + + $item = new \App\Entity\BilletBuyerItem(); + $item->setBillet($billet); + $item->setBilletName('Test'); + $item->setQuantity(1); + $item->setUnitPriceHT(1000); + $order->addItem($item); + $em->persist($order); + $em->flush(); + + $ticket = new \App\Entity\BilletOrder(); + $ticket->setBilletBuyer($order); + $ticket->setBillet($billet); + $ticket->setBilletName('Test'); + $ticket->setUnitPriceHT(1000); + $em->persist($ticket); + $em->flush(); + + $client->loginUser($other); + $client->request('GET', '/mon-compte/evenement/'.$event->getId().'/ticket/'.$ticket->getId().'/telecharger'); + + self::assertResponseStatusCodeSame(403); + } + + public function testDownloadTicketNotFound(): void + { + $client = static::createClient(); + $em = static::getContainer()->get(EntityManagerInterface::class); + $user = $this->createUser(['ROLE_ORGANIZER'], true); + + $event = $this->createEvent($em, $user); + + $client->loginUser($user); + $client->request('GET', '/mon-compte/evenement/'.$event->getId().'/ticket/999999/telecharger'); + + self::assertResponseStatusCodeSame(404); + } + + // ---- resendTicket tests ---- + + public function testResendTicketSuccess(): void + { + $client = static::createClient(); + $em = static::getContainer()->get(EntityManagerInterface::class); + $user = $this->createUser(['ROLE_ORGANIZER'], true); + + $event = $this->createEvent($em, $user); + $category = $this->createCategory($em, $event); + $billet = $this->createBillet($em, $category); + + $order = new \App\Entity\BilletBuyer(); + $order->setEvent($event); + $order->setFirstName('Jean'); + $order->setLastName('Dupont'); + $order->setEmail('jean@test.fr'); + $order->setOrderNumber('2026-03-21-'.random_int(100, 999)); + $order->setTotalHT(1000); + $order->setStatus(\App\Entity\BilletBuyer::STATUS_PAID); + + $item = new \App\Entity\BilletBuyerItem(); + $item->setBillet($billet); + $item->setBilletName('Test'); + $item->setQuantity(1); + $item->setUnitPriceHT(1000); + $order->addItem($item); + $em->persist($order); + $em->flush(); + + $ticket = new \App\Entity\BilletOrder(); + $ticket->setBilletBuyer($order); + $ticket->setBillet($billet); + $ticket->setBilletName('Test'); + $ticket->setUnitPriceHT(1000); + $em->persist($ticket); + $em->flush(); + + $billetOrderService = $this->createMock(\App\Service\BilletOrderService::class); + $billetOrderService->expects(self::once())->method('generateAndSendTickets'); + static::getContainer()->set(\App\Service\BilletOrderService::class, $billetOrderService); + + $client->loginUser($user); + $client->request('POST', '/mon-compte/evenement/'.$event->getId().'/ticket/'.$ticket->getId().'/renvoyer'); + + self::assertResponseRedirects('/mon-compte/evenement/'.$event->getId().'/modifier?tab=tickets'); + } + + public function testResendTicketDeniedForOtherUser(): void + { + $client = static::createClient(); + $em = static::getContainer()->get(EntityManagerInterface::class); + $owner = $this->createUser(['ROLE_ORGANIZER'], true); + $other = $this->createUser(['ROLE_ORGANIZER'], true); + + $event = $this->createEvent($em, $owner); + $category = $this->createCategory($em, $event); + $billet = $this->createBillet($em, $category); + + $order = new \App\Entity\BilletBuyer(); + $order->setEvent($event); + $order->setFirstName('Jean'); + $order->setLastName('Dupont'); + $order->setEmail('jean@test.fr'); + $order->setOrderNumber('2026-03-21-'.random_int(100, 999)); + $order->setTotalHT(1000); + $order->setStatus(\App\Entity\BilletBuyer::STATUS_PAID); + + $item = new \App\Entity\BilletBuyerItem(); + $item->setBillet($billet); + $item->setBilletName('Test'); + $item->setQuantity(1); + $item->setUnitPriceHT(1000); + $order->addItem($item); + $em->persist($order); + $em->flush(); + + $ticket = new \App\Entity\BilletOrder(); + $ticket->setBilletBuyer($order); + $ticket->setBillet($billet); + $ticket->setBilletName('Test'); + $ticket->setUnitPriceHT(1000); + $em->persist($ticket); + $em->flush(); + + $client->loginUser($other); + $client->request('POST', '/mon-compte/evenement/'.$event->getId().'/ticket/'.$ticket->getId().'/renvoyer'); + + self::assertResponseStatusCodeSame(403); + } + + public function testResendTicketNotFound(): void + { + $client = static::createClient(); + $em = static::getContainer()->get(EntityManagerInterface::class); + $user = $this->createUser(['ROLE_ORGANIZER'], true); + + $event = $this->createEvent($em, $user); + + $client->loginUser($user); + $client->request('POST', '/mon-compte/evenement/'.$event->getId().'/ticket/999999/renvoyer'); + + self::assertResponseStatusCodeSame(404); + } + + // ---- cancelTicket tests ---- + + public function testCancelTicketSuccess(): void + { + $client = static::createClient(); + $em = static::getContainer()->get(EntityManagerInterface::class); + $user = $this->createUser(['ROLE_ORGANIZER'], true); + + $event = $this->createEvent($em, $user); + $category = $this->createCategory($em, $event); + $billet = $this->createBillet($em, $category); + + $order = new \App\Entity\BilletBuyer(); + $order->setEvent($event); + $order->setFirstName('Jean'); + $order->setLastName('Dupont'); + $order->setEmail('jean@test.fr'); + $order->setOrderNumber('2026-03-21-'.random_int(100, 999)); + $order->setTotalHT(1000); + $order->setStatus(\App\Entity\BilletBuyer::STATUS_PAID); + + $item = new \App\Entity\BilletBuyerItem(); + $item->setBillet($billet); + $item->setBilletName('Test'); + $item->setQuantity(1); + $item->setUnitPriceHT(1000); + $order->addItem($item); + $em->persist($order); + $em->flush(); + + $ticket = new \App\Entity\BilletOrder(); + $ticket->setBilletBuyer($order); + $ticket->setBillet($billet); + $ticket->setBilletName('Test'); + $ticket->setUnitPriceHT(1000); + $em->persist($ticket); + $em->flush(); + + $client->loginUser($user); + $client->request('POST', '/mon-compte/evenement/'.$event->getId().'/ticket/'.$ticket->getId().'/annuler'); + + self::assertResponseRedirects('/mon-compte/evenement/'.$event->getId().'/modifier?tab=tickets'); + + $em->refresh($ticket); + self::assertSame(\App\Entity\BilletOrder::STATE_INVALID, $ticket->getState()); + } + + public function testCancelTicketDeniedForOtherUser(): void + { + $client = static::createClient(); + $em = static::getContainer()->get(EntityManagerInterface::class); + $owner = $this->createUser(['ROLE_ORGANIZER'], true); + $other = $this->createUser(['ROLE_ORGANIZER'], true); + + $event = $this->createEvent($em, $owner); + $category = $this->createCategory($em, $event); + $billet = $this->createBillet($em, $category); + + $order = new \App\Entity\BilletBuyer(); + $order->setEvent($event); + $order->setFirstName('Jean'); + $order->setLastName('Dupont'); + $order->setEmail('jean@test.fr'); + $order->setOrderNumber('2026-03-21-'.random_int(100, 999)); + $order->setTotalHT(1000); + $order->setStatus(\App\Entity\BilletBuyer::STATUS_PAID); + + $item = new \App\Entity\BilletBuyerItem(); + $item->setBillet($billet); + $item->setBilletName('Test'); + $item->setQuantity(1); + $item->setUnitPriceHT(1000); + $order->addItem($item); + $em->persist($order); + $em->flush(); + + $ticket = new \App\Entity\BilletOrder(); + $ticket->setBilletBuyer($order); + $ticket->setBillet($billet); + $ticket->setBilletName('Test'); + $ticket->setUnitPriceHT(1000); + $em->persist($ticket); + $em->flush(); + + $client->loginUser($other); + $client->request('POST', '/mon-compte/evenement/'.$event->getId().'/ticket/'.$ticket->getId().'/annuler'); + + self::assertResponseStatusCodeSame(403); + } + + public function testCancelTicketNotFound(): void + { + $client = static::createClient(); + $em = static::getContainer()->get(EntityManagerInterface::class); + $user = $this->createUser(['ROLE_ORGANIZER'], true); + + $event = $this->createEvent($em, $user); + + $client->loginUser($user); + $client->request('POST', '/mon-compte/evenement/'.$event->getId().'/ticket/999999/annuler'); + + self::assertResponseStatusCodeSame(404); + } + + // ---- createAccreditation tests ---- + + public function testCreateAccreditationStaffSuccess(): void + { + $client = static::createClient(); + $em = static::getContainer()->get(EntityManagerInterface::class); + $user = $this->createUser(['ROLE_ORGANIZER'], true); + + $event = $this->createEvent($em, $user); + $category = $this->createCategory($em, $event); + + $billetOrderService = $this->createMock(\App\Service\BilletOrderService::class); + $billetOrderService->expects(self::once())->method('generateOrderTickets'); + $billetOrderService->expects(self::once())->method('generateAndSendTickets'); + static::getContainer()->set(\App\Service\BilletOrderService::class, $billetOrderService); + + $client->loginUser($user); + $client->request('POST', '/mon-compte/evenement/'.$event->getId().'/accreditation', [ + 'accreditation_type' => 'staff', + 'first_name' => 'Alice', + 'last_name' => 'Martin', + 'email' => 'alice@test.fr', + ]); + + self::assertResponseRedirects('/mon-compte/evenement/'.$event->getId().'/modifier?tab=invitations'); + } + + public function testCreateAccreditationExposantSuccess(): void + { + $client = static::createClient(); + $em = static::getContainer()->get(EntityManagerInterface::class); + $user = $this->createUser(['ROLE_ORGANIZER'], true); + + $event = $this->createEvent($em, $user); + $category = $this->createCategory($em, $event); + + $billetOrderService = $this->createMock(\App\Service\BilletOrderService::class); + $billetOrderService->expects(self::once())->method('generateOrderTickets'); + $billetOrderService->expects(self::once())->method('generateAndSendTickets'); + static::getContainer()->set(\App\Service\BilletOrderService::class, $billetOrderService); + + $client->loginUser($user); + $client->request('POST', '/mon-compte/evenement/'.$event->getId().'/accreditation', [ + 'accreditation_type' => 'exposant', + 'first_name' => 'Bob', + 'last_name' => 'Durand', + 'email' => 'bob@test.fr', + ]); + + self::assertResponseRedirects('/mon-compte/evenement/'.$event->getId().'/modifier?tab=invitations'); + } + + public function testCreateAccreditationEmptyFields(): void + { + $client = static::createClient(); + $em = static::getContainer()->get(EntityManagerInterface::class); + $user = $this->createUser(['ROLE_ORGANIZER'], true); + + $event = $this->createEvent($em, $user); + $category = $this->createCategory($em, $event); + + $client->loginUser($user); + $client->request('POST', '/mon-compte/evenement/'.$event->getId().'/accreditation', [ + 'accreditation_type' => 'staff', + 'first_name' => '', + 'last_name' => '', + 'email' => '', + ]); + + self::assertResponseRedirects('/mon-compte/evenement/'.$event->getId().'/modifier?tab=invitations'); + } + + public function testCreateAccreditationNoCategories(): void + { + $client = static::createClient(); + $em = static::getContainer()->get(EntityManagerInterface::class); + $user = $this->createUser(['ROLE_ORGANIZER'], true); + + $event = $this->createEvent($em, $user); + // No category created + + $client->loginUser($user); + $client->request('POST', '/mon-compte/evenement/'.$event->getId().'/accreditation', [ + 'accreditation_type' => 'staff', + 'first_name' => 'Alice', + 'last_name' => 'Martin', + 'email' => 'alice@test.fr', + ]); + + self::assertResponseRedirects('/mon-compte/evenement/'.$event->getId().'/modifier?tab=invitations'); + } + + public function testCreateAccreditationInvalidTypeDefaultsToStaff(): void + { + $client = static::createClient(); + $em = static::getContainer()->get(EntityManagerInterface::class); + $user = $this->createUser(['ROLE_ORGANIZER'], true); + + $event = $this->createEvent($em, $user); + $category = $this->createCategory($em, $event); + + $billetOrderService = $this->createMock(\App\Service\BilletOrderService::class); + $billetOrderService->expects(self::once())->method('generateOrderTickets'); + $billetOrderService->expects(self::once())->method('generateAndSendTickets'); + static::getContainer()->set(\App\Service\BilletOrderService::class, $billetOrderService); + + $client->loginUser($user); + $client->request('POST', '/mon-compte/evenement/'.$event->getId().'/accreditation', [ + 'accreditation_type' => 'invalid_type', + 'first_name' => 'Alice', + 'last_name' => 'Martin', + 'email' => 'alice@test.fr', + ]); + + self::assertResponseRedirects('/mon-compte/evenement/'.$event->getId().'/modifier?tab=invitations'); + + // Verify the billet was created with 'staff' label (default) + $billets = $em->getRepository(\App\Entity\Billet::class)->findBy(['category' => $category, 'type' => 'staff']); + self::assertNotEmpty($billets); + } + + // ---- eventAttestation tests ---- + + public function testEventAttestationWithCategories(): void + { + $client = static::createClient(); + $em = static::getContainer()->get(EntityManagerInterface::class); + $user = $this->createUser(['ROLE_ORGANIZER'], true); + + $event = $this->createEvent($em, $user); + $category = $this->createCategory($em, $event); + $billet = $this->createBillet($em, $category); + + $client->loginUser($user); + $client->request('POST', '/mon-compte/evenement/'.$event->getId().'/attestation', [ + 'categories' => [$category->getId()], + 'mode' => 'simple', + ]); + + self::assertResponseIsSuccessful(); + self::assertResponseHeaderSame('Content-Type', 'application/pdf'); + } + + public function testEventAttestationWithBillets(): void + { + $client = static::createClient(); + $em = static::getContainer()->get(EntityManagerInterface::class); + $user = $this->createUser(['ROLE_ORGANIZER'], true); + + $event = $this->createEvent($em, $user); + $category = $this->createCategory($em, $event); + $billet = $this->createBillet($em, $category); + + $client->loginUser($user); + $client->request('POST', '/mon-compte/evenement/'.$event->getId().'/attestation', [ + 'billets' => [$billet->getId()], + 'mode' => 'detail', + ]); + + self::assertResponseIsSuccessful(); + self::assertResponseHeaderSame('Content-Type', 'application/pdf'); + } + + public function testEventAttestationEmptySelection(): void + { + $client = static::createClient(); + $em = static::getContainer()->get(EntityManagerInterface::class); + $user = $this->createUser(['ROLE_ORGANIZER'], true); + + $event = $this->createEvent($em, $user); + + $client->loginUser($user); + $client->request('POST', '/mon-compte/evenement/'.$event->getId().'/attestation', [ + 'categories' => [], + 'billets' => [], + ]); + + self::assertResponseRedirects('/mon-compte/evenement/'.$event->getId().'/modifier?tab=attestation'); + } + /** * @param list $roles */ diff --git a/tests/Controller/AdminControllerTest.php b/tests/Controller/AdminControllerTest.php index 1d55847..4995408 100644 --- a/tests/Controller/AdminControllerTest.php +++ b/tests/Controller/AdminControllerTest.php @@ -2,7 +2,11 @@ namespace App\Tests\Controller; +use App\Entity\Billet; use App\Entity\BilletBuyer; +use App\Entity\BilletBuyerItem; +use App\Entity\BilletOrder; +use App\Entity\Category; use App\Entity\Event; use App\Entity\User; use App\Service\BilletOrderService; @@ -1120,4 +1124,223 @@ class AdminControllerTest extends WebTestCase self::assertResponseStatusCodeSame(403); } + + public function testAnalyticsDefaultPeriod(): void + { + $client = static::createClient(); + $admin = $this->createUser(['ROLE_ROOT']); + + $client->loginUser($admin); + $client->request('GET', '/admin/analytics'); + + self::assertResponseIsSuccessful(); + } + + public function testAnalyticsPeriodToday(): void + { + $client = static::createClient(); + $admin = $this->createUser(['ROLE_ROOT']); + + $client->loginUser($admin); + $client->request('GET', '/admin/analytics?period=today'); + + self::assertResponseIsSuccessful(); + } + + public function testAnalyticsPeriod30d(): void + { + $client = static::createClient(); + $admin = $this->createUser(['ROLE_ROOT']); + + $client->loginUser($admin); + $client->request('GET', '/admin/analytics?period=30d'); + + self::assertResponseIsSuccessful(); + } + + public function testAnalyticsPeriodAll(): void + { + $client = static::createClient(); + $admin = $this->createUser(['ROLE_ROOT']); + + $client->loginUser($admin); + $client->request('GET', '/admin/analytics?period=all'); + + self::assertResponseIsSuccessful(); + } + + public function testAnalyticsDeniedForNonRoot(): void + { + $client = static::createClient(); + $user = $this->createUser(); + + $client->loginUser($user); + $client->request('GET', '/admin/analytics'); + + self::assertResponseStatusCodeSame(403); + } + + public function testOrderTicketsSingleTicketReturnsPdf(): void + { + $client = static::createClient(); + $em = static::getContainer()->get(EntityManagerInterface::class); + $admin = $this->createUser(['ROLE_ROOT']); + + $orga = $this->createOrganizer($em); + $event = $this->createEvent($em, $orga); + + $category = new Category(); + $category->setName('Test'); + $category->setEvent($event); + $category->setPosition(0); + $category->setStartAt(new \DateTimeImmutable('2026-08-01 10:00')); + $category->setEndAt(new \DateTimeImmutable('2026-08-01 18:00')); + $em->persist($category); + + $billet = new Billet(); + $billet->setName('Test'); + $billet->setCategory($category); + $billet->setPriceHT(1000); + $em->persist($billet); + + $order = new BilletBuyer(); + $order->setEvent($event); + $order->setFirstName('Jean'); + $order->setLastName('Test'); + $order->setEmail('jean@test.fr'); + $order->setOrderNumber('2026-'.uniqid()); + $order->setTotalHT(1000); + $order->setStatus(BilletBuyer::STATUS_PAID); + $em->persist($order); + + $item = new BilletBuyerItem(); + $item->setBillet($billet); + $item->setBilletName('Test'); + $item->setQuantity(1); + $item->setUnitPriceHT(1000); + $order->addItem($item); + $em->persist($item); + + $ticket = new BilletOrder(); + $ticket->setBilletBuyer($order); + $ticket->setBillet($billet); + $ticket->setBilletName('Test'); + $ticket->setUnitPriceHT(1000); + $em->persist($ticket); + $em->flush(); + + $billetOrderService = $this->createMock(BilletOrderService::class); + $billetOrderService->expects(self::once())->method('generatePdf')->willReturn('%PDF-1.4 dummy'); + static::getContainer()->set(BilletOrderService::class, $billetOrderService); + + $client->loginUser($admin); + $client->request('GET', '/admin/commandes/'.$order->getId().'/billets'); + + self::assertResponseIsSuccessful(); + self::assertStringContainsString('application/pdf', $client->getResponse()->headers->get('Content-Type')); + } + + public function testOrderTicketsMultipleTicketsReturnsZip(): void + { + $client = static::createClient(); + $em = static::getContainer()->get(EntityManagerInterface::class); + $admin = $this->createUser(['ROLE_ROOT']); + + $orga = $this->createOrganizer($em); + $event = $this->createEvent($em, $orga); + + $category = new Category(); + $category->setName('Test'); + $category->setEvent($event); + $category->setPosition(0); + $category->setStartAt(new \DateTimeImmutable('2026-08-01 10:00')); + $category->setEndAt(new \DateTimeImmutable('2026-08-01 18:00')); + $em->persist($category); + + $billet = new Billet(); + $billet->setName('Test'); + $billet->setCategory($category); + $billet->setPriceHT(1000); + $em->persist($billet); + + $order = new BilletBuyer(); + $order->setEvent($event); + $order->setFirstName('Jean'); + $order->setLastName('Test'); + $order->setEmail('jean@test.fr'); + $order->setOrderNumber('2026-'.uniqid()); + $order->setTotalHT(2000); + $order->setStatus(BilletBuyer::STATUS_PAID); + $em->persist($order); + + $item = new BilletBuyerItem(); + $item->setBillet($billet); + $item->setBilletName('Test'); + $item->setQuantity(2); + $item->setUnitPriceHT(1000); + $order->addItem($item); + $em->persist($item); + + $ticket1 = new BilletOrder(); + $ticket1->setBilletBuyer($order); + $ticket1->setBillet($billet); + $ticket1->setBilletName('Test'); + $ticket1->setUnitPriceHT(1000); + $em->persist($ticket1); + + $ticket2 = new BilletOrder(); + $ticket2->setBilletBuyer($order); + $ticket2->setBillet($billet); + $ticket2->setBilletName('Test'); + $ticket2->setUnitPriceHT(1000); + $em->persist($ticket2); + $em->flush(); + + $billetOrderService = $this->createMock(BilletOrderService::class); + $billetOrderService->expects(self::exactly(2))->method('generatePdf')->willReturn('%PDF-1.4 dummy'); + static::getContainer()->set(BilletOrderService::class, $billetOrderService); + + $client->loginUser($admin); + $client->request('GET', '/admin/commandes/'.$order->getId().'/billets'); + + self::assertResponseIsSuccessful(); + self::assertStringContainsString('application/zip', $client->getResponse()->headers->get('Content-Type')); + } + + public function testOrderTicketsNotFound(): void + { + $client = static::createClient(); + $admin = $this->createUser(['ROLE_ROOT']); + + $client->loginUser($admin); + $client->request('GET', '/admin/commandes/999999/billets'); + + self::assertResponseStatusCodeSame(404); + } + + public function testOrderTicketsNoTickets(): void + { + $client = static::createClient(); + $em = static::getContainer()->get(EntityManagerInterface::class); + $admin = $this->createUser(['ROLE_ROOT']); + + $orga = $this->createOrganizer($em); + $event = $this->createEvent($em, $orga); + + $order = new BilletBuyer(); + $order->setEvent($event); + $order->setFirstName('Jean'); + $order->setLastName('Test'); + $order->setEmail('jean@test.fr'); + $order->setOrderNumber('2026-'.uniqid()); + $order->setTotalHT(1000); + $order->setStatus(BilletBuyer::STATUS_PAID); + $em->persist($order); + $em->flush(); + + $client->loginUser($admin); + $client->request('GET', '/admin/commandes/'.$order->getId().'/billets'); + + self::assertResponseStatusCodeSame(404); + } } diff --git a/tests/Controller/AttestationControllerTest.php b/tests/Controller/AttestationControllerTest.php index 9d49437..54b7bcc 100644 --- a/tests/Controller/AttestationControllerTest.php +++ b/tests/Controller/AttestationControllerTest.php @@ -2,6 +2,9 @@ namespace App\Tests\Controller; +use App\Controller\AttestationController; +use App\Entity\Attestation; +use App\Entity\Event; use App\Entity\Payout; use App\Entity\User; use Doctrine\ORM\EntityManagerInterface; @@ -44,4 +47,179 @@ class AttestationControllerTest extends WebTestCase self::assertResponseIsSuccessful(); self::assertSelectorTextContains('body', 'Attestation introuvable'); } + + public function testVentesRefWithValidReference(): void + { + $client = static::createClient(); + $em = static::getContainer()->get(EntityManagerInterface::class); + + $user = new User(); + $user->setEmail('test-vref-'.uniqid().'@example.com'); + $user->setFirstName('Test'); + $user->setLastName('User'); + $user->setPassword('$2y$13$hashed'); + $em->persist($user); + + $event = new Event(); + $event->setAccount($user); + $event->setTitle('Test Event'); + $event->setStartAt(new \DateTimeImmutable('+1 day')); + $event->setEndAt(new \DateTimeImmutable('+2 days')); + $event->setAddress('123 Rue Test'); + $event->setZipcode('75001'); + $event->setCity('Paris'); + $em->persist($event); + + $reference = 'REF-'.uniqid(); + $payload = ['event' => 'Test Event', 'total' => 5000]; + + $attestation = new Attestation($reference, 'sig_'.uniqid(), $event, $user, 10, $payload); + $em->persist($attestation); + $em->flush(); + + $client->request('GET', '/attestation/ventes/r/'.$reference); + + self::assertResponseIsSuccessful(); + } + + public function testVentesRefWithInvalidReference(): void + { + $client = static::createClient(); + $client->request('GET', '/attestation/ventes/r/INVALID_REF'); + + self::assertResponseIsSuccessful(); + self::assertSelectorTextContains('body', 'introuvable'); + } + + public function testVentesWithValidHash(): void + { + $client = static::createClient(); + $appSecret = static::getContainer()->getParameter('kernel.secret'); + + $data = ['event' => 'Test Event', 'total' => 5000]; + $hash = AttestationController::generateHash($data, $appSecret); + + $client->request('GET', '/attestation/ventes/'.$hash); + + self::assertResponseIsSuccessful(); + } + + public function testVentesWithValidHashAndRegisteredAttestation(): void + { + $client = static::createClient(); + $em = static::getContainer()->get(EntityManagerInterface::class); + $appSecret = static::getContainer()->getParameter('kernel.secret'); + + $user = new User(); + $user->setEmail('test-vreg-'.uniqid().'@example.com'); + $user->setFirstName('Test'); + $user->setLastName('User'); + $user->setPassword('$2y$13$hashed'); + $em->persist($user); + + $event = new Event(); + $event->setAccount($user); + $event->setTitle('Test Event'); + $event->setStartAt(new \DateTimeImmutable('+1 day')); + $event->setEndAt(new \DateTimeImmutable('+2 days')); + $event->setAddress('123 Rue Test'); + $event->setZipcode('75001'); + $event->setCity('Paris'); + $em->persist($event); + + $data = ['event' => 'Registered Event', 'total' => 8000]; + $json = json_encode($data, \JSON_UNESCAPED_UNICODE); + $signatureHash = hash_hmac('sha256', $json, $appSecret); + + $attestation = new Attestation('REF-'.uniqid(), $signatureHash, $event, $user, 20, $data); + $em->persist($attestation); + $em->flush(); + + $hash = AttestationController::generateHash($data, $appSecret); + $client->request('GET', '/attestation/ventes/'.$hash); + + self::assertResponseIsSuccessful(); + } + + public function testVentesWithInvalidBase64(): void + { + $client = static::createClient(); + $client->request('GET', '/attestation/ventes/!!!invalid-base64!!!'); + + self::assertResponseIsSuccessful(); + self::assertSelectorTextContains('body', 'introuvable'); + } + + public function testVentesWithMissingPipeSeparator(): void + { + $client = static::createClient(); + $hash = rtrim(strtr(base64_encode('no-pipe-here'), '+/', '-_'), '='); + + $client->request('GET', '/attestation/ventes/'.$hash); + + self::assertResponseIsSuccessful(); + self::assertSelectorTextContains('body', 'introuvable'); + } + + public function testVentesWithInvalidSignature(): void + { + $client = static::createClient(); + $json = json_encode(['event' => 'Test'], \JSON_UNESCAPED_UNICODE); + $hash = rtrim(strtr(base64_encode('invalidsignature|'.$json), '+/', '-_'), '='); + + $client->request('GET', '/attestation/ventes/'.$hash); + + self::assertResponseIsSuccessful(); + self::assertSelectorTextContains('body', 'introuvable'); + } + + public function testVentesWithInvalidJsonPayload(): void + { + $client = static::createClient(); + $appSecret = static::getContainer()->getParameter('kernel.secret'); + + $invalidJson = '{invalid json}'; + $signature = hash_hmac('sha256', $invalidJson, $appSecret); + $hash = rtrim(strtr(base64_encode($signature.'|'.$invalidJson), '+/', '-_'), '='); + + $client->request('GET', '/attestation/ventes/'.$hash); + + self::assertResponseIsSuccessful(); + self::assertSelectorTextContains('body', 'introuvable'); + } + + public function testGenerateHash(): void + { + $data = ['key' => 'value', 'amount' => 1000]; + $secret = 'test-secret'; + + $hash = AttestationController::generateHash($data, $secret); + + self::assertNotEmpty($hash); + self::assertMatchesRegularExpression('/^[A-Za-z0-9\-_]+$/', $hash); + + $decoded = base64_decode(strtr($hash, '-_', '+/'), true); + self::assertNotFalse($decoded); + + $parts = explode('|', $decoded, 2); + self::assertCount(2, $parts); + + [$signature, $jsonPayload] = $parts; + $expectedSignature = hash_hmac('sha256', $jsonPayload, $secret); + self::assertTrue(hash_equals($expectedSignature, $signature)); + self::assertSame($data, json_decode($jsonPayload, true)); + } + + public function testGenerateHashWithUnicodeData(): void + { + $data = ['name' => 'Événement été', 'city' => 'Zürich']; + $secret = 'test-secret'; + + $hash = AttestationController::generateHash($data, $secret); + $decoded = base64_decode(strtr($hash, '-_', '+/'), true); + $parts = explode('|', $decoded, 2); + + self::assertStringContainsString('Événement été', $parts[1]); + self::assertStringContainsString('Zürich', $parts[1]); + } } diff --git a/tests/Controller/LegalControllerTest.php b/tests/Controller/LegalControllerTest.php index 9fa9905..16bf599 100644 --- a/tests/Controller/LegalControllerTest.php +++ b/tests/Controller/LegalControllerTest.php @@ -2,6 +2,7 @@ namespace App\Tests\Controller; +use App\Service\RgpdService; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; class LegalControllerTest extends WebTestCase @@ -61,4 +62,84 @@ class LegalControllerTest extends WebTestCase self::assertResponseIsSuccessful(); } + + public function testRgpdAccessEmptyFields(): void + { + $client = static::createClient(); + $client->request('POST', '/rgpd/acces', ['ip' => '', 'email' => '']); + + self::assertResponseRedirects('/rgpd'); + } + + public function testRgpdAccessWithDataFound(): void + { + $client = static::createClient(); + + $mock = $this->createMock(RgpdService::class); + $mock->method('handleAccessRequest') + ->with('192.168.1.1', 'test@example.com') + ->willReturn(['found' => true, 'count' => 5]); + + static::getContainer()->set(RgpdService::class, $mock); + + $client->request('POST', '/rgpd/acces', ['ip' => '192.168.1.1', 'email' => 'test@example.com']); + + self::assertResponseRedirects('/rgpd'); + } + + public function testRgpdAccessWithNoData(): void + { + $client = static::createClient(); + + $mock = $this->createMock(RgpdService::class); + $mock->method('handleAccessRequest') + ->with('192.168.1.1', 'test@example.com') + ->willReturn(['found' => false, 'count' => 0]); + + static::getContainer()->set(RgpdService::class, $mock); + + $client->request('POST', '/rgpd/acces', ['ip' => '192.168.1.1', 'email' => 'test@example.com']); + + self::assertResponseRedirects('/rgpd'); + } + + public function testRgpdDeletionEmptyFields(): void + { + $client = static::createClient(); + $client->request('POST', '/rgpd/suppression', ['ip' => '', 'email' => '']); + + self::assertResponseRedirects('/rgpd'); + } + + public function testRgpdDeletionWithDataFound(): void + { + $client = static::createClient(); + + $mock = $this->createMock(RgpdService::class); + $mock->method('handleDeletionRequest') + ->with('192.168.1.1', 'test@example.com') + ->willReturn(['found' => true, 'count' => 3]); + + static::getContainer()->set(RgpdService::class, $mock); + + $client->request('POST', '/rgpd/suppression', ['ip' => '192.168.1.1', 'email' => 'test@example.com']); + + self::assertResponseRedirects('/rgpd'); + } + + public function testRgpdDeletionWithNoData(): void + { + $client = static::createClient(); + + $mock = $this->createMock(RgpdService::class); + $mock->method('handleDeletionRequest') + ->with('192.168.1.1', 'test@example.com') + ->willReturn(['found' => false, 'count' => 0]); + + static::getContainer()->set(RgpdService::class, $mock); + + $client->request('POST', '/rgpd/suppression', ['ip' => '192.168.1.1', 'email' => 'test@example.com']); + + self::assertResponseRedirects('/rgpd'); + } } diff --git a/tests/Entity/AnalyticsEventTest.php b/tests/Entity/AnalyticsEventTest.php new file mode 100644 index 0000000..897273b --- /dev/null +++ b/tests/Entity/AnalyticsEventTest.php @@ -0,0 +1,88 @@ +getId()); + self::assertSame('page_view', $event->getEventName()); + self::assertNull($event->getTitle()); + self::assertNull($event->getReferrer()); + self::assertInstanceOf(\DateTimeImmutable::class, $event->getCreatedAt()); + } + + public function testSetEventName(): void + { + $event = new AnalyticsEvent(); + $result = $event->setEventName('click'); + + self::assertSame('click', $event->getEventName()); + self::assertSame($event, $result); + } + + public function testSetUrl(): void + { + $event = new AnalyticsEvent(); + $result = $event->setUrl('https://example.com/page'); + + self::assertSame('https://example.com/page', $event->getUrl()); + self::assertSame($event, $result); + } + + public function testSetTitle(): void + { + $event = new AnalyticsEvent(); + $result = $event->setTitle('My Page'); + + self::assertSame('My Page', $event->getTitle()); + self::assertSame($event, $result); + } + + public function testSetTitleNull(): void + { + $event = new AnalyticsEvent(); + $event->setTitle('My Page'); + $result = $event->setTitle(null); + + self::assertNull($event->getTitle()); + self::assertSame($event, $result); + } + + public function testSetReferrer(): void + { + $event = new AnalyticsEvent(); + $result = $event->setReferrer('https://google.com'); + + self::assertSame('https://google.com', $event->getReferrer()); + self::assertSame($event, $result); + } + + public function testSetReferrerNull(): void + { + $event = new AnalyticsEvent(); + $event->setReferrer('https://google.com'); + $result = $event->setReferrer(null); + + self::assertNull($event->getReferrer()); + self::assertSame($event, $result); + } + + public function testSetVisitor(): void + { + $visitor = $this->createMock(AnalyticsUniqId::class); + + $event = new AnalyticsEvent(); + $result = $event->setVisitor($visitor); + + self::assertSame($visitor, $event->getVisitor()); + self::assertSame($event, $result); + } +}