From f5819870bc4a0abd41e74ce63bd12cca9f702a88 Mon Sep 17 00:00:00 2001 From: Hecht Date: Mon, 18 Mar 2024 22:56:23 +0100 Subject: [PATCH] Added the tournament routes and tests --- config/api_platform/resources.xml | 12 +- src/Controller/GetTournamentFights.php | 2 - src/Entity/Character.php | 12 +- src/Entity/Dojo.php | 3 +- src/Entity/Fight.php | 1 - src/Entity/Tournament.php | 15 ++ src/Entity/TournamentRegistration.php | 6 +- .../TournamentRegistrationStateProcessor.php | 41 +++++ src/Validator/CharacterOwned.php | 12 ++ src/Validator/CharacterOwnedValidator.php | 33 ++++ src/Validator/StartDateInFuture.php | 12 ++ src/Validator/StartDateInFutureValidator.php | 27 +++ tests/AbstractTest.php | 6 + tests/TournamentTest.php | 168 +++++++++++++++--- 14 files changed, 316 insertions(+), 34 deletions(-) create mode 100644 src/State/TournamentRegistrationStateProcessor.php create mode 100644 src/Validator/CharacterOwned.php create mode 100644 src/Validator/CharacterOwnedValidator.php create mode 100644 src/Validator/StartDateInFuture.php create mode 100644 src/Validator/StartDateInFutureValidator.php diff --git a/config/api_platform/resources.xml b/config/api_platform/resources.xml index 023144a..9f3de08 100644 --- a/config/api_platform/resources.xml +++ b/config/api_platform/resources.xml @@ -143,14 +143,22 @@ - + - + + + + + + + + + diff --git a/src/Controller/GetTournamentFights.php b/src/Controller/GetTournamentFights.php index 9d3d0a5..af55de5 100644 --- a/src/Controller/GetTournamentFights.php +++ b/src/Controller/GetTournamentFights.php @@ -13,8 +13,6 @@ class GetTournamentFights { return $em->getRepository(Fight::class)->findBy([ 'tournament' => $tournamentId - ], [ - 'startDate' => 'ASC' ]); } } diff --git a/src/Entity/Character.php b/src/Entity/Character.php index 38443b4..b1bae6e 100644 --- a/src/Entity/Character.php +++ b/src/Entity/Character.php @@ -167,17 +167,19 @@ class Character extends Thing } /** - * - * @return Technique[] + * @return Collection */ - public function getTechniques(): mixed + public function getTechniques(): Collection { - return $this->techniques->getValues(); + return $this->techniques; } public function addTechnique(Technique $technique): static { - $this->techniques[] = $technique; + if (!$this->techniques->contains($technique)) { + $this->techniques->add($technique); + } + return $this; } diff --git a/src/Entity/Dojo.php b/src/Entity/Dojo.php index 1323ff4..c0c4512 100644 --- a/src/Entity/Dojo.php +++ b/src/Entity/Dojo.php @@ -62,7 +62,6 @@ class Dojo extends Thing } /** - * * @return Collection */ public function getMembers(): Collection @@ -72,7 +71,7 @@ class Dojo extends Thing public function addMember(Character $member): static { - if (! $this->members->contains($member)) { + if (!$this->members->contains($member)) { $this->members->add($member); $member->setDojo($this); } diff --git a/src/Entity/Fight.php b/src/Entity/Fight.php index bf6d0c8..0c0e11b 100644 --- a/src/Entity/Fight.php +++ b/src/Entity/Fight.php @@ -21,7 +21,6 @@ class Fight extends Thing private array $events = []; #[ORM\ManyToOne] - #[ORM\JoinColumn(nullable: false)] private ?Character $winner = null; #[ORM\ManyToOne(inversedBy: 'fights')] diff --git a/src/Entity/Tournament.php b/src/Entity/Tournament.php index c616d89..170c88d 100644 --- a/src/Entity/Tournament.php +++ b/src/Entity/Tournament.php @@ -23,6 +23,9 @@ class Tournament extends Thing #[ORM\OneToMany(mappedBy: 'tournament', targetEntity: Fight::class)] private Collection $fights; + #[ORM\ManyToOne] + private ?Character $winner = null; + public function __construct() { $this->characters = new ArrayCollection(); @@ -108,4 +111,16 @@ class Tournament extends Thing return $this; } + + public function getWinner(): ?Character + { + return $this->winner; + } + + public function setWinner(?Character $winner): static + { + $this->winner = $winner; + + return $this; + } } diff --git a/src/Entity/TournamentRegistration.php b/src/Entity/TournamentRegistration.php index 4a7e731..193fee3 100644 --- a/src/Entity/TournamentRegistration.php +++ b/src/Entity/TournamentRegistration.php @@ -1,11 +1,15 @@ updateData($data); + $result = $this->persistProcessor->process($data, $operation, $uriVariables, $context); + + return $result; + } + + private function updateData(TournamentRegistration $registration): void + { + $tournament = $registration->tournament; + $character = $registration->character; + + if ($tournament->getCharacters()->contains($character)) { + throw new BadRequestException("Character is already registered!"); + } + + $registration->tournament->addCharacter($registration->character); + $this->em->persist($registration->tournament); + $this->em->persist($registration->character); + $this->em->flush(); + } +} diff --git a/src/Validator/CharacterOwned.php b/src/Validator/CharacterOwned.php new file mode 100644 index 0000000..2d5c11b --- /dev/null +++ b/src/Validator/CharacterOwned.php @@ -0,0 +1,12 @@ +getDojo() + ->getOwner() + ->getId() != $this->security->getUser()->getUserIdentifier()) { + $this->context->buildViolation($constraint->invalidOwnerMessage)->addViolation(); + } + } +} + diff --git a/src/Validator/StartDateInFuture.php b/src/Validator/StartDateInFuture.php new file mode 100644 index 0000000..7e8728f --- /dev/null +++ b/src/Validator/StartDateInFuture.php @@ -0,0 +1,12 @@ +getStartDate() < new DateTimeImmutable("now", new DateTimeZone("UTC"))) { + $this->context->buildViolation($constraint->invalidStartDateMessage)->addViolation(); + } + } +} + diff --git a/tests/AbstractTest.php b/tests/AbstractTest.php index bb5f96b..7ce59bd 100644 --- a/tests/AbstractTest.php +++ b/tests/AbstractTest.php @@ -4,6 +4,7 @@ namespace App\Tests; use ApiPlatform\Symfony\Bundle\Test\ApiTestCase; use ApiPlatform\Symfony\Bundle\Test\Client; use App\Entity\Thing; +use Doctrine\ORM\EntityManagerInterface; use Zenstruck\Foundry\Proxy; use Zenstruck\Foundry\Test\Factories; use Zenstruck\Foundry\Test\ResetDatabase; @@ -48,5 +49,10 @@ abstract class AbstractTest extends ApiTestCase } return $this->getIriFromResource($thing); } + + protected function getEntityManager(): EntityManagerInterface + { + return static::$kernel->getContainer()->get('doctrine.orm.entity_manager'); + } } diff --git a/tests/TournamentTest.php b/tests/TournamentTest.php index 54d0b72..37b2ec8 100644 --- a/tests/TournamentTest.php +++ b/tests/TournamentTest.php @@ -1,29 +1,53 @@ sub(\DateInterval::createFromDateString($offset)); + return TournamentFactory::createOne([ + 'startDate' => $tournamentStartDate, + 'characters' => $characters + ]); + } + + private function createCharacter(User|Proxy $user): Character|Proxy + { $dojo = DojoFactory::createOne([ - 'owner' => UserFactory::createOne() + 'owner' => $user ]); $character = CharacterFactory::createOne([ 'dojo' => $dojo ]); - $response = static::createClientWithToken($dojo->getOwner()->authName)->request('POST', - '/api/tournament_registrations', + return $character; + } + + public function testRegisterCharacter(): void + { + $tournament = $this->createTournament("-5 min"); + $user = UserFactory::createOne(); + $character = $this->createCharacter($user); + + static::createClientWithToken($user->authName)->request('POST', '/api/tournament_registrations', [ 'json' => [ 'tournament' => $this->getIri($tournament), @@ -32,40 +56,142 @@ class TournamentTest extends AbstractTest ]); $this->assertResponseStatusCodeSame(201); + } + + public function testRegisterCharacterDifferentUser(): void + { + $tournament = $this->createTournament("-5 min"); + $characterOwner = UserFactory::createOne(); + $character = $this->createCharacter($characterOwner); + $user = UserFactory::createOne(); - $this->assertArrayHasKey('id', $response->toArray()); + static::createClientWithToken($user->authName)->request('POST', '/api/tournament_registrations', + [ + 'json' => [ + 'tournament' => $this->getIri($tournament), + 'character' => $this->getIri($character) + ] + ]); + + $this->assertResponseStatusCodeSame(422); + } + + public function testRegisterCharacterOnPastTournament(): void + { + $tournament = $this->createTournament("5 min"); + $user = UserFactory::createOne(); + $character = $this->createCharacter($user); + + static::createClientWithToken($user->authName)->request('POST', '/api/tournament_registrations', + [ + 'json' => [ + 'tournament' => $this->getIri($tournament), + 'character' => $this->getIri($character) + ] + ]); + + $this->assertResponseStatusCodeSame(422); } public function testRegisterCharacterNotPossibleTwice(): void - {} + { + $tournament = $this->createTournament("-5 min"); + $user = UserFactory::createOne(); + $character = $this->createCharacter($user); - public function testRegisterCharacterOnPastTournament(): void - {} + static::createClientWithToken($user->authName)->request('POST', '/api/tournament_registrations', + [ + 'json' => [ + 'tournament' => $this->getIri($tournament), + 'character' => $this->getIri($character) + ] + ]); + + static::createClientWithToken($user->authName)->request('POST', '/api/tournament_registrations', + [ + 'json' => [ + 'tournament' => $this->getIri($tournament), + 'character' => $this->getIri($character) + ] + ]); + + $this->assertEquals(1, + $this->getEntityManager() + ->find(Tournament::class, $tournament->getId()) + ->getCharacters() + ->count()); + $this->assertResponseStatusCodeSame(400); + } public function testShowRegisteredCharacters(): void - {} + { + $tournament = $this->createTournament("-5 min", CharacterFactory::createMany(4)); + + $response = static::createClientWithToken()->request('GET', $this->getIri($tournament)); + $this->assertResponseStatusCodeSame(200); + $this->assertCount(4, $response->toArray()['characters']); + } public function testListTournaments(): void - {} + { + TournamentFactory::createMany(5); + $response = static::createClientWithToken()->request('GET', '/api/tournaments'); + $this->assertResponseStatusCodeSame(200); + $this->assertCount(5, $response->toArray()); + } /** * Status is ... * meta data like when it is starting, name, "location", Winner (nullable), etc. */ public function testTournamentStatus(): void - {} + { + $tournament = TournamentFactory::createOne([ + 'winner' => CharacterFactory::createOne() + ]); + $response = static::createClientWithToken()->request('GET', $this->getIri($tournament)); + $this->assertResponseStatusCodeSame(200); + + $arrayResponse = $response->toArray(); + $this->assertArrayHasKey('winner', $arrayResponse); + } - // /api/tournament/{id}/fights - // readableLink: true -> participant ids and winner public function testTournamentFights(): void - {} + { + $tournament = TournamentFactory::createOne([ + 'fights' => FightFactory::createMany(16) + ]); + + $response = static::createClientWithToken()->request('GET', $this->getIri($tournament) . '/fights'); + $this->assertResponseStatusCodeSame(200); + $this->assertCount(16, $response->toArray()); + } - // /api/tournament/{id}/character/{id}/fights public function testTournamentFightsForCharacter(): void - {} + { + $characters = CharacterFactory::createMany(16); + $tournament = TournamentFactory::createOne([ + 'characters' => $characters + ]); - // /api/tournament/{id}/ranking - public function testTournamentRanking(): void - {} + $winner = $characters[0]; + for ($i = 1; $i < count($characters); ++ $i) { + $tournament->addFight( + FightFactory::createOne([ + 'winner' => $winner, + 'characters' => array( + $winner, + $characters[$i] + ) + ])->object()); + } + $tournament->save(); + + $response = static::createClientWithToken()->request('GET', + $this->getIri($tournament) . '/characters/' . $winner->getId() + ->toBase32() . '/fights'); + + $this->assertCount(15, $response->toArray()); + } }