From f886f0f6c27cea118707f592a05cc475825c1775 Mon Sep 17 00:00:00 2001 From: Marijn Doeve Date: Fri, 31 Oct 2025 22:00:28 +0100 Subject: [PATCH] More tests! --- .../packages/dama_doctrine_test_bundle.yaml | 5 + rector.php | 1 - src/Command/ClaimSeasonCommand.php | 3 +- src/DataFixtures/TestFixtures.php | 2 +- src/Repository/QuizRepository.php | 31 +++--- src/Repository/SeasonRepository.php | 2 +- tests/Command/ClaimSeasonCommandTest.php | 12 ++- tests/Command/MakeAdminCommandTest.php | 11 ++- tests/Repository/CandidateRepositoryTest.php | 9 +- tests/Repository/QuestionRepositoryTest.php | 32 +++--- .../QuizCandidateRepositoryTest.php | 98 +++++++++++++++++++ tests/Repository/QuizRepositoryTest.php | 66 +++++++++++++ tests/Security/Voter/SeasonVoterTest.php | 2 + 13 files changed, 230 insertions(+), 44 deletions(-) create mode 100644 config/packages/dama_doctrine_test_bundle.yaml create mode 100644 tests/Repository/QuizCandidateRepositoryTest.php create mode 100644 tests/Repository/QuizRepositoryTest.php diff --git a/config/packages/dama_doctrine_test_bundle.yaml b/config/packages/dama_doctrine_test_bundle.yaml new file mode 100644 index 0000000..3482cba --- /dev/null +++ b/config/packages/dama_doctrine_test_bundle.yaml @@ -0,0 +1,5 @@ +when@test: + dama_doctrine_test: + enable_static_connection: true + enable_static_meta_data_cache: true + enable_static_query_cache: true diff --git a/rector.php b/rector.php index 3702cc1..bff6114 100644 --- a/rector.php +++ b/rector.php @@ -26,7 +26,6 @@ return RectorConfig::configure() privatization: true, instanceOf: true, earlyReturn: true, - strictBooleans: true, rectorPreset: true, phpunitCodeQuality: true, doctrineCodeQuality: true, diff --git a/src/Command/ClaimSeasonCommand.php b/src/Command/ClaimSeasonCommand.php index 33badac..e6dfd1b 100644 --- a/src/Command/ClaimSeasonCommand.php +++ b/src/Command/ClaimSeasonCommand.php @@ -9,6 +9,7 @@ use Symfony\Component\Console\Attribute\Argument; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Style\SymfonyStyle; +use Tvdt\Entity\Season; use Tvdt\Repository\SeasonRepository; use Tvdt\Repository\UserRepository; @@ -29,7 +30,7 @@ final readonly class ClaimSeasonCommand ): int { try { $season = $this->seasonRepository->findOneBySeasonCode($seasonCode); - if (null === $season) { + if (!$season instanceof Season) { throw new \InvalidArgumentException('Season not found'); } diff --git a/src/DataFixtures/TestFixtures.php b/src/DataFixtures/TestFixtures.php index 9911a44..d233d4f 100644 --- a/src/DataFixtures/TestFixtures.php +++ b/src/DataFixtures/TestFixtures.php @@ -13,7 +13,7 @@ use Tvdt\Entity\User; final class TestFixtures extends Fixture implements FixtureGroupInterface { public function __construct( - private UserPasswordHasherInterface $passwordHasher, + private readonly UserPasswordHasherInterface $passwordHasher, ) {} public static function getGroups(): array diff --git a/src/Repository/QuizRepository.php b/src/Repository/QuizRepository.php index 2f72807..c06f395 100644 --- a/src/Repository/QuizRepository.php +++ b/src/Repository/QuizRepository.php @@ -7,10 +7,7 @@ namespace Tvdt\Repository; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\Persistence\ManagerRegistry; use Psr\Log\LoggerInterface; -use Tvdt\Entity\Elimination; -use Tvdt\Entity\GivenAnswer; use Tvdt\Entity\Quiz; -use Tvdt\Entity\QuizCandidate; use Tvdt\Exception\ErrorClearingQuizException; /** @@ -29,22 +26,26 @@ class QuizRepository extends ServiceEntityRepository $em = $this->getEntityManager(); $em->beginTransaction(); try { - $em->createQueryBuilder() - ->delete()->from(QuizCandidate::class, 'qc') - ->where('qc.quiz = :quiz') + $em->createQuery(<<setParameter('quiz', $quiz) - ->getQuery()->execute(); + ->execute(); - $em->createQueryBuilder() - ->delete()->from(GivenAnswer::class, 'ga') - ->where('ga.quiz = :quiz') + $em->createQuery(<<setParameter('quiz', $quiz) - ->getQuery()->execute(); - $em->createQueryBuilder() - ->delete()->from(Elimination::class, 'e') - ->where('e.quiz = :quiz') + ->execute(); + + $em->createQuery(<<setParameter('quiz', $quiz) - ->getQuery()->execute(); + ->execute(); } catch (\Throwable $throwable) { $this->logger->error($throwable->getMessage()); $em->rollback(); diff --git a/src/Repository/SeasonRepository.php b/src/Repository/SeasonRepository.php index 3429b58..d07d2a7 100644 --- a/src/Repository/SeasonRepository.php +++ b/src/Repository/SeasonRepository.php @@ -25,7 +25,7 @@ class SeasonRepository extends ServiceEntityRepository select s from Tvdt\Entity\Season s where s.seasonCode = :seasonCode DQL) - ->setParameter(':seasonCode', $seasonCode) + ->setParameter('seasonCode', $seasonCode) ->setMaxResults(1) ->getOneOrNullResult(); } diff --git a/tests/Command/ClaimSeasonCommandTest.php b/tests/Command/ClaimSeasonCommandTest.php index 29638f2..ebec6e7 100644 --- a/tests/Command/ClaimSeasonCommandTest.php +++ b/tests/Command/ClaimSeasonCommandTest.php @@ -4,24 +4,28 @@ declare(strict_types=1); namespace Tvdt\Tests\Command; +use PHPUnit\Framework\Attributes\CoversClass; use Symfony\Bundle\FrameworkBundle\Console\Application; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Tester\CommandTester; use Symfony\Component\HttpKernel\KernelInterface; +use Tvdt\Command\ClaimSeasonCommand; use Tvdt\Entity\Season; use Tvdt\Repository\SeasonRepository; +#[CoversClass(ClaimSeasonCommand::class)] final class ClaimSeasonCommandTest extends KernelTestCase { private SeasonRepository $seasonRepository; + private CommandTester $commandTester; protected function setUp(): void { $container = self::getContainer(); - \assert(self::$kernel instanceof KernelInterface); + $this->assertInstanceOf(KernelInterface::class, self::$kernel); $this->seasonRepository = $container->get(SeasonRepository::class); $application = new Application(self::$kernel); @@ -37,13 +41,13 @@ final class ClaimSeasonCommandTest extends KernelTestCase ]); $season = $this->seasonRepository->findOneBySeasonCode('krtek'); - \assert($season instanceof Season); + $this->assertInstanceOf(Season::class, $season); $this->assertSame(Command::SUCCESS, $this->commandTester->getStatusCode()); $this->assertCount(1, $season->owners); } - public function testInvalidEmalFails(): void + public function testInvalidEmailFails(): void { $this->commandTester->execute([ 'season-code' => 'krtek', @@ -51,7 +55,6 @@ final class ClaimSeasonCommandTest extends KernelTestCase ]); $this->assertSame(Command::FAILURE, $this->commandTester->getStatusCode()); - $this->commandTester->getStatusCode(); } public function testInvalidSeasonCodeFails(): void @@ -62,6 +65,5 @@ final class ClaimSeasonCommandTest extends KernelTestCase ]); $this->assertSame(Command::FAILURE, $this->commandTester->getStatusCode()); - $this->commandTester->getStatusCode(); } } diff --git a/tests/Command/MakeAdminCommandTest.php b/tests/Command/MakeAdminCommandTest.php index 05f47c9..a904455 100644 --- a/tests/Command/MakeAdminCommandTest.php +++ b/tests/Command/MakeAdminCommandTest.php @@ -4,24 +4,28 @@ declare(strict_types=1); namespace Tvdt\Tests\Command; +use PHPUnit\Framework\Attributes\CoversClass; use Symfony\Bundle\FrameworkBundle\Console\Application; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Tester\CommandTester; use Symfony\Component\HttpKernel\KernelInterface; +use Tvdt\Command\MakeAdminCommand; use Tvdt\Entity\User; use Tvdt\Repository\UserRepository; +#[CoversClass(MakeAdminCommand::class)] final class MakeAdminCommandTest extends KernelTestCase { private UserRepository $userRepository; + private CommandTester $commandTester; protected function setUp(): void { $container = self::getContainer(); - \assert(self::$kernel instanceof KernelInterface); + $this->assertInstanceOf(KernelInterface::class, self::$kernel); $this->userRepository = $container->get(UserRepository::class); $application = new Application(self::$kernel); @@ -36,19 +40,18 @@ final class MakeAdminCommandTest extends KernelTestCase ]); $user = $this->userRepository->findOneBy(['email' => 'test@example.org']); - \assert($user instanceof User); + $this->assertInstanceOf(User::class, $user); $this->assertSame(Command::SUCCESS, $this->commandTester->getStatusCode()); $this->assertContains('ROLE_ADMIN', $user->roles); } - public function testInvalidEmalFails(): void + public function testInvalidEmailFails(): void { $this->commandTester->execute([ 'email' => 'nonexisting@example.org', ]); $this->assertSame(Command::FAILURE, $this->commandTester->getStatusCode()); - $this->commandTester->getStatusCode(); } } diff --git a/tests/Repository/CandidateRepositoryTest.php b/tests/Repository/CandidateRepositoryTest.php index 6f5b6f4..84b319a 100644 --- a/tests/Repository/CandidateRepositoryTest.php +++ b/tests/Repository/CandidateRepositoryTest.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace Tvdt\Tests\Repository; +use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; use Tvdt\Entity\Candidate; @@ -11,9 +12,11 @@ use Tvdt\Entity\Season; use Tvdt\Repository\CandidateRepository; use Tvdt\Repository\SeasonRepository; -class CandidateRepositoryTest extends KernelTestCase +#[CoversClass(CandidateRepository::class)] +final class CandidateRepositoryTest extends KernelTestCase { private SeasonRepository $seasonRepository; + private CandidateRepository $candidateRepository; protected function setUp(): void @@ -53,7 +56,7 @@ class CandidateRepositoryTest extends KernelTestCase $krtekSeason, 'TWFyaWpu', ); - $this->assertNull($result); + $this->assertNotInstanceOf(Candidate::class, $result); } public function testGetCandidateByHashInvalidBase64HashReturnsNull(): void @@ -64,7 +67,7 @@ class CandidateRepositoryTest extends KernelTestCase $krtekSeason, 'TWFyaWpu*', ); - $this->assertNull($result); + $this->assertNotInstanceOf(Candidate::class, $result); } public function testGetScores(): void diff --git a/tests/Repository/QuestionRepositoryTest.php b/tests/Repository/QuestionRepositoryTest.php index 25c2307..244740f 100644 --- a/tests/Repository/QuestionRepositoryTest.php +++ b/tests/Repository/QuestionRepositoryTest.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace Tvdt\Tests\Repository; use Doctrine\ORM\EntityManagerInterface; +use PHPUnit\Framework\Attributes\CoversClass; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; use Tvdt\Entity\Answer; use Tvdt\Entity\Candidate; @@ -16,11 +17,15 @@ use Tvdt\Repository\CandidateRepository; use Tvdt\Repository\QuestionRepository; use Tvdt\Repository\SeasonRepository; -class QuestionRepositoryTest extends KernelTestCase +#[CoversClass(QuestionRepository::class)] +final class QuestionRepositoryTest extends KernelTestCase { private EntityManagerInterface $entityManager; + private QuestionRepository $questionRepository; + private SeasonRepository $seasonRepository; + private CandidateRepository $candidateRepository; protected function setUp(): void @@ -36,9 +41,9 @@ class QuestionRepositoryTest extends KernelTestCase public function testFindNextQuestionReturnsRightQuestion(): void { $krtekSeason = $this->seasonRepository->findOneBySeasonCode('krtek'); - \assert($krtekSeason instanceof Season); + $this->assertInstanceOf(Season::class, $krtekSeason); $candidate = $this->candidateRepository->findOneBy(['season' => $krtekSeason, 'name' => 'Tom']); - \assert($candidate instanceof Candidate); + $this->assertInstanceOf(Candidate::class, $candidate); $question = $this->questionRepository->findNextQuestionForCandidate($candidate); $this->assertInstanceOf(Question::class, $question); @@ -55,7 +60,7 @@ class QuestionRepositoryTest extends KernelTestCase $this->assertSame('Hoeveel broers heeft de Krtek?', $question->question, 'Getting question a second time fails'); $quiz = $krtekSeason->quizzes->last(); - \assert($quiz instanceof Quiz); + $this->assertInstanceOf(Quiz::class, $quiz); $krtekSeason->activeQuiz = $quiz; $this->entityManager->flush(); @@ -64,42 +69,43 @@ class QuestionRepositoryTest extends KernelTestCase $this->assertSame('Is de Krtek een man of een vrouw?', $question->question, 'Wrong question after switching season.'); } - public function testFindNextQuestionGivesNullWhenAllQuestionsAnswred(): void + public function testFindNextQuestionGivesNullWhenAllQuestionsAnswered(): void { $krtekSeason = $this->seasonRepository->findOneBySeasonCode('krtek'); - \assert($krtekSeason instanceof Season); + $this->assertInstanceOf(Season::class, $krtekSeason); $candidate = $this->candidateRepository->findOneBy(['season' => $krtekSeason, 'name' => 'Tom']); - \assert($candidate instanceof Candidate); + $this->assertInstanceOf(Candidate::class, $candidate); for ($i = 0; $i < 15; ++$i) { $question = $this->questionRepository->findNextQuestionForCandidate($candidate); - \assert($question instanceof Question); + $this->assertInstanceOf(Question::class, $question); $this->answerQuestion($question, $candidate); } + $question = $this->questionRepository->findNextQuestionForCandidate($candidate); - $this->assertNull($question); + $this->assertNotInstanceOf(Question::class, $question); } public function testFindNextQuestionWithNoActiveQuizReturnsNull(): void { $krtekSeason = $this->seasonRepository->findOneBySeasonCode('krtek'); - \assert($krtekSeason instanceof Season); + $this->assertInstanceOf(Season::class, $krtekSeason); $candidate = $this->candidateRepository->findOneBy(['season' => $krtekSeason, 'name' => 'Tom']); - \assert($candidate instanceof Candidate); + $this->assertInstanceOf(Candidate::class, $candidate); $krtekSeason->activeQuiz = null; $this->entityManager->flush(); $question = $this->questionRepository->findNextQuestionForCandidate($candidate); - $this->assertNull($question); + $this->assertNotInstanceOf(Question::class, $question); } private function answerQuestion(Question $question, Candidate $candidate): void { $answer = $question->answers->first(); - \assert($answer instanceof Answer); + $this->assertInstanceOf(Answer::class, $answer); $this->entityManager->persist(new GivenAnswer( $candidate, $question->quiz, diff --git a/tests/Repository/QuizCandidateRepositoryTest.php b/tests/Repository/QuizCandidateRepositoryTest.php new file mode 100644 index 0000000..780c3fe --- /dev/null +++ b/tests/Repository/QuizCandidateRepositoryTest.php @@ -0,0 +1,98 @@ +seasonRepository = $container->get(SeasonRepository::class); + $this->quizCandidateRepository = $container->get(QuizCandidateRepository::class); + $this->candidateRepository = $container->get(CandidateRepository::class); + } + + public function testCreateIfNotExists(): void + { + $krtekSeason = $this->seasonRepository->findOneBySeasonCode('krtek'); + $this->assertInstanceOf(Season::class, $krtekSeason); + $candidate = $this->candidateRepository->findOneBy(['season' => $krtekSeason, 'name' => 'Myrthe']); + $this->assertInstanceOf(Candidate::class, $candidate); + $quiz = $krtekSeason->activeQuiz; + $this->assertInstanceOf(Quiz::class, $quiz); + + $result = $this->quizCandidateRepository->createIfNotExist($quiz, $candidate); + $this->assertTrue($result); + + $quizCandidate = $this->quizCandidateRepository->findOneBy([ + 'candidate' => $candidate, + 'quiz' => $quiz, + ]); + + $this->assertInstanceOf(QuizCandidate::class, $quizCandidate); + + $result = $this->quizCandidateRepository->createIfNotExist($quiz, $candidate); + $this->assertFalse($result); + } + + public function testSetCorrectionsForCandidateUpdatesCandidateCorrectly(): void + { + $krtekSeason = $this->seasonRepository->findOneBySeasonCode('krtek'); + $this->assertInstanceOf(Season::class, $krtekSeason); + $candidate = $this->candidateRepository->findOneBy(['season' => $krtekSeason, 'name' => 'Myrthe']); + $this->assertInstanceOf(Candidate::class, $candidate); + $quiz = $krtekSeason->activeQuiz; + $this->assertInstanceOf(Quiz::class, $quiz); + + $this->quizCandidateRepository->createIfNotExist($quiz, $candidate); + + $this->quizCandidateRepository->setCorrectionsForCandidate( + $quiz, $candidate, 3.5, + ); + + $quizCandidate = $this->quizCandidateRepository->findOneBy([ + 'candidate' => $candidate, + 'quiz' => $quiz, + ]); + + $this->assertInstanceOf(QuizCandidate::class, $quizCandidate); + + $this->assertEqualsWithDelta(3.5, $quizCandidate->corrections, 0.1); + } + + public function testCannotGiveCorrectionsToCandidateWithoutResult(): void + { + $krtekSeason = $this->seasonRepository->findOneBySeasonCode('krtek'); + $this->assertInstanceOf(Season::class, $krtekSeason); + $candidate = $this->candidateRepository->findOneBy(['season' => $krtekSeason, 'name' => 'Myrthe']); + $this->assertInstanceOf(Candidate::class, $candidate); + $quiz = $krtekSeason->activeQuiz; + $this->assertInstanceOf(Quiz::class, $quiz); + + $this->expectException(\InvalidArgumentException::class); + + $this->quizCandidateRepository->setCorrectionsForCandidate( + $quiz, $candidate, 3.5, + ); + } +} diff --git a/tests/Repository/QuizRepositoryTest.php b/tests/Repository/QuizRepositoryTest.php new file mode 100644 index 0000000..a6462fa --- /dev/null +++ b/tests/Repository/QuizRepositoryTest.php @@ -0,0 +1,66 @@ +entityManager = self::getContainer()->get(EntityManagerInterface::class); + $this->seasonRepository = self::getContainer()->get(SeasonRepository::class); + $this->quizRepository = self::getContainer()->get(QuizRepository::class); + parent::setUp(); + } + + public function testClearQuiz(): void + { + $krtekSeason = $this->seasonRepository->findOneBy(['seasonCode' => 'krtek']); + $this->assertInstanceOf(Season::class, $krtekSeason); + + $quiz = $krtekSeason->activeQuiz; + $this->assertInstanceOf(Quiz::class, $quiz); + + $this->quizRepository->clearQuiz($quiz); + + $this->entityManager->refresh($krtekSeason); + + $this->assertEmpty($quiz->candidateData); + $this->assertEmpty($quiz->eliminations); + + /** @var GivenAnswerRepository $givenAnswerRepository */ + $givenAnswerRepository = self::getContainer()->get(GivenAnswerRepository::class); + $this->assertEmpty($givenAnswerRepository->findBy(['quiz' => $quiz])); + } + + public function testDeleteQuiz(): void + { + $krtekSeason = $this->seasonRepository->findOneBy(['seasonCode' => 'krtek']); + $this->assertInstanceOf(Season::class, $krtekSeason); + $quiz = $krtekSeason->quizzes->last(); + $this->assertInstanceOf(Quiz::class, $quiz); + + $this->quizRepository->deleteQuiz($quiz); + + $this->entityManager->refresh($krtekSeason); + + $this->assertCount(1, $krtekSeason->quizzes); + } +} diff --git a/tests/Security/Voter/SeasonVoterTest.php b/tests/Security/Voter/SeasonVoterTest.php index 884939b..eeca45b 100644 --- a/tests/Security/Voter/SeasonVoterTest.php +++ b/tests/Security/Voter/SeasonVoterTest.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace Tvdt\Tests\Security\Voter; +use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\MockObject\Stub; use PHPUnit\Framework\TestCase; @@ -18,6 +19,7 @@ use Tvdt\Entity\Season; use Tvdt\Entity\User; use Tvdt\Security\Voter\SeasonVoter; +#[CoversClass(SeasonVoter::class)] final class SeasonVoterTest extends TestCase { private SeasonVoter $seasonVoter;