From b01104b68fcdc9c29bdcc06183f56ad942db91fd Mon Sep 17 00:00:00 2001 From: Teddy Roncin Date: Fri, 3 Feb 2023 17:48:57 +0100 Subject: [PATCH] :pencil: (README and code) Wrote documentation Documented new folders in the tests/ folder in the README file. Documented class EtuUTTApiTestCase (and its methods). Documented test classes --- README.md | 2 + tests/EtuUTTApiTestCase.php | 103 +++++++++++++++++++++++++++++- tests/Groups/GetGroupFromSlug.php | 2 + tests/Groups/GetGroups.php | 2 + tests/Groups/UpdateGroup.php | 2 + tests/Users/DeleteUser.php | 2 + tests/Users/GetUserFromId.php | 2 + tests/Users/GetUsers.php | 2 + tests/Users/UpdateUser.php | 2 + 9 files changed, 117 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 17ff0ea..ea4da08 100644 --- a/README.md +++ b/README.md @@ -152,6 +152,8 @@ Once the local server is running, go to [http://127.0.0.1:8000/](http://127.0.0. | ├── Security/ # Login process and Voters | └── Util/ # Static classes to centralize simple tasks ├── tests/ # Automated tests (e.g. Unit tests) +| └── Groups/ # Tests for /groups routes +| └── Users/ # Tests for /users routes ├── var/ # Generated files (cache, logs, etc) ├── vendor/ # The third-party dependencies ├── .czrc # Git Commitizen configuration file diff --git a/tests/EtuUTTApiTestCase.php b/tests/EtuUTTApiTestCase.php index 640ea7e..d6481c8 100644 --- a/tests/EtuUTTApiTestCase.php +++ b/tests/EtuUTTApiTestCase.php @@ -12,14 +12,24 @@ use Doctrine\Common\DataFixtures\Purger\ORMPurger; use Doctrine\ORM\EntityManager; use Symfony\Bridge\Doctrine\Types\UuidType; +use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Uid\Uuid; +/** + * Base class for all tests of the project. + * It provides some helper methods. + */ abstract class EtuUTTApiTestCase extends ApiTestCase { protected EntityManager $em; protected User $user; private array $databaseBackup; + /** + * Initializes $this->em and $this->user. + * $this->user is an admin user. Its login is 'test'. Tests may use it to connect to the API. + * It also purges the database. + */ protected function setUp(): void { $this->em = static::getContainer()->get('doctrine.orm.entity_manager'); @@ -28,6 +38,12 @@ protected function setUp(): void $this->user = $this->createUser('test', 'test', 'test', 'ROLE_ADMIN'); } + /** + * Asserts that the received group is the same as the expected one. It should have the layout defined by the {@see Groups} 'group:read:some' in the {@see Group} entity. + * + * @param Group $expected The group that should be received + * @param mixed $actual The group that was received (as an stdClass) + */ protected static function assertSameGroupReadSome(Group $expected, mixed $actual): void { static::assertIsObject($actual); @@ -43,6 +59,12 @@ protected static function assertSameGroupReadSome(Group $expected, mixed $actual static::assertSame($expected->getNumberOfMembers(), $actual->numberOfMembers); } + /** + * Asserts that the received group is the same as the expected one. It should have the layout defined by the {@see Groups} 'group:read:one' in the {@see Group} entity. + * + * @param Group $expected The group that should be received + * @param mixed $actual The group that was received (as an stdClass) + */ protected static function assertSameGroupReadOne(Group $expected, mixed $actual): void { static::assertIsObject($actual); @@ -66,6 +88,12 @@ protected static function assertSameGroupReadOne(Group $expected, mixed $actual) static::assertSame($expected->getUpdatedAt()->format(\DateTimeInterface::RFC3339), $actual->updatedAt); } + /** + * Asserts that the received translation is the same as the expected one. + * + * @param Translation $expected The translation that should be received + * @param mixed $actual The translation that was received (as an stdClass) + */ protected static function assertSameTranslation(Translation $expected, mixed $actual): void { static::assertFalse(null === $expected xor null === $actual); @@ -80,6 +108,12 @@ protected static function assertSameTranslation(Translation $expected, mixed $ac static::assertSame($expected->getChinese(), $actual->chinese); } + /** + * Asserts that the received user is the same as the expected one. It should have the layout defined by the {@see Groups} 'user:read:some' in the {@see User} entity. + * + * @param User $expected The user that should be received + * @param mixed $actual The user that was received (as an stdClass) + */ protected static function assertSameUserReadSome(User $expected, mixed $actual): void { static::assertIsObject($actual); @@ -93,6 +127,12 @@ protected static function assertSameUserReadSome(User $expected, mixed $actual): static::assertSameUserInfosReadSome($expected->getInfos(), $actual->infos); } + /** + * Asserts that the received user infos are the same as the expected one. It should have the layout defined by the {@see Groups} 'user:read:some' in the {@see UserInfos} entity. + * + * @param UserInfos $expected The user infos that should be received + * @param mixed $actual The user infos that was received (as an stdClass) + */ protected static function assertSameUserInfosReadSome(UserInfos $expected, mixed $actual): void { static::assertIsObject($actual); @@ -103,6 +143,11 @@ protected static function assertSameUserInfosReadSome(UserInfos $expected, mixed static::assertSame($expected->getNickname(), $actual->nickname); } + /** + * Loads the given fixtures. + * + * @param Fixture ...$fixtures The fixtures to load + */ protected function loadFixtures(Fixture ...$fixtures) { $fixtureLoader = new Loader(); @@ -114,6 +159,15 @@ protected function loadFixtures(Fixture ...$fixtures) } } + /** + * Creates, persists, and returns a user. It can also flush the database if specified. + * + * @param string $firstName The first name of the user + * @param string $lastName The last name of the user + * @param string $login The login of the user + * @param null|string $role The role of the user (defaults to 'ROLE_USER') + * @param bool $flush Whether to flush the database or not (defaults to true) + */ protected function createUser(string $firstName, string $lastName, string $login, ?string $role = 'ROLE_USER', bool $flush = true): User { $user = new User(); @@ -129,12 +183,44 @@ protected function createUser(string $firstName, string $lastName, string $login return $user; } + /** + * Backups the database. It sets the value of {@see $databaseBackup}. It should be used before the database is altered. + * After changes, tests should use {@see assertDatabaseSameExcept} to assert that only specified fields have been modified. + */ protected function backupDatabase(): void { $this->databaseBackup = []; $this->_backupDatabase($this->databaseBackup); } + /** + * Asserts that the database is the same as the backup, except for the specified fields. + * + * @param array $diff The fields that should be different from the backup. It should be an array of the form: + * + * + * [ + * '' => [ + * 'where' => [ + * '' => '', // Changes are only expected where have value + * ], + * 'diff' => [ + * '' => '', + * ], + * ], + * ] + * + * @param array $new The new entries that should be in the database. It should be an array of the form: + * + * + * [ + * '' => [ + * [ + * '' => '', + * ], // Each array is a new entry + * ], + * + */ protected function assertDatabaseSameExcept(array $diff, array $new): void { $actualDatabase = []; @@ -153,18 +239,31 @@ protected function assertDatabaseSameExcept(array $diff, array $new): void $this->databaseBackup[$table][] = $entry; } } - static::assertEquals($this->databaseBackup, $actualDatabase); + // Yes, that's dumb, but PHPFixer keeps changing it to assertSame + \call_user_func_array([static::class, 'assertEquals'], [$this->databaseBackup, $actualDatabase]); } + /** + * Backups the database in the given array. + * + * @param array $backup A reference to the array in which the backup will be stored + */ private function _backupDatabase(array &$backup): void { $backup = []; + // Fetch all tables $tables = $this->em->getConnection()->createSchemaManager()->listTables(); foreach ($tables as $table) { $tableName = $table->getName(); $backup[$tableName] = []; + // Fetch all rows $rows = $this->em->getConnection()->prepare("SELECT * FROM {$tableName}")->executeQuery()->fetchAllAssociative(); - $getPrintableValue = fn (string $column, $value): ?string => UuidType::class === $table->getColumn($column)->getType()::class && null !== $value ? Uuid::fromBinary($value)->jsonSerialize() : $value; + // We don't want to directly use the binary value of the UUIDs, but the more human-readable representation + $getPrintableValue = function (string $column, $value) use ($table): ?string { + $shouldConvert = UuidType::class === $table->getColumn($column)->getType()::class && null !== $value; + + return $shouldConvert ? Uuid::fromBinary($value)->jsonSerialize() : $value; + }; foreach ($rows as $row) { // Convert all values to printable values foreach ($row as $column => &$value) { diff --git a/tests/Groups/GetGroupFromSlug.php b/tests/Groups/GetGroupFromSlug.php index 9375d32..c49b93d 100644 --- a/tests/Groups/GetGroupFromSlug.php +++ b/tests/Groups/GetGroupFromSlug.php @@ -11,6 +11,8 @@ * @internal * * @coversNothing + * + * Tests for route GET /groups/{slug} */ final class GetGroupFromSlug extends EtuUTTApiTestCase { diff --git a/tests/Groups/GetGroups.php b/tests/Groups/GetGroups.php index d0f6d89..b093245 100644 --- a/tests/Groups/GetGroups.php +++ b/tests/Groups/GetGroups.php @@ -11,6 +11,8 @@ * @internal * * @coversNothing + * + * Tests for route GET /groups */ final class GetGroups extends EtuUTTApiTestCase { diff --git a/tests/Groups/UpdateGroup.php b/tests/Groups/UpdateGroup.php index 01d7508..c2e56d7 100644 --- a/tests/Groups/UpdateGroup.php +++ b/tests/Groups/UpdateGroup.php @@ -11,6 +11,8 @@ * @internal * * @coversNothing + * + * Tests for route PATCH /groups/{slug} */ final class UpdateGroup extends EtuUTTApiTestCase { diff --git a/tests/Users/DeleteUser.php b/tests/Users/DeleteUser.php index 89b0555..93be07e 100644 --- a/tests/Users/DeleteUser.php +++ b/tests/Users/DeleteUser.php @@ -11,6 +11,8 @@ * @internal * * @coversNothing + * + * Tests for route DELETE /users/{id} */ final class DeleteUser extends EtuUTTApiTestCase { diff --git a/tests/Users/GetUserFromId.php b/tests/Users/GetUserFromId.php index dc032c4..aae5bc3 100644 --- a/tests/Users/GetUserFromId.php +++ b/tests/Users/GetUserFromId.php @@ -12,6 +12,8 @@ * @internal * * @coversNothing + * + * Tests for route GET /users/{id} */ final class GetUserFromId extends EtuUTTApiTestCase { diff --git a/tests/Users/GetUsers.php b/tests/Users/GetUsers.php index c76f1ba..27aeef3 100644 --- a/tests/Users/GetUsers.php +++ b/tests/Users/GetUsers.php @@ -16,6 +16,8 @@ * @internal * * @coversNothing + * + * Tests for route GET /users */ final class GetUsers extends EtuUTTApiTestCase { diff --git a/tests/Users/UpdateUser.php b/tests/Users/UpdateUser.php index f25d632..c185052 100644 --- a/tests/Users/UpdateUser.php +++ b/tests/Users/UpdateUser.php @@ -10,6 +10,8 @@ * @internal * * @coversNothing + * + * Tests for route PATCH /users/{id} */ final class UpdateUser extends EtuUTTApiTestCase {