From cdcf41e8ab41e89657c3edf7617cbb13450b5624 Mon Sep 17 00:00:00 2001 From: Alex Klassen Date: Thu, 27 Aug 2020 14:56:19 +0300 Subject: [PATCH] #109 Tests for OnMoon\OpenApiServerBundle\CodeGenerator\PhpParserGenerators\DtoCodeGenerator --- .../DtoCodeGeneratorTest.php | 841 +++++++++++++++--- 1 file changed, 696 insertions(+), 145 deletions(-) diff --git a/test/unit/CodeGenerator/PhpParserGenerators/DtoCodeGeneratorTest.php b/test/unit/CodeGenerator/PhpParserGenerators/DtoCodeGeneratorTest.php index 042402f..4c0723b 100644 --- a/test/unit/CodeGenerator/PhpParserGenerators/DtoCodeGeneratorTest.php +++ b/test/unit/CodeGenerator/PhpParserGenerators/DtoCodeGeneratorTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace OnMoon\OpenApiServerBundle\Test\Unit\CodeGenerator; +namespace OnMoon\OpenApiServerBundle\Test\Unit\CodeGenerator\PhpParserGenerators; use OnMoon\OpenApiServerBundle\CodeGenerator\Definitions\DtoDefinition; use OnMoon\OpenApiServerBundle\CodeGenerator\Definitions\PropertyDefinition; @@ -13,114 +13,504 @@ use PHPUnit\Framework\Assert; use PHPUnit\Framework\TestCase; +use function array_key_exists; +use function ucfirst; + /** * @covers \OnMoon\OpenApiServerBundle\CodeGenerator\PhpParserGenerators\DtoCodeGenerator */ final class DtoCodeGeneratorTest extends TestCase { + private const DEFINITION_EMPTY_PROPERTIES = [ + 'namespace' => 'Some\\Custom\\Namespace', + 'class' => 'SomeCustomClass', + 'properties' => [], + ]; + + private const DEFINITION_BASIC = [ + 'namespace' => 'Some\\Custom\\Namespace', + 'class' => 'SomeCustomClass', + 'properties' => [ + [ + 'classPropertyName' => 'name100', + 'isInConstructor' => false, + 'hasGetter' => false, + 'hasSetter' => false, + 'name' => 'name-100', + 'defaultValue' => 'default-value-100', + 'description' => null, + 'required' => false, + 'pattern' => '1111111111111111111111111111111111111111111111111111111', + 'array' => false, + 'nullable' => false, + 'scalarTypeId' => 8, + 'expected' => ['renderedParamPattern' => '/private [?]int [$]name100 = null;/'], + 'typeHint' => 'int', + ], + [ + 'classPropertyName' => 'name300', + 'isInConstructor' => true, + 'hasGetter' => false, + 'hasSetter' => false, + 'name' => 'name-300', + 'defaultValue' => 'default-value-300', + 'description' => null, + 'required' => false, + 'pattern' => '1111111111111111111111111111111111111111111111111111111', + 'array' => false, + 'nullable' => false, + 'scalarTypeId' => 8, + 'expected' => ['renderedParamPattern' => '/private [?]int [$]name300 = null;/'], + 'typeHint' => 'int', + ], + ], + ]; + + private const DEFINITION_EXTENDED = [ + 'namespace' => 'Some\\Custom\\Namespace', + 'class' => 'SomeCustomClass', + 'properties' => [ + [ + 'classPropertyName' => 'name100', + 'isInConstructor' => false, + 'hasGetter' => false, + 'hasSetter' => false, + 'name' => 'name-100', + 'defaultValue' => 'default-value-100', + 'description' => 'Description of property "name-100"', + 'required' => false, + 'pattern' => '1111111111111111111111111111111111111111111111111111111', + 'array' => false, + 'nullable' => false, + 'scalarTypeId' => 8, + 'expected' => ['renderedParamPattern' => '/private [?]int [$]name100 = null;/'], + 'typeHint' => 'int', + ], + [ + 'classPropertyName' => 'name300', + 'isInConstructor' => false, + 'hasGetter' => false, + 'hasSetter' => false, + 'name' => 'name-300', + 'defaultValue' => 'default-value-300', + 'description' => 'Description of property "name-300"', + 'required' => true, + 'pattern' => '', + 'array' => true, + 'nullable' => true, + 'scalarTypeId' => 10, + 'typeHint' => 'array', + ], + [ + 'classPropertyName' => 'name500', + 'isInConstructor' => false, + 'hasGetter' => false, + 'hasSetter' => false, + 'name' => 'name-500', + 'description' => 'Description of property "name-500"', + 'required' => false, + 'definition' => [ + 'namespace' => 'Some\\Custom\\Namespace50000', + 'class' => 'SomeCustomClass50000', + 'properties' => [ + [ + 'classPropertyName' => 'name50100', + 'isInConstructor' => false, + 'hasGetter' => false, + 'hasSetter' => false, + 'name' => 'name-50100', + 'defaultValue' => 'value-50100', + ], + ], + ], + 'typeHint' => 'SomeCustomClass50000', + ], + [ + 'classPropertyName' => 'name700', + 'isInConstructor' => true, + 'hasGetter' => true, + 'hasSetter' => true, + 'name' => 'name-700', + 'defaultValue' => 'default-value-700', + 'description' => 'Description of property "name-700"', + 'required' => true, + 'pattern' => '', + 'array' => false, + 'nullable' => true, + 'scalarTypeId' => 10, + 'typeHint' => 'array', + ], + [ + 'classPropertyName' => 'name800', + 'isInConstructor' => true, + 'hasGetter' => true, + 'hasSetter' => true, + 'name' => 'name-700', + 'defaultValue' => 'default-value-800', + 'description' => 'Description of property "name-800"', + 'required' => true, + 'pattern' => '', + 'array' => true, + 'nullable' => true, + 'scalarTypeId' => 10, + 'typeHint' => 'array', + ], + ], + ]; + + private const EXAMPLE_FILE_CONTENT_EMPTY_PROPERTIES_SHORT_DOC_7_4 = 'name300 = $name300; + } + /** @inheritDoc */ + public function toArray() : array + { + return array(\'name-100\' => $this->name100, \'name-300\' => $this->name300); + } + /** @inheritDoc */ + public static function fromArray(array $data) : self + { + $dto = new SomeCustomClass($data[\'name-300\']); + $dto->name100 = $data[\'name-100\']; + return $dto; + } +}'; + + private const EXAMPLE_FILE_CONTENT_EXTENDED_SHORT_DOC_7_4 = 'name700 = $name700; + $this->name800 = $name800; + } + public function getName700() : ?int + { + return $this->name700; + } + /** @return int[]|null */ + public function getName800() : ?array + { + return $this->name800; + } + public function setName700(?int $name700) : self + { + $this->name700 = $name700; + return $this; + } + /** @param int[]|null $name800 */ + public function setName800(?array $name800) : self + { + $this->name800 = $name800; + return $this; + } + /** @inheritDoc */ + public function toArray() : array + { + return array(\'name-100\' => $this->name100, \'name-300\' => $this->name300, \'name-500\' => null === $this->name500 ? null : $this->name500->toArray(), \'name-700\' => $this->name700, \'name-700\' => $this->name800); + } + /** @inheritDoc */ + public static function fromArray(array $data) : self + { + $dto = new SomeCustomClass($data[\'name-700\'], $data[\'name-700\']); + $dto->name100 = $data[\'name-100\']; + $dto->name300 = $data[\'name-300\']; + $dto->name500 = null === $data[\'name-500\'] ? null : SomeCustomClass50000::fromArray($data[\'name-500\']); + return $dto; + } +}'; + + private const EXAMPLE_FILE_CONTENT_EMPTY_PROPERTIES_FULL_DOC_7_4 = 'name300 = $name300; + } + /** @inheritDoc */ + public function toArray() : array + { + return array(\'name-100\' => $this->name100, \'name-300\' => $this->name300); + } + /** @inheritDoc */ + public static function fromArray(array $data) : self + { + $dto = new SomeCustomClass($data[\'name-300\']); + $dto->name100 = $data[\'name-100\']; + return $dto; + } +}'; + + private const EXAMPLE_FILE_CONTENT_EXTENDED_FULL_DOC_7_4 = 'name700 = $name700; + $this->name800 = $name800; + } + /** @return int|null */ + public function getName700() : ?int + { + return $this->name700; + } + /** @return int[]|null */ + public function getName800() : ?array + { + return $this->name800; + } + /** + * @param int|null $name700 + * @return self + */ + public function setName700(?int $name700) : self + { + $this->name700 = $name700; + return $this; + } + /** + * @param int[]|null $name800 + * @return self + */ + public function setName800(?array $name800) : self + { + $this->name800 = $name800; + return $this; + } + /** @inheritDoc */ + public function toArray() : array + { + return array(\'name-100\' => $this->name100, \'name-300\' => $this->name300, \'name-500\' => null === $this->name500 ? null : $this->name500->toArray(), \'name-700\' => $this->name700, \'name-700\' => $this->name800); + } + /** @inheritDoc */ + public static function fromArray(array $data) : self + { + $dto = new SomeCustomClass($data[\'name-700\'], $data[\'name-700\']); + $dto->name100 = $data[\'name-100\']; + $dto->name300 = $data[\'name-300\']; + $dto->name500 = null === $data[\'name-500\'] ? null : SomeCustomClass50000::fromArray($data[\'name-500\']); + return $dto; + } +}'; + /** * @return mixed[] */ public function generateProvider(): array { - $basicDefinition = [ - 'namespace' => 'Some\\Custom\\Namespace', - 'class' => 'SomeCustomClass', - 'properties' => [], - ]; - - $extendedDefinition = [ - 'namespace' => 'Some\\Custom\\Namespace', - 'class' => 'SomeCustomClass', - 'properties' => [ - [ - 'classPropertyName' => 'name100', - 'name' => 'name-100', - 'defaultValue' => 'default-value-100', - 'description' => 'Description of property "name-100"', - 'required' => false, - 'pattern' => null, - 'array' => false, - 'nullable' => false, - 'scalarTypeId' => 8, - ], - [ - 'classPropertyName' => 'name300', - 'name' => 'name-300', - 'defaultValue' => 'default-value-300', - 'description' => 'Description of property "name-300"', - 'required' => true, - 'pattern' => '', - 'array' => true, - 'nullable' => true, - 'scalarTypeId' => 10, + return [ + [ + 'payload' => [ + 'definition' => self::DEFINITION_EMPTY_PROPERTIES, + 'codeGenerator' => [ + 'languageLevel' => '7.4', + 'fullDocs' => false, + ], ], - [ - 'classPropertyName' => 'name500', - 'name' => '500', - 'description' => 'Description of property "name-500"', - 'required' => false, - 'definition' => [ - 'namespace' => 'Some\\Custom\\Namespace50000', - 'class' => 'SomeCustomClass50000', - 'properties' => [ - [ - 'classPropertyName' => 'name50100', - 'name' => 'name-50100', - 'defaultValue' => 'value-50100', - ], - ], + 'expected' => self::EXAMPLE_FILE_CONTENT_EMPTY_PROPERTIES_SHORT_DOC_7_4, + ], + [ + 'payload' => [ + 'definition' => self::DEFINITION_BASIC, + 'codeGenerator' => [ + 'languageLevel' => '7.4', + 'fullDocs' => false, ], ], + 'expected' => self::EXAMPLE_FILE_CONTENT_BASIC_SHORT_DOC_7_4, ], - ]; - - return [ -// [ -// 'payload' => [ -// 'definition' => $basicDefinition, -// 'codeGenerator' => [ -// 'languageLevel' => 'undefined', -// 'fullDocs' => false, -// ], -// ], -// ], -// [ -// 'payload' => [ -// 'definition' => $basicDefinition, -// 'codeGenerator' => [ -// 'languageLevel' => '7.4', -// 'fullDocs' => false, -// ], -// ], -// ], -// [ -// 'payload' => [ -// 'definition' => $basicDefinition, -// 'codeGenerator' => [ -// 'languageLevel' => '7.4', -// 'fullDocs' => true, -// ], -// ], -// ], [ 'payload' => [ - 'definition' => $extendedDefinition, + 'definition' => self::DEFINITION_EXTENDED, 'codeGenerator' => [ 'languageLevel' => '7.4', 'fullDocs' => false, ], ], + 'expected' => self::EXAMPLE_FILE_CONTENT_EXTENDED_SHORT_DOC_7_4, + ], + [ + 'payload' => [ + 'definition' => self::DEFINITION_EMPTY_PROPERTIES, + 'codeGenerator' => [ + 'languageLevel' => '7.4', + 'fullDocs' => true, + ], + ], + 'expected' => self::EXAMPLE_FILE_CONTENT_EMPTY_PROPERTIES_FULL_DOC_7_4, + ], + [ + 'payload' => [ + 'definition' => self::DEFINITION_BASIC, + 'codeGenerator' => [ + 'languageLevel' => '7.4', + 'fullDocs' => true, + ], + ], + 'expected' => self::EXAMPLE_FILE_CONTENT_BASIC_FULL_DOC_7_4, + ], + [ + 'payload' => [ + 'definition' => self::DEFINITION_EXTENDED, + 'codeGenerator' => [ + 'languageLevel' => '7.4', + 'fullDocs' => true, + ], + ], + 'expected' => self::EXAMPLE_FILE_CONTENT_EXTENDED_FULL_DOC_7_4, ], -// [ -// 'payload' => [ -// 'definition' => $extendedDefinition, -// 'codeGenerator' => [ -// 'languageLevel' => '7.4', -// 'fullDocs' => true, -// ], -// ], -// ], ]; } @@ -129,7 +519,7 @@ public function generateProvider(): array * * @dataProvider generateProvider */ - public function testGenerate(array $payload): void + public function testGenerate(array $payload, string $expected): void { $codeGenerator = new DtoCodeGenerator( new BuilderFactory(), @@ -143,63 +533,209 @@ public function testGenerate(array $payload): void $generatedFileDefinition = $codeGenerator->generate($definition); Assert::assertSame($definition, $generatedFileDefinition->getClass()); - - $this->assertMatchesRegularExpression( - addslashes(sprintf( - '/namespace %s;/', - $payload['definition']['namespace'] - )), - $generatedFileDefinition->getFileContents() - ); - - $this->assertMatchesRegularExpression( - addslashes(sprintf( - '/final class %s/', - $payload['definition']['class'] - )), - $generatedFileDefinition->getFileContents() - ); - - foreach ($payload['definition']['properties'] as $definitionProperty) { - if (array_key_exists('description', $definitionProperty)) { - $this->assertMatchesRegularExpression( - addslashes(sprintf( - '/\* %s/', - $definitionProperty['description'] - )), - $generatedFileDefinition->getFileContents() - ); - } - if ($definitionProperty['codeGenerator']['fullDocs'] === true) { - $this->assertMatchesRegularExpression( - addslashes(sprintf( - '/\* %s/', - $definitionProperty['description'] - )), - $generatedFileDefinition->getFileContents() - ); - } - - if (array_key_exists('definition', $definitionProperty)) { - $this->assertMatchesRegularExpression( - addslashes(sprintf( - '/use %s\%s;/', - $definitionProperty['definition']['namespace'], - $definitionProperty['definition']['class'] - )), - $generatedFileDefinition->getFileContents() - ); - } - } - - dump( -// $generatedFileDefinition, -// $generatedFileDefinition->getClass(), - $generatedFileDefinition->getFileContents() - ); - exit; + Assert::assertSame($expected, $generatedFileDefinition->getFileContents()); } +// /** +// * @deprecated Changed to compare by content. +// * +// * @param mixed[] $payload +// * +// * @dataProvider generateProvider +// */ +// public function testGenerate(array $payload): void +// { +// $codeGenerator = new DtoCodeGenerator( +// new BuilderFactory(), +// new ScalarTypesResolver(), +// $payload['codeGenerator']['languageLevel'], +// $payload['codeGenerator']['fullDocs'], +// ); +// +// $definition = $this->createDtoDefinition($payload['definition']); +// +// $generatedFileDefinition = $codeGenerator->generate($definition); +// +// Assert::assertSame($definition, $generatedFileDefinition->getClass()); +// +// $this->assertMatchesRegularExpression( +// addslashes(sprintf( +// '/namespace %s;/', +// $payload['definition']['namespace'] +// )), +// $generatedFileDefinition->getFileContents() +// ); +// +// $this->assertMatchesRegularExpression( +// addslashes(sprintf( +// '/final class %s/', +// $payload['definition']['class'] +// )), +// $generatedFileDefinition->getFileContents() +// ); +// +// $toArrayParams = []; +// +// foreach ($payload['definition']['properties'] as $definitionProperty) { +// if (array_key_exists('definition', $definitionProperty)) { +// $this->assertMatchesRegularExpression( +// addslashes(sprintf( +// '/use %s\%s;/', +// $definitionProperty['definition']['namespace'], +// $definitionProperty['definition']['class'] +// )), +// $generatedFileDefinition->getFileContents() +// ); +// } +// +// if (array_key_exists('description', $definitionProperty)) { +// $this->assertMatchesRegularExpression( +// addslashes(sprintf( +// '/\* %s/', +// $definitionProperty['description'] +// )), +// $generatedFileDefinition->getFileContents() +// ); +// } +// +// // REMARK: The PHP version should be checked here ($payload['codeGenerator']['languageLevel']). +// // If it's 7.4 or higher, add a type hint to the expression template. +// $paramTypeHint = ' [?]' . $definitionProperty['typeHint']; +// +// $this->assertMatchesRegularExpression( +// addslashes(sprintf( +// '/private%s [$]%s = null;/', +// $paramTypeHint, +// $definitionProperty['classPropertyName'] +// )), +// $generatedFileDefinition->getFileContents() +// ); +// +// if (array_key_exists('definition', $definitionProperty)) { +// $toArrayParamValue = sprintf( +// 'null === [$]this->%s [?] null : [$]this->%s->toArray[(][)]', +// $definitionProperty['classPropertyName'], +// $definitionProperty['classPropertyName'] +// ); +// +// if (! $definitionProperty['isInConstructor']) { +// $this->assertMatchesRegularExpression( +// addslashes(sprintf( +// '/[$]dto->%s = null === [$]data[[]\'%s\'[]] [?] null : %s::fromArray[(][$]data[[]\'%s\'[]][)];/', +// $definitionProperty['classPropertyName'], +// $definitionProperty['name'], +// $definitionProperty['definition']['class'], +// $definitionProperty['name'] +// )), +// $generatedFileDefinition->getFileContents() +// ); +// } +// } else { +// $toArrayParamValue = sprintf('[$]this->%s', $definitionProperty['classPropertyName']); +// +// if (! $definitionProperty['isInConstructor']) { +// $this->assertMatchesRegularExpression( +// addslashes(sprintf( +// '/[$]dto->%s = [$]data[[]\'%s\'[]];/', +// $definitionProperty['classPropertyName'], +// $definitionProperty['name'] +// )), +// $generatedFileDefinition->getFileContents() +// ); +// } +// } +// +// if ($definitionProperty['isInConstructor']) { +// $this->assertMatchesRegularExpression( +// addslashes(sprintf( +// '/[$]this->%s = [$]%s;/', +// $definitionProperty['classPropertyName'], +// $definitionProperty['classPropertyName'] +// )), +// $generatedFileDefinition->getFileContents() +// ); +// } +// +// if ($definitionProperty['hasGetter']) { +// $this->assertMatchesRegularExpression( +// addslashes(sprintf( +// '/public function get%s[(][)] : [?]%s/', +// ucfirst($definitionProperty['classPropertyName']), +// $definitionProperty['typeHint'] +// )), +// $generatedFileDefinition->getFileContents() +// ); +// +// Assert::assertNotFalse( +// preg_match( +// sprintf('/get%s[(]([^}])+[}]/', ucfirst($definitionProperty['classPropertyName'])), +// $generatedFileDefinition->getFileContents(), +// $matches +// ) +// ); +// +// $this->assertMatchesRegularExpression( +// addslashes(sprintf( +// '/return [$]this->%s;/', +// $definitionProperty['classPropertyName'] +// )), +// $matches[0] +// ); +// +// unset($matches); +// } +// +// if ($definitionProperty['hasSetter']) { +// $this->assertMatchesRegularExpression( +// addslashes(sprintf( +// '/public function set%s[(][?]%s [$]%s[)]/', +// ucfirst($definitionProperty['classPropertyName']), +// $definitionProperty['typeHint'], +// $definitionProperty['classPropertyName'] +// )), +// $generatedFileDefinition->getFileContents() +// ); +// +// Assert::assertNotFalse( +// preg_match( +// sprintf('/set%s[(]([^}])+[}]/', ucfirst($definitionProperty['classPropertyName'])), +// $generatedFileDefinition->getFileContents(), +// $matches +// ) +// ); +// +// $this->assertMatchesRegularExpression( +// addslashes(sprintf( +// '/[$]this->%s = [$]%s;/', +// $definitionProperty['classPropertyName'], +// $definitionProperty['classPropertyName'] +// )), +// $matches[0] +// ); +// +// $this->assertMatchesRegularExpression( +// addslashes('/return [$]this;/'), +// $matches[0] +// ); +// +// unset($matches); +// } +// +// $toArrayParams[] = sprintf("'%s' => %s", $definitionProperty['name'], $toArrayParamValue); +// } +// +// $this->assertMatchesRegularExpression( +// addslashes(sprintf( +// '/return array[(]%s[)];/', +// $toArrayParams ? implode(', ', $toArrayParams) : null +// )), +// $generatedFileDefinition->getFileContents() +// ); +// +//// dump($generatedFileDefinition->getFileContents()); +//// exit; +// } + /** * @param mixed[] $payload */ @@ -217,7 +753,19 @@ private function createDtoDefinition(array $payload): DtoDefinition ->setArray($property['array'] ?? false) ->setNullable($property['nullable'] ?? false) ->setScalarTypeId($property['scalarTypeId'] ?? null) - ))->setClassPropertyName($property['classPropertyName']); + )) + ->setClassPropertyName($property['classPropertyName']) + ->setInConstructor($property['isInConstructor']); + + if ($property['hasGetter']) { + $propertyDefinition->setHasGetter(true); + $propertyDefinition->setGetterName('get' . ucfirst($property['classPropertyName'])); + } + + if ($property['hasSetter']) { + $propertyDefinition->setHasSetter(true); + $propertyDefinition->setSetterName('set' . ucfirst($property['classPropertyName'])); + } if (array_key_exists('definition', $property)) { $subDefinition = $this->createDtoDefinition($property['definition']); @@ -227,8 +775,11 @@ private function createDtoDefinition(array $payload): DtoDefinition $definitionProperties[] = $propertyDefinition; } - return (new DtoDefinition($definitionProperties)) + $definition = new DtoDefinition($definitionProperties); + $definition ->setNamespace($payload['namespace']) ->setClassName($payload['class']); + + return $definition; } }