From e41bedce8db595af6f53267c059c8672d66d4956 Mon Sep 17 00:00:00 2001 From: Marijn Doeve Date: Fri, 31 Oct 2025 20:41:10 +0100 Subject: [PATCH] Some tests --- .env | 1 + .github/workflows/ci.yml | 2 + .idea/TijdVoorDeTest.iml | 2 + .idea/php-test-framework.xml | 2 +- .idea/php.xml | 6 +- Justfile | 8 +- composer.json | 2 + composer.lock | 164 ++++++++++++++++++- config/bundles.php | 2 + phpunit.dist.xml | 1 + src/Command/AddSettingsCommand.php | 41 ----- src/Command/ClaimSeasonCommand.php | 11 +- src/Command/MakeAdminCommand.php | 8 +- src/Controller/QuizController.php | 6 +- src/DataFixtures/KrtekFixtures.php | 8 +- src/DataFixtures/TestFixtures.php | 33 ++++ src/Repository/CandidateRepository.php | 9 - src/Repository/GivenAnswerRepository.php | 9 - src/Repository/SeasonRepository.php | 11 ++ symfony.lock | 12 ++ tests/Command/ClaimSeasonCommandTest.php | 67 ++++++++ tests/Command/MakeAdminCommandTest.php | 54 ++++++ tests/Repository/CandidateRepositoryTest.php | 74 +++++++++ tests/Repository/QuestionRepositoryTest.php | 110 +++++++++++++ 24 files changed, 562 insertions(+), 81 deletions(-) delete mode 100644 src/Command/AddSettingsCommand.php create mode 100644 src/DataFixtures/TestFixtures.php create mode 100644 tests/Command/ClaimSeasonCommandTest.php create mode 100644 tests/Command/MakeAdminCommandTest.php create mode 100644 tests/Repository/CandidateRepositoryTest.php create mode 100644 tests/Repository/QuestionRepositoryTest.php diff --git a/.env b/.env index 08f2977..f8efe0f 100644 --- a/.env +++ b/.env @@ -36,3 +36,4 @@ MAILER_DSN=null://null ###> sentry/sentry-symfony ### SENTRY_DSN= ###< sentry/sentry-symfony ### +XDEBUG_MODE=coverage diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0034028..25bd701 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,6 +57,8 @@ jobs: run: docker compose exec -T php bin/console -e test doctrine:database:create - name: Run migrations run: docker compose exec -T php bin/console -e test doctrine:migrations:migrate --no-interaction + - name: Load fixtures + run: docker compose exec -T php bin/console -e test doctrine:fixtures:load --no-interaction --group=test - name: Run PHPUnit run: docker compose exec -T php vendor/bin/phpunit - name: Doctrine Schema Validator diff --git a/.idea/TijdVoorDeTest.iml b/.idea/TijdVoorDeTest.iml index 19e2f78..a5a7e22 100644 --- a/.idea/TijdVoorDeTest.iml +++ b/.idea/TijdVoorDeTest.iml @@ -165,6 +165,8 @@ + + diff --git a/.idea/php-test-framework.xml b/.idea/php-test-framework.xml index 527878e..6a33060 100644 --- a/.idea/php-test-framework.xml +++ b/.idea/php-test-framework.xml @@ -5,7 +5,7 @@ - + diff --git a/.idea/php.xml b/.idea/php.xml index 697eb1b..b2b17bf 100644 --- a/.idea/php.xml +++ b/.idea/php.xml @@ -195,6 +195,9 @@ + + + @@ -288,8 +291,7 @@ - - + diff --git a/Justfile b/Justfile index e261941..91b613b 100644 --- a/Justfile +++ b/Justfile @@ -20,7 +20,7 @@ migrate: up docker compose run --rm php bin/console doctrine:migrations:migrate --no-interaction fixtures: - docker compose exec php bin/console doctrine:fixtures:load --purge-with-truncate --no-interaction + docker compose exec php bin/console doctrine:fixtures:load --purge-with-truncate --no-interaction --group=dev translations: docker compose exec php bin/console translation:extract --force --format=xliff --sort=asc --clean nl @@ -39,3 +39,9 @@ phpstan *args: clean: docker compose down -v --remove-orphans rm -rf vendor var assets/vendor public/assets public/bundles .php-cs-fixer.cache .twig-cs-fixer.cache + +reload-tests: + @docker compose exec php bin/console --env=test doctrine:database:drop --if-exists --force + @docker compose exec php bin/console --env=test doctrine:database:create + @docker compose exec php bin/console --env=test doctrine:schema:create + @docker compose exec php bin/console --env=test doctrine:fixtures:load --no-interaction --group=test diff --git a/composer.json b/composer.json index 9db9615..f3b513a 100644 --- a/composer.json +++ b/composer.json @@ -48,6 +48,8 @@ "twig/twig": "^3.21.1" }, "require-dev": { + "brianium/paratest": "^7.14", + "dama/doctrine-test-bundle": "^8.4", "doctrine/doctrine-fixtures-bundle": "^4.3", "friendsofphp/php-cs-fixer": "^3.89.0", "phpstan/extension-installer": "^1.4.3", diff --git a/composer.lock b/composer.lock index 7e9a55a..c6b85af 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "a87c0ab4f169484014962048bb246199", + "content-hash": "0ad053f4679a6b7cb76699c8391a12e5", "packages": [ { "name": "composer/pcre", @@ -8537,6 +8537,99 @@ } ], "packages-dev": [ + { + "name": "brianium/paratest", + "version": "v7.14.2", + "source": { + "type": "git", + "url": "https://github.com/paratestphp/paratest.git", + "reference": "de06de1ae1203b11976c6ca01d6a9081c8b33d45" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paratestphp/paratest/zipball/de06de1ae1203b11976c6ca01d6a9081c8b33d45", + "reference": "de06de1ae1203b11976c6ca01d6a9081c8b33d45", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-simplexml": "*", + "fidry/cpu-core-counter": "^1.3.0", + "jean85/pretty-package-versions": "^2.1.1", + "php": "~8.3.0 || ~8.4.0 || ~8.5.0", + "phpunit/php-code-coverage": "^12.4.0", + "phpunit/php-file-iterator": "^6", + "phpunit/php-timer": "^8", + "phpunit/phpunit": "^12.4.1", + "sebastian/environment": "^8.0.3", + "symfony/console": "^6.4.20 || ^7.3.4", + "symfony/process": "^6.4.20 || ^7.3.4" + }, + "require-dev": { + "doctrine/coding-standard": "^14.0.0", + "ext-pcntl": "*", + "ext-pcov": "*", + "ext-posix": "*", + "phpstan/phpstan": "^2.1.31", + "phpstan/phpstan-deprecation-rules": "^2.0.3", + "phpstan/phpstan-phpunit": "^2.0.7", + "phpstan/phpstan-strict-rules": "^2.0.7", + "symfony/filesystem": "^6.4.13 || ^7.3.2" + }, + "bin": [ + "bin/paratest", + "bin/paratest_for_phpstorm" + ], + "type": "library", + "autoload": { + "psr-4": { + "ParaTest\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Scaturro", + "email": "scaturrob@gmail.com", + "role": "Developer" + }, + { + "name": "Filippo Tessarotto", + "email": "zoeslam@gmail.com", + "role": "Developer" + } + ], + "description": "Parallel testing for PHP", + "homepage": "https://github.com/paratestphp/paratest", + "keywords": [ + "concurrent", + "parallel", + "phpunit", + "testing" + ], + "support": { + "issues": "https://github.com/paratestphp/paratest/issues", + "source": "https://github.com/paratestphp/paratest/tree/v7.14.2" + }, + "funding": [ + { + "url": "https://github.com/sponsors/Slamdunk", + "type": "github" + }, + { + "url": "https://paypal.me/filippotessarotto", + "type": "paypal" + } + ], + "time": "2025-10-24T07:20:53+00:00" + }, { "name": "clue/ndjson-react", "version": "v1.3.0", @@ -8667,6 +8760,75 @@ ], "time": "2024-05-06T16:37:16+00:00" }, + { + "name": "dama/doctrine-test-bundle", + "version": "v8.4.0", + "source": { + "type": "git", + "url": "https://github.com/dmaicher/doctrine-test-bundle.git", + "reference": "ce7cd44126c36694e2f2d92c4aedd4fc5b0874f2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dmaicher/doctrine-test-bundle/zipball/ce7cd44126c36694e2f2d92c4aedd4fc5b0874f2", + "reference": "ce7cd44126c36694e2f2d92c4aedd4fc5b0874f2", + "shasum": "" + }, + "require": { + "doctrine/dbal": "^3.3 || ^4.0", + "doctrine/doctrine-bundle": "^2.11.0 || ^3.0", + "php": ">= 8.1", + "psr/cache": "^2.0 || ^3.0", + "symfony/cache": "^6.4 || ^7.3 || ^8.0", + "symfony/framework-bundle": "^6.4 || ^7.3 || ^8.0" + }, + "conflict": { + "phpunit/phpunit": "<10.0" + }, + "require-dev": { + "behat/behat": "^3.0", + "friendsofphp/php-cs-fixer": "^3.27", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^10.5.57 || ^11.5.41|| ^12.3.14", + "symfony/dotenv": "^6.4 || ^7.3 || ^8.0", + "symfony/process": "^6.4 || ^7.3 || ^8.0" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "8.x-dev" + } + }, + "autoload": { + "psr-4": { + "DAMA\\DoctrineTestBundle\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "David Maicher", + "email": "mail@dmaicher.de" + } + ], + "description": "Symfony bundle to isolate doctrine database tests and improve test performance", + "keywords": [ + "doctrine", + "isolation", + "performance", + "symfony", + "testing", + "tests" + ], + "support": { + "issues": "https://github.com/dmaicher/doctrine-test-bundle/issues", + "source": "https://github.com/dmaicher/doctrine-test-bundle/tree/v8.4.0" + }, + "time": "2025-10-11T15:24:02+00:00" + }, { "name": "doctrine/data-fixtures", "version": "2.2.0", diff --git a/config/bundles.php b/config/bundles.php index 31046bc..5c150fe 100644 --- a/config/bundles.php +++ b/config/bundles.php @@ -2,6 +2,7 @@ declare(strict_types=1); +use DAMA\DoctrineTestBundle\DAMADoctrineTestBundle; use Doctrine\Bundle\DoctrineBundle\DoctrineBundle; use Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle; use Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle; @@ -32,4 +33,5 @@ return [ SymfonycastsSassBundle::class => ['all' => true], StimulusBundle::class => ['all' => true], TurboBundle::class => ['all' => true], + DAMADoctrineTestBundle::class => ['test' => true], ]; diff --git a/phpunit.dist.xml b/phpunit.dist.xml index 0b31da2..81218c2 100644 --- a/phpunit.dist.xml +++ b/phpunit.dist.xml @@ -30,5 +30,6 @@ + diff --git a/src/Command/AddSettingsCommand.php b/src/Command/AddSettingsCommand.php deleted file mode 100644 index e9e22a5..0000000 --- a/src/Command/AddSettingsCommand.php +++ /dev/null @@ -1,41 +0,0 @@ -seasonRepository->findAll() as $season) { - if (null !== $season->settings) { - continue; - } - - $io->text('Adding settings to season : '.$season->seasonCode); - $season->settings = new SeasonSettings(); - } - - $this->entityManager->flush(); - - return Command::SUCCESS; - } -} diff --git a/src/Command/ClaimSeasonCommand.php b/src/Command/ClaimSeasonCommand.php index 42a8897..33badac 100644 --- a/src/Command/ClaimSeasonCommand.php +++ b/src/Command/ClaimSeasonCommand.php @@ -8,8 +8,6 @@ use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\Console\Attribute\Argument; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Tvdt\Repository\SeasonRepository; use Tvdt\Repository\UserRepository; @@ -18,7 +16,7 @@ use Tvdt\Repository\UserRepository; name: 'tvdt:claim-season', description: 'Give a user owner rights on a season', )] -readonly class ClaimSeasonCommand +final readonly class ClaimSeasonCommand { public function __construct(private UserRepository $userRepository, private SeasonRepository $seasonRepository, private EntityManagerInterface $entityManager) {} @@ -27,13 +25,10 @@ readonly class ClaimSeasonCommand string $seasonCode, #[Argument] string $email, - InputInterface $input, - OutputInterface $output, + SymfonyStyle $io, ): int { - $io = new SymfonyStyle($input, $output); - try { - $season = $this->seasonRepository->findOneBy(['seasonCode' => $seasonCode]); + $season = $this->seasonRepository->findOneBySeasonCode($seasonCode); if (null === $season) { throw new \InvalidArgumentException('Season not found'); } diff --git a/src/Command/MakeAdminCommand.php b/src/Command/MakeAdminCommand.php index ca66b67..4c3d1c2 100644 --- a/src/Command/MakeAdminCommand.php +++ b/src/Command/MakeAdminCommand.php @@ -7,8 +7,6 @@ namespace Tvdt\Command; use Symfony\Component\Console\Attribute\Argument; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Tvdt\Repository\UserRepository; @@ -16,17 +14,15 @@ use Tvdt\Repository\UserRepository; name: 'tvdt:make-admin', description: 'Give a user the role admin', )] -readonly class MakeAdminCommand +final readonly class MakeAdminCommand { public function __construct(private UserRepository $userRepository) {} public function __invoke( #[Argument] string $email, - InputInterface $input, - OutputInterface $output, + SymfonyStyle $io, ): int { - $io = new SymfonyStyle($input, $output); try { $this->userRepository->makeAdmin($email); } catch (\InvalidArgumentException) { diff --git a/src/Controller/QuizController.php b/src/Controller/QuizController.php index fb5b9e1..535ce14 100644 --- a/src/Controller/QuizController.php +++ b/src/Controller/QuizController.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace Tvdt\Controller; +use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\AsController; @@ -30,7 +31,7 @@ use Tvdt\Repository\SeasonRepository; #[AsController] final class QuizController extends AbstractController { - public function __construct(private readonly TranslatorInterface $translator) {} + public function __construct(private readonly TranslatorInterface $translator, private readonly EntityManagerInterface $entityManager) {} #[Route(path: '/', name: 'tvdt_quiz_select_season', methods: ['GET', 'POST'])] public function selectSeason(Request $request, SeasonRepository $seasonRepository): Response @@ -110,7 +111,8 @@ final class QuizController extends AbstractController } $givenAnswer = new GivenAnswer($candidate, $answer->question->quiz, $answer); - $givenAnswerRepository->save($givenAnswer); + $this->entityManager->persist($givenAnswer); + $this->entityManager->flush(); return $this->redirectToRoute('tvdt_quiz_quiz_page', ['seasonCode' => $season->seasonCode, 'nameHash' => $nameHash]); } diff --git a/src/DataFixtures/KrtekFixtures.php b/src/DataFixtures/KrtekFixtures.php index 8f5bdbf..dab5226 100644 --- a/src/DataFixtures/KrtekFixtures.php +++ b/src/DataFixtures/KrtekFixtures.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace Tvdt\DataFixtures; use Doctrine\Bundle\FixturesBundle\Fixture; +use Doctrine\Bundle\FixturesBundle\FixtureGroupInterface; use Doctrine\Persistence\ObjectManager; use Tvdt\Entity\Answer; use Tvdt\Entity\Candidate; @@ -12,8 +13,13 @@ use Tvdt\Entity\Question; use Tvdt\Entity\Quiz; use Tvdt\Entity\Season; -class KrtekFixtures extends Fixture +final class KrtekFixtures extends Fixture implements FixtureGroupInterface { + public static function getGroups(): array + { + return ['test', 'dev']; + } + public function load(ObjectManager $manager): void { $season = new Season(); diff --git a/src/DataFixtures/TestFixtures.php b/src/DataFixtures/TestFixtures.php new file mode 100644 index 0000000..9911a44 --- /dev/null +++ b/src/DataFixtures/TestFixtures.php @@ -0,0 +1,33 @@ +email = 'test@example.org'; + $user->password = $this->passwordHasher->hashPassword($user, 'test1234'); + + $manager->persist($user); + $manager->flush(); + } +} diff --git a/src/Repository/CandidateRepository.php b/src/Repository/CandidateRepository.php index 028fece..e2a4643 100644 --- a/src/Repository/CandidateRepository.php +++ b/src/Repository/CandidateRepository.php @@ -43,15 +43,6 @@ class CandidateRepository extends ServiceEntityRepository ->getOneOrNullResult(); } - public function save(Candidate $candidate, bool $flush = true): void - { - $this->getEntityManager()->persist($candidate); - - if ($flush) { - $this->getEntityManager()->flush(); - } - } - /** * @throws DatetimeException * diff --git a/src/Repository/GivenAnswerRepository.php b/src/Repository/GivenAnswerRepository.php index b8a13df..096f23e 100644 --- a/src/Repository/GivenAnswerRepository.php +++ b/src/Repository/GivenAnswerRepository.php @@ -17,13 +17,4 @@ class GivenAnswerRepository extends ServiceEntityRepository { parent::__construct($registry, GivenAnswer::class); } - - public function save(GivenAnswer $givenAnswer, bool $flush = true): void - { - $this->getEntityManager()->persist($givenAnswer); - - if ($flush) { - $this->getEntityManager()->flush(); - } - } } diff --git a/src/Repository/SeasonRepository.php b/src/Repository/SeasonRepository.php index 194817d..3429b58 100644 --- a/src/Repository/SeasonRepository.php +++ b/src/Repository/SeasonRepository.php @@ -19,6 +19,17 @@ class SeasonRepository extends ServiceEntityRepository parent::__construct($registry, Season::class); } + public function findOneBySeasonCode(string $seasonCode): ?Season + { + return $this->getEntityManager()->createQuery(<<setParameter(':seasonCode', $seasonCode) + ->setMaxResults(1) + ->getOneOrNullResult(); + } + /** @return list Returns an array of Season objects */ public function getSeasonsForUser(User $user): array { diff --git a/symfony.lock b/symfony.lock index 331a929..0daf6ab 100644 --- a/symfony.lock +++ b/symfony.lock @@ -1,4 +1,16 @@ { + "dama/doctrine-test-bundle": { + "version": "8.4", + "recipe": { + "repo": "github.com/symfony/recipes-contrib", + "branch": "main", + "version": "8.3", + "ref": "dfc51177476fb39d014ed89944cde53dc3326d23" + }, + "files": [ + "config/packages/dama_doctrine_test_bundle.yaml" + ] + }, "doctrine/deprecations": { "version": "1.1", "recipe": { diff --git a/tests/Command/ClaimSeasonCommandTest.php b/tests/Command/ClaimSeasonCommandTest.php new file mode 100644 index 0000000..29638f2 --- /dev/null +++ b/tests/Command/ClaimSeasonCommandTest.php @@ -0,0 +1,67 @@ +seasonRepository = $container->get(SeasonRepository::class); + $application = new Application(self::$kernel); + $command = $application->find('tvdt:claim-season'); + $this->commandTester = new CommandTester($command); + } + + public function testSeasonClaim(): void + { + $this->commandTester->execute([ + 'season-code' => 'krtek', + 'email' => 'test@example.org', + ]); + + $season = $this->seasonRepository->findOneBySeasonCode('krtek'); + \assert($season instanceof Season); + + $this->assertSame(Command::SUCCESS, $this->commandTester->getStatusCode()); + $this->assertCount(1, $season->owners); + } + + public function testInvalidEmalFails(): void + { + $this->commandTester->execute([ + 'season-code' => 'krtek', + 'email' => 'nonexisting@example.org', + ]); + + $this->assertSame(Command::FAILURE, $this->commandTester->getStatusCode()); + $this->commandTester->getStatusCode(); + } + + public function testInvalidSeasonCodeFails(): void + { + $this->commandTester->execute([ + 'season-code' => 'dhadk', + 'email' => 'test@example.org', + ]); + + $this->assertSame(Command::FAILURE, $this->commandTester->getStatusCode()); + $this->commandTester->getStatusCode(); + } +} diff --git a/tests/Command/MakeAdminCommandTest.php b/tests/Command/MakeAdminCommandTest.php new file mode 100644 index 0000000..05f47c9 --- /dev/null +++ b/tests/Command/MakeAdminCommandTest.php @@ -0,0 +1,54 @@ +userRepository = $container->get(UserRepository::class); + $application = new Application(self::$kernel); + $command = $application->find('tvdt:make-admin'); + $this->commandTester = new CommandTester($command); + } + + public function testMakeAdmin(): void + { + $this->commandTester->execute([ + 'email' => 'test@example.org', + ]); + + $user = $this->userRepository->findOneBy(['email' => 'test@example.org']); + \assert($user instanceof User); + + $this->assertSame(Command::SUCCESS, $this->commandTester->getStatusCode()); + $this->assertContains('ROLE_ADMIN', $user->roles); + } + + public function testInvalidEmalFails(): 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 new file mode 100644 index 0000000..6f5b6f4 --- /dev/null +++ b/tests/Repository/CandidateRepositoryTest.php @@ -0,0 +1,74 @@ +seasonRepository = self::getContainer()->get(SeasonRepository::class); + $this->candidateRepository = self::getContainer()->get(CandidateRepository::class); + } + + /** @return iterable */ + public static function candidateHashDataProvider(): iterable + { + yield 'Normal' => ['Q2xhdWRpYQ']; + yield 'lowercase' => ['Y2xhdWRpYQ']; + yield 'UPPERCASE' => ['Q0xBVURJQQ']; + } + + #[DataProvider('candidateHashDataProvider')] + public function testGetCandidateByHash(string $hash): void + { + /** @var Season $krtekSeason */ + $krtekSeason = $this->seasonRepository->findOneBySeasonCode('krtek'); + $candidate = $this->candidateRepository->getCandidateByHash( + $krtekSeason, + $hash, + ); + + $this->assertInstanceOf(Candidate::class, $candidate); + + $this->assertSame('Claudia', $candidate->name); + } + + public function testGetCandidateByHashUnknownHashReturnsNull(): void + { + /** @var Season $krtekSeason */ + $krtekSeason = $this->seasonRepository->findOneBySeasonCode('krtek'); + $result = $this->candidateRepository->getCandidateByHash( + $krtekSeason, + 'TWFyaWpu', + ); + $this->assertNull($result); + } + + public function testGetCandidateByHashInvalidBase64HashReturnsNull(): void + { + /** @var Season $krtekSeason */ + $krtekSeason = $this->seasonRepository->findOneBySeasonCode('krtek'); + $result = $this->candidateRepository->getCandidateByHash( + $krtekSeason, + 'TWFyaWpu*', + ); + $this->assertNull($result); + } + + public function testGetScores(): void + { + $this->markTestIncomplete('TODO: Make fixtures first and write good test.'); + } +} diff --git a/tests/Repository/QuestionRepositoryTest.php b/tests/Repository/QuestionRepositoryTest.php new file mode 100644 index 0000000..25c2307 --- /dev/null +++ b/tests/Repository/QuestionRepositoryTest.php @@ -0,0 +1,110 @@ +entityManager = $container->get(EntityManagerInterface::class); + $this->questionRepository = $container->get(QuestionRepository::class); + $this->seasonRepository = $container->get(SeasonRepository::class); + $this->candidateRepository = $container->get(CandidateRepository::class); + } + + public function testFindNextQuestionReturnsRightQuestion(): void + { + $krtekSeason = $this->seasonRepository->findOneBySeasonCode('krtek'); + \assert($krtekSeason instanceof Season); + $candidate = $this->candidateRepository->findOneBy(['season' => $krtekSeason, 'name' => 'Tom']); + \assert($candidate instanceof Candidate); + + $question = $this->questionRepository->findNextQuestionForCandidate($candidate); + $this->assertInstanceOf(Question::class, $question); + $this->assertSame('Is de Krtek een man of een vrouw?', $question->question, 'Wrong first question'); + + $this->answerQuestion($question, $candidate); + + $question = $this->questionRepository->findNextQuestionForCandidate($candidate); + $this->assertInstanceOf(Question::class, $question); + $this->assertSame('Hoeveel broers heeft de Krtek?', $question->question, 'Wrong second question'); + + $question = $this->questionRepository->findNextQuestionForCandidate($candidate); + $this->assertInstanceOf(Question::class, $question); + $this->assertSame('Hoeveel broers heeft de Krtek?', $question->question, 'Getting question a second time fails'); + + $quiz = $krtekSeason->quizzes->last(); + \assert($quiz instanceof Quiz); + $krtekSeason->activeQuiz = $quiz; + $this->entityManager->flush(); + + $question = $this->questionRepository->findNextQuestionForCandidate($candidate); + $this->assertInstanceOf(Question::class, $question); + $this->assertSame('Is de Krtek een man of een vrouw?', $question->question, 'Wrong question after switching season.'); + } + + public function testFindNextQuestionGivesNullWhenAllQuestionsAnswred(): void + { + $krtekSeason = $this->seasonRepository->findOneBySeasonCode('krtek'); + \assert($krtekSeason instanceof Season); + $candidate = $this->candidateRepository->findOneBy(['season' => $krtekSeason, 'name' => 'Tom']); + \assert($candidate instanceof Candidate); + + for ($i = 0; $i < 15; ++$i) { + $question = $this->questionRepository->findNextQuestionForCandidate($candidate); + \assert($question instanceof Question); + $this->answerQuestion($question, $candidate); + } + $question = $this->questionRepository->findNextQuestionForCandidate($candidate); + + $this->assertNull($question); + } + + public function testFindNextQuestionWithNoActiveQuizReturnsNull(): void + { + $krtekSeason = $this->seasonRepository->findOneBySeasonCode('krtek'); + \assert($krtekSeason instanceof Season); + $candidate = $this->candidateRepository->findOneBy(['season' => $krtekSeason, 'name' => 'Tom']); + \assert($candidate instanceof Candidate); + + $krtekSeason->activeQuiz = null; + $this->entityManager->flush(); + + $question = $this->questionRepository->findNextQuestionForCandidate($candidate); + + $this->assertNull($question); + } + + private function answerQuestion(Question $question, Candidate $candidate): void + { + $answer = $question->answers->first(); + \assert($answer instanceof Answer); + $this->entityManager->persist(new GivenAnswer( + $candidate, + $question->quiz, + $answer, + )); + $this->entityManager->flush(); + } +}