From d76534d6a36b28ff2c3b4d91da7617b0acf23f80 Mon Sep 17 00:00:00 2001 From: Hecht Date: Tue, 5 Mar 2024 20:57:35 +0100 Subject: [PATCH] Fixes on API level (finally a character can be created) --- config/api_platform/resources.xml | 17 +- migrations/Version20240129212818.php | 31 ---- migrations/Version20240219220159.php | 112 +++++++++++++ src/Controller/GetUserDojo.php | 21 +++ src/Entity/Character.php | 103 +++++++----- src/Entity/Technique.php | 8 +- src/Entity/Thing.php | 7 + src/Entity/User.php | 2 +- src/Factory/CharacterFactory.php | 3 +- src/Repository/TechniqueRepository.php | 24 +++ src/Validator/CharacterStats.php | 16 ++ src/Validator/CharacterStatsValidator.php | 32 ++++ tests/AbstractTest.php | 42 +++++ tests/CharacterTest.php | 181 ++++++++++++++++------ tests/DojoTest.php | 102 +++++++----- 15 files changed, 539 insertions(+), 162 deletions(-) delete mode 100644 migrations/Version20240129212818.php create mode 100644 migrations/Version20240219220159.php create mode 100644 src/Controller/GetUserDojo.php create mode 100644 src/Repository/TechniqueRepository.php create mode 100644 src/Validator/CharacterStats.php create mode 100644 src/Validator/CharacterStatsValidator.php create mode 100644 tests/AbstractTest.php diff --git a/config/api_platform/resources.xml b/config/api_platform/resources.xml index 5f6a9db..2885aa3 100644 --- a/config/api_platform/resources.xml +++ b/config/api_platform/resources.xml @@ -77,6 +77,7 @@ + @@ -108,7 +109,6 @@ public - @@ -118,4 +118,19 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/migrations/Version20240129212818.php b/migrations/Version20240129212818.php deleted file mode 100644 index da5f16d..0000000 --- a/migrations/Version20240129212818.php +++ /dev/null @@ -1,31 +0,0 @@ -addSql('CREATE SCHEMA public'); - } -} diff --git a/migrations/Version20240219220159.php b/migrations/Version20240219220159.php new file mode 100644 index 0000000..f3fa10f --- /dev/null +++ b/migrations/Version20240219220159.php @@ -0,0 +1,112 @@ +addSql('CREATE TABLE "character" (id UUID NOT NULL, dojo_id UUID DEFAULT NULL, name VARCHAR(255) NOT NULL, strength INT NOT NULL, constitution INT NOT NULL, agility INT NOT NULL, chi INT NOT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE INDEX IDX_937AB03432F09E9C ON "character" (dojo_id)'); + $this->addSql('COMMENT ON COLUMN "character".id IS \'(DC2Type:ulid)\''); + $this->addSql('COMMENT ON COLUMN "character".dojo_id IS \'(DC2Type:ulid)\''); + $this->addSql('CREATE TABLE character_technique (character_id UUID NOT NULL, technique_id UUID NOT NULL, PRIMARY KEY(character_id, technique_id))'); + $this->addSql('CREATE INDEX IDX_506B3A7A1136BE75 ON character_technique (character_id)'); + $this->addSql('CREATE INDEX IDX_506B3A7A1F8ACB26 ON character_technique (technique_id)'); + $this->addSql('COMMENT ON COLUMN character_technique.character_id IS \'(DC2Type:ulid)\''); + $this->addSql('COMMENT ON COLUMN character_technique.technique_id IS \'(DC2Type:ulid)\''); + $this->addSql('CREATE TABLE city (id UUID NOT NULL, dungeon_id UUID NOT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_2D5B0234B606863 ON city (dungeon_id)'); + $this->addSql('COMMENT ON COLUMN city.id IS \'(DC2Type:ulid)\''); + $this->addSql('COMMENT ON COLUMN city.dungeon_id IS \'(DC2Type:ulid)\''); + $this->addSql('CREATE TABLE country (id UUID NOT NULL, capital_id UUID NOT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_5373C966FC2D9FF7 ON country (capital_id)'); + $this->addSql('COMMENT ON COLUMN country.id IS \'(DC2Type:ulid)\''); + $this->addSql('COMMENT ON COLUMN country.capital_id IS \'(DC2Type:ulid)\''); + $this->addSql('CREATE TABLE dojo (id UUID NOT NULL, village_id UUID DEFAULT NULL, owner_id UUID NOT NULL, name VARCHAR(255) NOT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_9494CCB15E237E06 ON dojo (name)'); + $this->addSql('CREATE INDEX IDX_9494CCB15E0D5582 ON dojo (village_id)'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_9494CCB17E3C61F9 ON dojo (owner_id)'); + $this->addSql('COMMENT ON COLUMN dojo.id IS \'(DC2Type:ulid)\''); + $this->addSql('COMMENT ON COLUMN dojo.village_id IS \'(DC2Type:ulid)\''); + $this->addSql('COMMENT ON COLUMN dojo.owner_id IS \'(DC2Type:ulid)\''); + $this->addSql('CREATE TABLE dungeon (id UUID NOT NULL, city_id UUID NOT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_3FFA1F908BAC62AF ON dungeon (city_id)'); + $this->addSql('COMMENT ON COLUMN dungeon.id IS \'(DC2Type:ulid)\''); + $this->addSql('COMMENT ON COLUMN dungeon.city_id IS \'(DC2Type:ulid)\''); + $this->addSql('CREATE TABLE prefecture (id UUID NOT NULL, capital_id UUID NOT NULL, country_id UUID NOT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_ABE6511AFC2D9FF7 ON prefecture (capital_id)'); + $this->addSql('CREATE INDEX IDX_ABE6511AF92F3E70 ON prefecture (country_id)'); + $this->addSql('COMMENT ON COLUMN prefecture.id IS \'(DC2Type:ulid)\''); + $this->addSql('COMMENT ON COLUMN prefecture.capital_id IS \'(DC2Type:ulid)\''); + $this->addSql('COMMENT ON COLUMN prefecture.country_id IS \'(DC2Type:ulid)\''); + $this->addSql('CREATE TABLE technique (id UUID NOT NULL, prerequisite_id UUID DEFAULT NULL, costs INT NOT NULL, damage VARCHAR(255) NOT NULL, energy VARCHAR(255) NOT NULL, accuracy VARCHAR(255) NOT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_D73B9841276AF86B ON technique (prerequisite_id)'); + $this->addSql('COMMENT ON COLUMN technique.id IS \'(DC2Type:ulid)\''); + $this->addSql('COMMENT ON COLUMN technique.prerequisite_id IS \'(DC2Type:ulid)\''); + $this->addSql('CREATE TABLE "user" (id UUID NOT NULL, dojo_id UUID DEFAULT NULL, auth_name VARCHAR(255) NOT NULL, properties JSON NOT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_8D93D64932F09E9C ON "user" (dojo_id)'); + $this->addSql('COMMENT ON COLUMN "user".id IS \'(DC2Type:ulid)\''); + $this->addSql('COMMENT ON COLUMN "user".dojo_id IS \'(DC2Type:ulid)\''); + $this->addSql('CREATE TABLE village (id UUID NOT NULL, prefecture_id UUID NOT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE INDEX IDX_4E6C7FAA9D39C865 ON village (prefecture_id)'); + $this->addSql('COMMENT ON COLUMN village.id IS \'(DC2Type:ulid)\''); + $this->addSql('COMMENT ON COLUMN village.prefecture_id IS \'(DC2Type:ulid)\''); + $this->addSql('ALTER TABLE "character" ADD CONSTRAINT FK_937AB03432F09E9C FOREIGN KEY (dojo_id) REFERENCES dojo (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE character_technique ADD CONSTRAINT FK_506B3A7A1136BE75 FOREIGN KEY (character_id) REFERENCES "character" (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE character_technique ADD CONSTRAINT FK_506B3A7A1F8ACB26 FOREIGN KEY (technique_id) REFERENCES technique (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE city ADD CONSTRAINT FK_2D5B0234B606863 FOREIGN KEY (dungeon_id) REFERENCES dungeon (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE country ADD CONSTRAINT FK_5373C966FC2D9FF7 FOREIGN KEY (capital_id) REFERENCES city (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE dojo ADD CONSTRAINT FK_9494CCB15E0D5582 FOREIGN KEY (village_id) REFERENCES village (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE dojo ADD CONSTRAINT FK_9494CCB17E3C61F9 FOREIGN KEY (owner_id) REFERENCES "user" (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE dungeon ADD CONSTRAINT FK_3FFA1F908BAC62AF FOREIGN KEY (city_id) REFERENCES city (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE prefecture ADD CONSTRAINT FK_ABE6511AFC2D9FF7 FOREIGN KEY (capital_id) REFERENCES city (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE prefecture ADD CONSTRAINT FK_ABE6511AF92F3E70 FOREIGN KEY (country_id) REFERENCES country (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE technique ADD CONSTRAINT FK_D73B9841276AF86B FOREIGN KEY (prerequisite_id) REFERENCES technique (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE "user" ADD CONSTRAINT FK_8D93D64932F09E9C FOREIGN KEY (dojo_id) REFERENCES dojo (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE village ADD CONSTRAINT FK_4E6C7FAA9D39C865 FOREIGN KEY (prefecture_id) REFERENCES prefecture (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('CREATE SCHEMA public'); + $this->addSql('ALTER TABLE "character" DROP CONSTRAINT FK_937AB03432F09E9C'); + $this->addSql('ALTER TABLE character_technique DROP CONSTRAINT FK_506B3A7A1136BE75'); + $this->addSql('ALTER TABLE character_technique DROP CONSTRAINT FK_506B3A7A1F8ACB26'); + $this->addSql('ALTER TABLE city DROP CONSTRAINT FK_2D5B0234B606863'); + $this->addSql('ALTER TABLE country DROP CONSTRAINT FK_5373C966FC2D9FF7'); + $this->addSql('ALTER TABLE dojo DROP CONSTRAINT FK_9494CCB15E0D5582'); + $this->addSql('ALTER TABLE dojo DROP CONSTRAINT FK_9494CCB17E3C61F9'); + $this->addSql('ALTER TABLE dungeon DROP CONSTRAINT FK_3FFA1F908BAC62AF'); + $this->addSql('ALTER TABLE prefecture DROP CONSTRAINT FK_ABE6511AFC2D9FF7'); + $this->addSql('ALTER TABLE prefecture DROP CONSTRAINT FK_ABE6511AF92F3E70'); + $this->addSql('ALTER TABLE technique DROP CONSTRAINT FK_D73B9841276AF86B'); + $this->addSql('ALTER TABLE "user" DROP CONSTRAINT FK_8D93D64932F09E9C'); + $this->addSql('ALTER TABLE village DROP CONSTRAINT FK_4E6C7FAA9D39C865'); + $this->addSql('DROP TABLE "character"'); + $this->addSql('DROP TABLE character_technique'); + $this->addSql('DROP TABLE city'); + $this->addSql('DROP TABLE country'); + $this->addSql('DROP TABLE dojo'); + $this->addSql('DROP TABLE dungeon'); + $this->addSql('DROP TABLE prefecture'); + $this->addSql('DROP TABLE technique'); + $this->addSql('DROP TABLE "user"'); + $this->addSql('DROP TABLE village'); + } +} diff --git a/src/Controller/GetUserDojo.php b/src/Controller/GetUserDojo.php new file mode 100644 index 0000000..d6607b2 --- /dev/null +++ b/src/Controller/GetUserDojo.php @@ -0,0 +1,21 @@ +getRepository(Dojo::class)->findOneByOwner($this->security->getUser()); + } +} + diff --git a/src/Entity/Character.php b/src/Entity/Character.php index 58569f6..41231bd 100644 --- a/src/Entity/Character.php +++ b/src/Entity/Character.php @@ -1,6 +1,8 @@ getStrength()); + $costs += self::skillCosts($this->getConstitution()); + $costs += self::skillCosts($this->getAgility()); + $costs += self::skillCosts($this->getChi()); + + foreach ($this->getTechniques() as $tech) { + $costs += $tech->getCosts(); + } + return $available - $costs; } - public function getDojo(): ?Dojo + public static function loadValidatorMetadata(ClassMetadata $metadata): void { - return $this->dojo; + $metadata->addConstraint(new CharacterStats()); } - public function setDojo(?Dojo $dojo): static + /** + * Calculates the aged based on the ulid value? + */ + #[Groups('public')] + public function getAge(): int { - $this->dojo = $dojo; + return 21; + } - return $this; + public static function skillCosts(int $value) + { + return pow(2, $value); } public function getName(): ?string @@ -92,14 +118,14 @@ class Character extends Thing return $this; } - public function getConstition(): ?int + public function getConstitution(): ?int { - return $this->constition; + return $this->constitution; } - public function setConstition(int $constition): static + public function setConstitution(int $constitution): static { - $this->constition = $constition; + $this->constitution = $constitution; return $this; } @@ -116,45 +142,48 @@ class Character extends Thing return $this; } - /** - * @return Collection - */ - public function getTechniques(): Collection + public function getChi(): ?int { - return $this->techniques; + return $this->chi; } - public function setTechniques(string $techniques): static + public function setChi(int $chi): static { - $this->techniques = $techniques; + $this->chi = $chi; return $this; } - public function addTechnique(Technique $technique): static + public function getDojo(): ?Dojo { - if (!$this->techniques->contains($technique)) { - $this->techniques->add($technique); - } - - return $this; + return $this->dojo; } - public function removeTechnique(Technique $technique): static + public function setDojo(?Dojo $dojo): static { - $this->techniques->removeElement($technique); + $this->dojo = $dojo; return $this; } - public function getConstitution(): ?int + /** + * + * @return Technique[] + */ + public function getTechniques(): mixed { - return $this->constitution; + return $this->techniques->getValues(); } - public function setConstitution(int $constitution): static + public function addTechnique(Technique $technique): static { - $this->constitution = $constitution; + $this->techniques[] = $technique; + return $this; + } + + public function removeTechnique(Technique $technique): static + { + $this->techniques->removeElement($technique); return $this; } diff --git a/src/Entity/Technique.php b/src/Entity/Technique.php index b16d980..340d6b5 100644 --- a/src/Entity/Technique.php +++ b/src/Entity/Technique.php @@ -6,7 +6,7 @@ use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\JoinColumn; use Doctrine\ORM\Mapping\OneToOne; -#[Entity] +#[Entity(repositoryClass: 'App\Repository\TechniqueRepository')] class Technique extends Thing { @@ -17,19 +17,19 @@ class Technique extends Thing public int $costs; /** - * Forumula to calculate the damage based on the stats. + * Formula to calculate the damage based on the stats. */ #[Column] public string $damage; /** - * Forumula to calculate the damage based on the stats. + * Formula to calculate the consumed energy on use based on the stats. */ #[Column] public string $energy; /** - * Forumula to calculate the damage based on the stats. + * Formula to calculate the hit chance accuracy based on the stats. */ #[Column] public string $accuracy; diff --git a/src/Entity/Thing.php b/src/Entity/Thing.php index 4d19bea..f883cdb 100644 --- a/src/Entity/Thing.php +++ b/src/Entity/Thing.php @@ -3,6 +3,7 @@ namespace App\Entity; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Types\UlidType; +use Symfony\Component\Serializer\Attribute\Groups; use Symfony\Component\Uid\Ulid; abstract class Thing @@ -12,6 +13,12 @@ abstract class Thing #[ORM\Column(type: UlidType::NAME, unique: true)] #[ORM\GeneratedValue(strategy: 'CUSTOM')] #[ORM\CustomIdGenerator(class: 'doctrine.ulid_generator')] + #[Groups('public')] public ?Ulid $id; + + public function getId(): ?Ulid + { + return $this->id; + } } diff --git a/src/Entity/User.php b/src/Entity/User.php index e50aa35..c22d298 100644 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -9,7 +9,7 @@ use Doctrine\ORM\Mapping\OneToOne; use Doctrine\ORM\Mapping\Table; use Symfony\Component\Security\Core\User\UserInterface; -#[Entity(repositoryClass: "App\Repository\UserRepository")] +#[Entity(repositoryClass: 'App\Repository\UserRepository')] #[Table(name: '`user`')] class User extends Thing implements UserInterface { diff --git a/src/Factory/CharacterFactory.php b/src/Factory/CharacterFactory.php index bf9cdbb..0b61aae 100644 --- a/src/Factory/CharacterFactory.php +++ b/src/Factory/CharacterFactory.php @@ -47,10 +47,11 @@ final class CharacterFactory extends ModelFactory protected function getDefaults(): array { return [ - 'name' => self::faker()->text(), + 'name' => self::faker()->firstName(), 'strength' => self::faker()->numberBetween(1, 4), 'constitution' => self::faker()->numberBetween(1, 4), 'agility' => self::faker()->numberBetween(1, 4), + 'chi' => self::faker()->numberBetween(1, 4), 'techniques' => TechniqueFactory::createMany(2) ]; } diff --git a/src/Repository/TechniqueRepository.php b/src/Repository/TechniqueRepository.php new file mode 100644 index 0000000..d8a97c3 --- /dev/null +++ b/src/Repository/TechniqueRepository.php @@ -0,0 +1,24 @@ + + * + * @method Technique|null find($id, $lockMode = null, $lockVersion = null) + * @method Technique|null findOneBy(array $criteria, array $orderBy = null) + * @method Technique[] findAll() + * @method Technique[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) + */ +class TechniqueRepository extends ServiceEntityRepository +{ + + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, Technique::class); + } +} diff --git a/src/Validator/CharacterStats.php b/src/Validator/CharacterStats.php new file mode 100644 index 0000000..bc1cee6 --- /dev/null +++ b/src/Validator/CharacterStats.php @@ -0,0 +1,16 @@ +getFreeSkillPoints() < 0) { + $this->context->buildViolation($constraint->noMoreSkillPointsMessage) + ->atPath('character.freeSkillPoints') + ->addViolation(); + } + } +} + diff --git a/tests/AbstractTest.php b/tests/AbstractTest.php new file mode 100644 index 0000000..f110a1f --- /dev/null +++ b/tests/AbstractTest.php @@ -0,0 +1,42 @@ +format("c"); + + return sodium_bin2base64(sodium_crypto_sign($message, $sign_secret), SODIUM_BASE64_VARIANT_URLSAFE); + } + + protected static function createClientWithToken($authName = null): Client + { + return static::createClient([], + [ + 'headers' => [ + 'accept' => 'application/json', + 'X-AUTH-TOKEN' => static::generateAuthToken($authName) + ] + ]); + } +} + diff --git a/tests/CharacterTest.php b/tests/CharacterTest.php index 627202a..c3fa502 100644 --- a/tests/CharacterTest.php +++ b/tests/CharacterTest.php @@ -1,28 +1,13 @@ format("c"); - - return sodium_bin2base64(sodium_crypto_sign($message, $sign_secret), SODIUM_BASE64_VARIANT_URLSAFE); - } /** * Requirement: A user should be able see all characters from a dojo, but only the public fields! @@ -36,65 +21,173 @@ class CharacterTest extends ApiTestCase ]); CharacterFactory::createMany(10); - $response = static::createClient()->request('GET', '/api/dojo/' . $dojo->id . '/characters', - [ - 'headers' => [ - 'accept' => 'application/json', - 'X-AUTH-TOKEN' => $this->generateAuthToken($requestUser->authName) - ] - ]); + $response = static::createClientWithToken($requestUser->authName)->request('GET', + '/api/dojo/' . $dojo->id . '/characters'); $this->assertResponseStatusCodeSame(200); + $this->assertNotEquals("[[],[],[],[]]", $response->getContent()); + // Because test fixtures are automatically loaded between each test, you can assert on them $this->assertCount(4, $response->toArray()); - $this->assertNotEquals("[[],[],[],[]]", $response->getContent()); - $chars = $response->toArray(); + $this->assertEquals(4, count($chars[0])); + $this->assertArrayHasKey('id', $chars[0]); $this->assertArrayHasKey('name', $chars[0]); - $this->assertArrayHasKey('dojo', $chars[0]); + $this->assertEquals('/api/dojos/' . $dojo->getId() + ->toBase32(), $chars[0]['dojo']); + $this->assertArrayHasKey('age', $chars[0]); + $this->assertArrayNotHasKey('freeSkillPoints', $chars[0]); $this->assertArrayNotHasKey('strength', $chars[0]); // not accessible via this route $this->assertArrayNotHasKey('constitution', $chars[0]); // not accessible via this route $this->assertArrayNotHasKey('agility', $chars[0]); // not accessible via this route + $this->assertArrayNotHasKey('chi', $chars[0]); // not accessible via this route $this->assertArrayNotHasKey('techniques', $chars[0]); // not accessible via this route } /** - * Requirement: A user should be able see all characters from a dojo, but only the public fields! + * Requirement: A user should be able see all of the characters from his dojo, not restricted by public fields! */ public function testRetrieveCharactersFromOwnDojoDetail(): void { $dojo = DojoFactory::createOne([ 'owner' => UserFactory::createOne() ]); - CharacterFactory::createMany(4, [ - 'dojo' => $dojo + + $technique = TechniqueFactory::createOne(); + + $foo = CharacterFactory::createMany(4, [ + 'dojo' => $dojo, + 'techniques' => [ + $technique + ] ]); + + $this->assertEquals(1, count($foo[0]->getTechniques())); + CharacterFactory::createMany(10); - $response = static::createClient()->request('GET', '/api/dojo/characters', - [ - 'headers' => [ - 'accept' => 'application/json', - 'X-AUTH-TOKEN' => $this->generateAuthToken($dojo->getOwner()->authName) - ] - ]); + $response = static::createClientWithToken($dojo->getOwner()->authName)->request('GET', '/api/dojo/characters'); $this->assertResponseStatusCodeSame(200); - // Because test fixtures are automatically loaded between each test, you can assert on them - $this->assertCount(4, $response->toArray()); - $this->assertNotEquals("[[],[],[],[]]", $response->getContent()); $chars = $response->toArray(); + $this->assertEquals(10, count($chars[0])); + $this->assertArrayHasKey('id', $chars[0]); $this->assertArrayHasKey('name', $chars[0]); $this->assertArrayHasKey('dojo', $chars[0]); - $this->assertArrayHasKey('strength', $chars[0]); // not accessible via this route - $this->assertArrayHasKey('constitution', $chars[0]); // not accessible via this route - $this->assertArrayHasKey('agility', $chars[0]); // not accessible via this route - $this->assertArrayHasKey('techniques', $chars[0]); // not accessible via this route + $this->assertEquals('/api/dojos/' . $dojo->getId() + ->toBase32(), $chars[0]['dojo']); + $this->assertArrayHasKey('age', $chars[0]); + $this->assertArrayHasKey('freeSkillPoints', $chars[0]); + $this->assertArrayHasKey('strength', $chars[0]); + $this->assertArrayHasKey('constitution', $chars[0]); + $this->assertArrayHasKey('agility', $chars[0]); + $this->assertArrayHasKey('chi', $chars[0]); + $this->assertArrayHasKey('techniques', $chars[0]); + $this->assertEquals('/api/techniques/' . $technique->getId() + ->toBase32(), $chars[0]['techniques'][0]); + } + + /** + * Requirement: MVP only (in the future the recuitment will be different). + * A user should be able to create a single character. + */ + public function testCreateCharacter(): void + { + $dojo = DojoFactory::createOne([ + 'owner' => UserFactory::createOne() + ]); + + $tech = TechniqueFactory::createOne([ + 'prerequisite' => NULL + ]); + + $response = static::createClientWithToken($dojo->getOwner()->authName)->request('POST', '/api/characters', + [ + 'json' => [ + 'name' => 'Dude', + 'strength' => 1, + 'constitution' => 1, + 'agility' => 1, + 'chi' => 1, + 'techniques' => [ + '/api/techniques/' . $tech->getId() + ->toBase32() + ] + ] + ]); + + $this->assertResponseStatusCodeSame(201); + + $this->assertArrayHasKey('id', $response->toArray()); + } + + /** + * Requirement: MVP only (in the future the recuitment will be different). + * A user should NOT be able spent more skill points than available. + */ + public function testCreateCharacterStatsTooHigh(): void + { + $dojo = DojoFactory::createOne([ + 'owner' => UserFactory::createOne() + ]); + + $tech = TechniqueFactory::createOne([ + 'prerequisite' => NULL + ]); + + static::createClientWithToken($dojo->getOwner()->authName)->request('POST', '/api/characters', + [ + 'json' => [ + 'name' => 'Dude', + 'strength' => 99, + 'constitution' => 1, + 'agility' => 1, + 'chi' => 1, + 'techniques' => [ + '/api/techniques/' . $tech->getId() + ->toBase32() + ] + ] + ]); + + $this->assertResponseStatusCodeSame(422); + } + + /** + * Requirement: MVP only (in the future the recuitment will be different). + * A user should NOT be able spent more skill points than available. + */ + public function testCreateCharacterTooExpensiveTechnique(): void + { + $dojo = DojoFactory::createOne([ + 'owner' => UserFactory::createOne() + ]); + + $tech = TechniqueFactory::createOne([ + 'costs' => 99 + ]); + + static::createClientWithToken($dojo->getOwner()->authName)->request('POST', '/api/characters', + [ + 'json' => [ + 'name' => 'Dude', + 'strength' => 1, + 'constitution' => 1, + 'agility' => 1, + 'chi' => 1, + 'techniques' => [ + '/api/techniques/' . $tech->getId() + ->toBase32() + ] + ] + ]); + + $this->assertResponseStatusCodeSame(422); } } diff --git a/tests/DojoTest.php b/tests/DojoTest.php index 0975c82..8d0520c 100644 --- a/tests/DojoTest.php +++ b/tests/DojoTest.php @@ -1,30 +1,12 @@ format("c"); - - return sodium_bin2base64(sodium_crypto_sign($message, $sign_secret), SODIUM_BASE64_VARIANT_URLSAFE); - } /** * Requirement: A user should be able to create a dojo! @@ -37,22 +19,63 @@ class DojoTest extends ApiTestCase $this->assertCount(0, $userRepository->findByAuthName($userName)); - static::createClient()->request('POST', '/api/dojos', - [ - 'headers' => [ - 'accept' => 'application/json', - 'X-AUTH-TOKEN' => $this->generateAuthToken($userName) - ], - 'json' => [ - 'name' => $dojoName - ] - ]); + static::createClientWithToken($userName)->request('POST', '/api/dojos', [ + 'json' => [ + 'name' => $dojoName + ] + ]); $this->assertResponseStatusCodeSame(201); $this->assertCount(1, $userRepository->findByAuthName($userName)); } + /** + * Requirement: A user should be request his own dojo! + */ + public function testUserReadDojo(): void + { + $userName = "FooBarFigher"; + $dojoName = "BigFightDojo"; + $dojo = DojoFactory::createOne( + [ + 'name' => $dojoName, + 'owner' => UserFactory::createOne([ + 'authName' => $userName + ]) + ]); + + static::createClientWithToken($userName)->request('GET', '/api/dojo'); + + $this->assertResponseStatusCodeSame(200); + + $this->assertJsonContains( + [ + 'name' => 'BigFightDojo', + 'members' => [], + 'owner' => '/api/users/' . $dojo->owner->getId() + ->toBase32(), + 'id' => $dojo->getId() + ->toBase32() + ]); + } + + /** + * Requirement: A user should be request his own dojo! + * Fails, if the dojo is not yet created. + */ + public function testUserReadDojoNotYetCreated(): void + { + $userName = "FooBarFigher"; + $userRepository = $this->getContainer()->get(UserRepository::class); + + $this->assertCount(0, $userRepository->findByAuthName($userName)); + + static::createClientWithToken($userName)->request('GET', '/api/dojo'); + + $this->assertResponseStatusCodeSame(404); + } + /** * Requirement: A user should NOT be able to create more than one dojos! */ @@ -67,16 +90,11 @@ class DojoTest extends ApiTestCase ]) ]); - static::createClient()->request('POST', '/api/dojos', - [ - 'headers' => [ - 'accept' => 'application/json', - 'X-AUTH-TOKEN' => $this->generateAuthToken($userName) - ], - 'json' => [ - 'name' => $dojoName - ] - ]); + static::createClientWithToken($userName)->request('POST', '/api/dojos', [ + 'json' => [ + 'name' => $dojoName + ] + ]); $this->assertResponseStatusCodeSame(409); // 409 Conflict } @@ -98,12 +116,10 @@ class DojoTest extends ApiTestCase ]) ]); - static::createClient()->request('PATCH', '/api/dojos/' . $dojo->id, + static::createClientWithToken($userName)->request('PATCH', '/api/dojos/' . $dojo->id, [ 'headers' => [ - 'content-type' => 'application/merge-patch+json', - 'accept' => 'application/json', - 'X-AUTH-TOKEN' => $this->generateAuthToken($userName) + 'content-type' => 'application/merge-patch+json' ], 'json' => [ 'name' => $newDojoName