From f93c7e014f9bdc8462a339ce3e2557ff12ab50dc Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Tue, 30 Sep 2025 15:45:26 +0200 Subject: [PATCH] Add test for MyInvitationsController.php --- .../Controller/MyInvitationsController.php | 4 +- .../Repository/InviteRepository.php | 2 +- .../MyInvitationsControllerTest.php | 292 ++++++++++++++++++ ...ocGeneratorTemplateRepositoryInterface.php | 5 + .../Pagination/PaginatorFactory.php | 2 +- 5 files changed, 301 insertions(+), 4 deletions(-) create mode 100644 src/Bundle/ChillCalendarBundle/Tests/Controller/MyInvitationsControllerTest.php diff --git a/src/Bundle/ChillCalendarBundle/Controller/MyInvitationsController.php b/src/Bundle/ChillCalendarBundle/Controller/MyInvitationsController.php index de029dd20..7af5ac18f 100644 --- a/src/Bundle/ChillCalendarBundle/Controller/MyInvitationsController.php +++ b/src/Bundle/ChillCalendarBundle/Controller/MyInvitationsController.php @@ -13,7 +13,7 @@ namespace Chill\CalendarBundle\Controller; use Chill\CalendarBundle\Entity\Calendar; use Chill\CalendarBundle\Repository\InviteRepository; -use Chill\DocGeneratorBundle\Repository\DocGeneratorTemplateRepository; +use Chill\DocGeneratorBundle\Repository\DocGeneratorTemplateRepositoryInterface; use Chill\MainBundle\Entity\User; use Chill\MainBundle\Pagination\PaginatorFactory; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; @@ -24,7 +24,7 @@ use Symfony\Component\Routing\Annotation\Route; class MyInvitationsController extends AbstractController { - public function __construct(private readonly InviteRepository $inviteRepository, private readonly PaginatorFactory $paginator, private readonly DocGeneratorTemplateRepository $docGeneratorTemplateRepository) {} + public function __construct(private readonly InviteRepository $inviteRepository, private readonly PaginatorFactory $paginator, private readonly DocGeneratorTemplateRepositoryInterface $docGeneratorTemplateRepository) {} #[Route(path: '/{_locale}/calendar/invitations/my', name: 'chill_calendar_invitations_list_my')] public function myInvitations(Request $request): Response diff --git a/src/Bundle/ChillCalendarBundle/Repository/InviteRepository.php b/src/Bundle/ChillCalendarBundle/Repository/InviteRepository.php index 5d19d326c..2c89021ac 100644 --- a/src/Bundle/ChillCalendarBundle/Repository/InviteRepository.php +++ b/src/Bundle/ChillCalendarBundle/Repository/InviteRepository.php @@ -16,7 +16,7 @@ use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Doctrine\Persistence\ObjectRepository; -readonly class InviteRepository implements ObjectRepository +class InviteRepository implements ObjectRepository { private EntityRepository $entityRepository; diff --git a/src/Bundle/ChillCalendarBundle/Tests/Controller/MyInvitationsControllerTest.php b/src/Bundle/ChillCalendarBundle/Tests/Controller/MyInvitationsControllerTest.php new file mode 100644 index 000000000..32ed70456 --- /dev/null +++ b/src/Bundle/ChillCalendarBundle/Tests/Controller/MyInvitationsControllerTest.php @@ -0,0 +1,292 @@ +prophesize(InviteRepository::class); + $paginatorFactory = $this->prophesize(PaginatorFactory::class); + $docGeneratorTemplateRepository = $this->prophesize(DocGeneratorTemplateRepositoryInterface::class); + + // Create controller instance + $this->controller = new MyInvitationsController( + $inviteRepository->reveal(), + $paginatorFactory->reveal(), + $docGeneratorTemplateRepository->reveal() + ); + + // Set up necessary services for AbstractController + $authorizationChecker = $this->prophesize(AuthorizationCheckerInterface::class); + $tokenStorage = $this->prophesize(TokenStorageInterface::class); + $twig = $this->prophesize(Environment::class); + + // Use reflection to set the container + $reflection = new \ReflectionClass($this->controller); + $containerProperty = $reflection->getParentClass()->getProperty('container'); + $containerProperty->setAccessible(true); + + // Create a mock container + $container = $this->prophesize(\Psr\Container\ContainerInterface::class); + $container->has('security.authorization_checker')->willReturn(true); + $container->get('security.authorization_checker')->willReturn($authorizationChecker->reveal()); + $container->has('security.token_storage')->willReturn(true); + $container->get('security.token_storage')->willReturn($tokenStorage->reveal()); + $container->has('twig')->willReturn(true); + $container->get('twig')->willReturn($twig->reveal()); + + $containerProperty->setValue($this->controller, $container->reveal()); + } + + public function testMyInvitationsReturnsCorrectAmountOfInvitations(): void + { + // Create test user + $user = new User(); + $user->setUsername('testuser'); + + // Create test invitations + $invite1 = new Invite(); + $invite1->setUser($user); + $invite1->setStatus(Invite::PENDING); + + $invite2 = new Invite(); + $invite2->setUser($user); + $invite2->setStatus(Invite::ACCEPTED); + + $invite3 = new Invite(); + $invite3->setUser($user); + $invite3->setStatus(Invite::DECLINED); + + $allInvitations = [$invite1, $invite2, $invite3]; + $paginatedInvitations = [$invite1, $invite2]; // First page with 2 items per page + + // Set up repository prophecies + $inviteRepository = $this->prophesize(InviteRepository::class); + $inviteRepository->findBy(['user' => $user])->willReturn($allInvitations); + $inviteRepository->findBy( + ['user' => $user], + ['createdAt' => 'DESC'], + 2, // items per page + 0 // offset + )->willReturn($paginatedInvitations); + + // Set up paginator prophecies + $paginator = $this->prophesize(PaginatorInterface::class); + $paginator->getItemsPerPage()->willReturn(2); + $paginator->getCurrentPageFirstItemNumber()->willReturn(0); + + $paginatorFactory = $this->prophesize(PaginatorFactory::class); + $paginatorFactory->create(3)->willReturn($paginator->reveal()); + + // Set up doc generator repository + $docGeneratorTemplateRepository = $this->prophesize(DocGeneratorTemplateRepositoryInterface::class); + $docGeneratorTemplateRepository->findByEntity(Calendar::class)->willReturn([]); + + // Create controller with mocked dependencies + $controller = new MyInvitationsController( + $inviteRepository->reveal(), + $paginatorFactory->reveal(), + $docGeneratorTemplateRepository->reveal() + ); + + // Set up authorization checker to return true for ROLE_USER + $authorizationChecker = $this->prophesize(AuthorizationCheckerInterface::class); + $authorizationChecker->isGranted('ROLE_USER', null)->willReturn(true); + + // Set up token storage to return user + $token = $this->prophesize(TokenInterface::class); + $token->getUser()->willReturn($user); + $tokenStorage = $this->prophesize(TokenStorageInterface::class); + $tokenStorage->getToken()->willReturn($token->reveal()); + + // Set up twig to return a response + $twig = $this->prophesize(Environment::class); + $twig->render('@ChillCalendar/Invitations/listByUser.html.twig', [ + 'invitations' => $paginatedInvitations, + 'paginator' => $paginator->reveal(), + 'templates' => [], + ])->willReturn('rendered content'); + + // Set up container + $container = $this->prophesize(\Psr\Container\ContainerInterface::class); + $container->has('security.authorization_checker')->willReturn(true); + $container->get('security.authorization_checker')->willReturn($authorizationChecker->reveal()); + $container->has('security.token_storage')->willReturn(true); + $container->get('security.token_storage')->willReturn($tokenStorage->reveal()); + $container->has('twig')->willReturn(true); + $container->get('twig')->willReturn($twig->reveal()); + + // Use reflection to set the container + $reflection = new \ReflectionClass($controller); + $containerProperty = $reflection->getParentClass()->getProperty('container'); + $containerProperty->setAccessible(true); + $containerProperty->setValue($controller, $container->reveal()); + + // Create request + $request = new Request(); + + // Execute the action + $response = $controller->myInvitations($request); + + // Assert that response is successful + self::assertInstanceOf(Response::class, $response); + self::assertSame(200, $response->getStatusCode()); + self::assertSame('rendered content', $response->getContent()); + } + + public function testMyInvitationsPageLoads(): void + { + // Create test user + $user = new User(); + $user->setUsername('testuser'); + + // Set up repository prophecies - no invitations + $inviteRepository = $this->prophesize(InviteRepository::class); + $inviteRepository->findBy(['user' => $user])->willReturn([]); + $inviteRepository->findBy( + ['user' => $user], + ['createdAt' => 'DESC'], + 20, // default items per page + 0 // offset + )->willReturn([]); + + // Set up paginator prophecies + $paginator = $this->prophesize(PaginatorInterface::class); + $paginator->getItemsPerPage()->willReturn(20); + $paginator->getCurrentPageFirstItemNumber()->willReturn(0); + + $paginatorFactory = $this->prophesize(PaginatorFactory::class); + $paginatorFactory->create(0)->willReturn($paginator->reveal()); + + // Set up doc generator repository + $docGeneratorTemplateRepository = $this->prophesize(DocGeneratorTemplateRepositoryInterface::class); + $docGeneratorTemplateRepository->findByEntity(Calendar::class)->willReturn([]); + + // Create controller with mocked dependencies + $controller = new MyInvitationsController( + $inviteRepository->reveal(), + $paginatorFactory->reveal(), + $docGeneratorTemplateRepository->reveal() + ); + + // Set up authorization checker to return true for ROLE_USER + $authorizationChecker = $this->prophesize(AuthorizationCheckerInterface::class); + $authorizationChecker->isGranted('ROLE_USER', null)->willReturn(true); + + // Set up token storage to return user + $token = $this->prophesize(TokenInterface::class); + $token->getUser()->willReturn($user); + $tokenStorage = $this->prophesize(TokenStorageInterface::class); + $tokenStorage->getToken()->willReturn($token->reveal()); + + // Set up twig to return a response + $twig = $this->prophesize(Environment::class); + $twig->render('@ChillCalendar/Invitations/listByUser.html.twig', [ + 'invitations' => [], + 'paginator' => $paginator->reveal(), + 'templates' => [], + ])->willReturn('empty page content'); + + // Set up container + $container = $this->prophesize(\Psr\Container\ContainerInterface::class); + $container->has('security.authorization_checker')->willReturn(true); + $container->get('security.authorization_checker')->willReturn($authorizationChecker->reveal()); + $container->has('security.token_storage')->willReturn(true); + $container->get('security.token_storage')->willReturn($tokenStorage->reveal()); + $container->has('twig')->willReturn(true); + $container->get('twig')->willReturn($twig->reveal()); + + // Use reflection to set the container + $reflection = new \ReflectionClass($controller); + $containerProperty = $reflection->getParentClass()->getProperty('container'); + $containerProperty->setAccessible(true); + $containerProperty->setValue($controller, $container->reveal()); + + // Create request + $request = new Request(); + + // Execute the action + $response = $controller->myInvitations($request); + + // Assert that page loads successfully + self::assertInstanceOf(Response::class, $response); + self::assertSame(200, $response->getStatusCode()); + self::assertSame('empty page content', $response->getContent()); + } + + public function testMyInvitationsRequiresAuthentication(): void + { + // Create controller with minimal dependencies + $inviteRepository = $this->prophesize(InviteRepository::class); + $paginatorFactory = $this->prophesize(PaginatorFactory::class); + $docGeneratorTemplateRepository = $this->prophesize(DocGeneratorTemplateRepositoryInterface::class); + + $controller = new MyInvitationsController( + $inviteRepository->reveal(), + $paginatorFactory->reveal(), + $docGeneratorTemplateRepository->reveal() + ); + + // Set up authorization checker to return false for ROLE_USER + $authorizationChecker = $this->prophesize(AuthorizationCheckerInterface::class); + $authorizationChecker->isGranted('ROLE_USER')->willReturn(false); + $authorizationChecker->isGranted('ROLE_USER', null)->willReturn(false); + + // Set up container + $container = $this->prophesize(\Psr\Container\ContainerInterface::class); + $container->has('security.authorization_checker')->willReturn(true); + $container->get('security.authorization_checker')->willReturn($authorizationChecker->reveal()); + + // Use reflection to set the container + $reflection = new \ReflectionClass($controller); + $containerProperty = $reflection->getParentClass()->getProperty('container'); + $containerProperty->setAccessible(true); + $containerProperty->setValue($controller, $container->reveal()); + + // Create request + $request = new Request(); + + // Expect AccessDeniedException + $this->expectException(\Symfony\Component\Security\Core\Exception\AccessDeniedException::class); + + // Execute the action + $controller->myInvitations($request); + } +} diff --git a/src/Bundle/ChillDocGeneratorBundle/Repository/DocGeneratorTemplateRepositoryInterface.php b/src/Bundle/ChillDocGeneratorBundle/Repository/DocGeneratorTemplateRepositoryInterface.php index e5071e76a..f2e3cf629 100644 --- a/src/Bundle/ChillDocGeneratorBundle/Repository/DocGeneratorTemplateRepositoryInterface.php +++ b/src/Bundle/ChillDocGeneratorBundle/Repository/DocGeneratorTemplateRepositoryInterface.php @@ -20,4 +20,9 @@ use Doctrine\Persistence\ObjectRepository; interface DocGeneratorTemplateRepositoryInterface extends ObjectRepository { public function countByEntity(string $entity): int; + + /** + * @return array|DocGeneratorTemplate[] + */ + public function findByEntity(string $entity, ?int $start = 0, ?int $limit = 50): array; } diff --git a/src/Bundle/ChillMainBundle/Pagination/PaginatorFactory.php b/src/Bundle/ChillMainBundle/Pagination/PaginatorFactory.php index 9a809287b..e53139fac 100644 --- a/src/Bundle/ChillMainBundle/Pagination/PaginatorFactory.php +++ b/src/Bundle/ChillMainBundle/Pagination/PaginatorFactory.php @@ -17,7 +17,7 @@ use Symfony\Component\Routing\RouterInterface; /** * Create paginator instances. */ -final readonly class PaginatorFactory implements PaginatorFactoryInterface +class PaginatorFactory implements PaginatorFactoryInterface { final public const DEFAULT_CURRENT_PAGE_KEY = 'page';