From f3667af946894286783835b76410be6d70328fb9 Mon Sep 17 00:00:00 2001 From: Benoit Jouhaud Date: Wed, 6 Sep 2023 00:19:08 +0200 Subject: [PATCH] Support an optional "min" argument for the FactoryAdapter to be able to handle functions like "randomSet" --- phpstan-baseline.neon | 5 ++ .../Factory/AccessorArgumentGuesser.php | 7 ++- .../Factory/ArgumentGuesserInterface.php | 8 ++- .../Factory/AttributesArgumentGuesser.php | 5 ++ .../Factory/MethodArgumentGuesser.php | 10 +++- .../Factory/MinArgumentGuesser.php | 21 +++++++ .../Evaluator/FactoryEvaluator.php | 25 +++++++-- .../Adapter/FactoryAdapterTest.php | 30 ++++++++++ .../Factory/AccessorArgumentGuesserTest.php | 25 ++++++++- .../Factory/AttributesArgumentGuesserTest.php | 20 +++++-- .../Factory/MethodArgumentGuesserTest.php | 9 ++- .../Factory/MinArgumentGuesserTest.php | 55 +++++++++++++++++++ 12 files changed, 202 insertions(+), 18 deletions(-) create mode 100644 src/ExpressionLanguage/ArgumentGuesser/Factory/MinArgumentGuesser.php create mode 100644 tests/Unit/ExpressionLanguage/ArgumentGuesser/Factory/MinArgumentGuesserTest.php diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 6aaaa0d..43debcc 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -20,6 +20,11 @@ parameters: count: 1 path: tests/Integration/Adapter/FactoryAdapterTest.php + - + message: "#^Parameter \\#2 \\$callback of function usort expects callable\\(mixed, mixed\\)\\: int, Closure\\(Presta\\\\BehatEvaluator\\\\Tests\\\\Application\\\\Entity\\\\User, Presta\\\\BehatEvaluator\\\\Tests\\\\Application\\\\Entity\\\\User\\)\\: int\\<\\-1, 1\\> given\\.$#" + count: 1 + path: tests/Integration/Adapter/FactoryAdapterTest.php + - message: "#^Generator expects value type array\\{bool\\|float\\|int\\|string, bool\\|float\\|int\\|string\\}, array\\{array\\|object\\|null, array\\|object\\|null\\} given\\.$#" count: 1 diff --git a/src/ExpressionLanguage/ArgumentGuesser/Factory/AccessorArgumentGuesser.php b/src/ExpressionLanguage/ArgumentGuesser/Factory/AccessorArgumentGuesser.php index 0e79cac..000387d 100644 --- a/src/ExpressionLanguage/ArgumentGuesser/Factory/AccessorArgumentGuesser.php +++ b/src/ExpressionLanguage/ArgumentGuesser/Factory/AccessorArgumentGuesser.php @@ -8,6 +8,7 @@ final class AccessorArgumentGuesser implements ArgumentGuesserInterface { public function __invoke( array|string|null $method, + array|string|null $min, array|string|null $attributes, string|null $accessor, ): string|null { @@ -15,7 +16,11 @@ public function __invoke( return null; } - if (\is_string($attributes)) { + if (\is_string($min) && !\is_numeric($min) && null === $attributes && null === $accessor) { + return $min; + } + + if (\is_string($attributes) && !\is_numeric($attributes) && null === $accessor) { return $attributes; } diff --git a/src/ExpressionLanguage/ArgumentGuesser/Factory/ArgumentGuesserInterface.php b/src/ExpressionLanguage/ArgumentGuesser/Factory/ArgumentGuesserInterface.php index 43727cd..eed0bee 100644 --- a/src/ExpressionLanguage/ArgumentGuesser/Factory/ArgumentGuesserInterface.php +++ b/src/ExpressionLanguage/ArgumentGuesser/Factory/ArgumentGuesserInterface.php @@ -11,7 +11,13 @@ interface ArgumentGuesserInterface { /** * @param FactoryAttributes|string|null $method + * @param FactoryAttributes|string|null $min * @param FactoryAttributes|string|null $attributes */ - public function __invoke(string|array|null $method, string|array|null $attributes, string|null $accessor): mixed; + public function __invoke( + string|array|null $method, + string|array|null $min, + string|array|null $attributes, + string|null $accessor, + ): mixed; } diff --git a/src/ExpressionLanguage/ArgumentGuesser/Factory/AttributesArgumentGuesser.php b/src/ExpressionLanguage/ArgumentGuesser/Factory/AttributesArgumentGuesser.php index dc5f30e..89c2d2a 100644 --- a/src/ExpressionLanguage/ArgumentGuesser/Factory/AttributesArgumentGuesser.php +++ b/src/ExpressionLanguage/ArgumentGuesser/Factory/AttributesArgumentGuesser.php @@ -11,6 +11,7 @@ final class AttributesArgumentGuesser implements ArgumentGuesserInterface */ public function __invoke( array|string|null $method, + array|string|null $min, array|string|null $attributes, string|null $accessor, ): array|null { @@ -18,6 +19,10 @@ public function __invoke( return $method; } + if (\is_array($min)) { + return $min; + } + if (\is_array($attributes)) { return $attributes; } diff --git a/src/ExpressionLanguage/ArgumentGuesser/Factory/MethodArgumentGuesser.php b/src/ExpressionLanguage/ArgumentGuesser/Factory/MethodArgumentGuesser.php index ecd594c..e892f1f 100644 --- a/src/ExpressionLanguage/ArgumentGuesser/Factory/MethodArgumentGuesser.php +++ b/src/ExpressionLanguage/ArgumentGuesser/Factory/MethodArgumentGuesser.php @@ -6,9 +6,13 @@ final class MethodArgumentGuesser implements ArgumentGuesserInterface { - public function __invoke(array|string|null $method, array|string|null $attributes, string|null $accessor): string - { - if (\is_string($method)) { + public function __invoke( + array|string|null $method, + array|string|null $min, + array|string|null $attributes, + string|null $accessor, + ): string { + if (\is_string($method) && !\is_numeric($method)) { return $method; } diff --git a/src/ExpressionLanguage/ArgumentGuesser/Factory/MinArgumentGuesser.php b/src/ExpressionLanguage/ArgumentGuesser/Factory/MinArgumentGuesser.php new file mode 100644 index 0000000..52a039a --- /dev/null +++ b/src/ExpressionLanguage/ArgumentGuesser/Factory/MinArgumentGuesser.php @@ -0,0 +1,21 @@ + $arguments * @param string|array|null $method + * @param string|array|null $min * @param string|array|null $attributes */ public function __invoke( array $arguments, string $name, string|array|null $method = null, + string|array|null $min = null, string|array|null $attributes = null, string|null $accessor = null, ): mixed { $originalMethod = $method; + $originalMin = $min; $originalAttributes = $attributes; $originalAccessor = $accessor; - $method = (new MethodArgumentGuesser())($originalMethod, $originalAttributes, $originalAccessor); - $attributes = (new AttributesArgumentGuesser())($originalMethod, $originalAttributes, $originalAccessor); - $accessor = (new AccessorArgumentGuesser())($originalMethod, $originalAttributes, $originalAccessor); + $method = (new MethodArgumentGuesser())($originalMethod, $originalMin, $originalAttributes, $originalAccessor); + $min = (new MinArgumentGuesser())($originalMethod, $originalMin, $originalAttributes, $originalAccessor); + $attributes = (new AttributesArgumentGuesser())( + $originalMethod, + $originalMin, + $originalAttributes, + $originalAccessor, + ); + $accessor = (new AccessorArgumentGuesser())( + $originalMethod, + $originalMin, + $originalAttributes, + $originalAccessor, + ); $factoryClass = $this->factoryClassFactory->fromName($name); @@ -51,7 +66,9 @@ public function __invoke( } $value = match (true) { - \is_array($attributes) => call_user_func($callable, $attributes), + \is_numeric($min) && \is_array($attributes) => call_user_func($callable, $min, $attributes), + \is_numeric($min) && !\is_array($attributes) => call_user_func($callable, $min), + !\is_numeric($min) && \is_array($attributes) => call_user_func($callable, $attributes), default => call_user_func($callable), }; diff --git a/tests/Integration/Adapter/FactoryAdapterTest.php b/tests/Integration/Adapter/FactoryAdapterTest.php index 18f5963..745efa0 100644 --- a/tests/Integration/Adapter/FactoryAdapterTest.php +++ b/tests/Integration/Adapter/FactoryAdapterTest.php @@ -7,6 +7,7 @@ use Doctrine\Common\Collections\ArrayCollection; use Presta\BehatEvaluator\Adapter\FactoryAdapter; use Presta\BehatEvaluator\Exception\UnexpectedTypeException; +use Presta\BehatEvaluator\Tests\Application\Entity\User; use Presta\BehatEvaluator\Tests\Application\Foundry\Factory\UserFactory; use Presta\BehatEvaluator\Tests\Integration\KernelTestCase; use Presta\BehatEvaluator\Tests\Resources\ExpressionLanguageFactory; @@ -46,6 +47,14 @@ public function testInvokingTheAdapter(mixed $expected, mixed $value): void $expected = $expected(); } + // ModelFactory::randomSet() does not return sorted entities + if ($value instanceof ArrayCollection) { + $data = $value->toArray(); + usort($data, static fn (User $left, User $right): int => $left->getId() <=> $right->getId()); + + $value = new ArrayCollection($data); + } + self::assertEquals($expected, $value); } @@ -81,6 +90,27 @@ public function values(): iterable 2, '', ]; + yield 'a string containing a factory expression with a factory name, the "randomSet" function' + . ' and an integer as third parameter should return the relevant object proxy(s)' => [ + static fn () => new ArrayCollection( + array_map( + static fn (Proxy $proxy): object => $proxy->object(), + UserFactory::all(), + ), + ), + '', + ]; + yield 'a string containing a factory expression with a factory name, the "randomSet" function,' + . ' an integer as third parameter and an array of attributes' + . ' should return the relevant object proxy(s)' => [ + static fn () => new ArrayCollection( + array_map( + static fn (Proxy $proxy): object => $proxy->object(), + UserFactory::findBy(['firstname' => 'John']), + ), + ), + '', + ]; yield 'a string containing a factory expression' . ' should return the string after evaluating the factory expression' => [ 'The value John comes from a string', diff --git a/tests/Unit/ExpressionLanguage/ArgumentGuesser/Factory/AccessorArgumentGuesserTest.php b/tests/Unit/ExpressionLanguage/ArgumentGuesser/Factory/AccessorArgumentGuesserTest.php index 4b13659..49f35a5 100644 --- a/tests/Unit/ExpressionLanguage/ArgumentGuesser/Factory/AccessorArgumentGuesserTest.php +++ b/tests/Unit/ExpressionLanguage/ArgumentGuesser/Factory/AccessorArgumentGuesserTest.php @@ -18,17 +18,19 @@ final class AccessorArgumentGuesserTest extends TestCase * * @param FactoryAttributes|string|null $expected * @param FactoryAttributes|string|null $method + * @param FactoryAttributes|string|null $min * @param FactoryAttributes|string|null $attributes */ public function testInvokingTheGuesser( array|string|null $expected, array|string|null $method, + array|string|null $min, array|string|null $attributes, string|null $accessor, ): void { $guess = new AccessorArgumentGuesser(); - self::assertSame($expected, $guess($method, $attributes, $accessor)); + self::assertSame($expected, $guess($method, $min, $attributes, $accessor)); } /** @@ -36,17 +38,19 @@ public function testInvokingTheGuesser( * FactoryAttributes|string|null, * FactoryAttributes|string|null, * FactoryAttributes|string|null, + * FactoryAttributes|string|null, * string|null, * }> */ public function arguments(): iterable { - yield 'all arguments set not null should return null' => [null, null, null, null]; + yield 'all arguments set not null should return null' => [null, null, null, null, null]; yield 'a non null method and a string as 2nd argument should return the 2nd argument' => [ 'firstname', 'find', 'firstname', null, + null, ]; yield 'a non null method, an array of attributes and a string as 3rd argument' . ' should return the 3rd argument' => [ @@ -54,6 +58,23 @@ public function arguments(): iterable 'find', [], 'lastname', + null, + ]; + yield 'a non null method, an numeric value as 2nd argument and a string as 3rd argument' + . ' should return the 3rd argument' => [ + 'lastname', + 'find', + '1', + 'lastname', + null, + ]; + yield 'a non null method, an numeric value as 2nd argument, an array of attributes and a string as 4th argument' + . ' should return the 4th argument' => [ + 'lastname', + 'find', + '1', + [], + 'lastname', ]; } } diff --git a/tests/Unit/ExpressionLanguage/ArgumentGuesser/Factory/AttributesArgumentGuesserTest.php b/tests/Unit/ExpressionLanguage/ArgumentGuesser/Factory/AttributesArgumentGuesserTest.php index 8e096ad..5e0bbfa 100644 --- a/tests/Unit/ExpressionLanguage/ArgumentGuesser/Factory/AttributesArgumentGuesserTest.php +++ b/tests/Unit/ExpressionLanguage/ArgumentGuesser/Factory/AttributesArgumentGuesserTest.php @@ -18,17 +18,19 @@ final class AttributesArgumentGuesserTest extends TestCase * * @param FactoryAttributes|string|null $expected * @param FactoryAttributes|string|null $method + * @param FactoryAttributes|string|null $min * @param FactoryAttributes|string|null $attributes */ public function testInvokingTheGuesser( array|string|null $expected, array|string|null $method, + array|string|null $min, array|string|null $attributes, string|null $accessor, ): void { $guess = new AttributesArgumentGuesser(); - self::assertSame($expected, $guess($method, $attributes, $accessor)); + self::assertSame($expected, $guess($method, $min, $attributes, $accessor)); } /** @@ -36,16 +38,26 @@ public function testInvokingTheGuesser( * FactoryAttributes|string|null, * FactoryAttributes|string|null, * FactoryAttributes|string|null, + * FactoryAttributes|string|null, * string|null, * }> */ public function arguments(): iterable { - yield 'all arguments set not null should return null' => [null, null, null, null]; - yield 'an array as 1st argument should return the 1st argument' => [[], [], null, null]; - yield 'a string as 1st argument and an array as 2nd argument should return the 2nd argument' => [ + yield 'all arguments set not null should return null' => [null, null, null, null, null]; + yield 'an array as 1st argument should return the 1st argument' => [[], [], null, null, null]; + yield 'a non null method and an array as 2nd argument should return the 2nd argument' => [ + [], + 'find', [], null, + null, + ]; + yield 'a non null method, a numeric value as 2nd argument and an array as 3rd argument' + . ' should return the 3rd argument' => [ + [], + 'randomSet', + '2', [], null, ]; diff --git a/tests/Unit/ExpressionLanguage/ArgumentGuesser/Factory/MethodArgumentGuesserTest.php b/tests/Unit/ExpressionLanguage/ArgumentGuesser/Factory/MethodArgumentGuesserTest.php index b7e5533..0d4abd2 100644 --- a/tests/Unit/ExpressionLanguage/ArgumentGuesser/Factory/MethodArgumentGuesserTest.php +++ b/tests/Unit/ExpressionLanguage/ArgumentGuesser/Factory/MethodArgumentGuesserTest.php @@ -17,17 +17,19 @@ final class MethodArgumentGuesserTest extends TestCase * @dataProvider arguments * * @param FactoryAttributes|string|null $method + * @param FactoryAttributes|string|null $min * @param FactoryAttributes|string|null $attributes */ public function testInvokingTheGuesser( string $expected, array|string|null $method, + array|string|null $min, array|string|null $attributes, string|null $accessor, ): void { $guess = new MethodArgumentGuesser(); - self::assertSame($expected, $guess($method, $attributes, $accessor)); + self::assertSame($expected, $guess($method, $min, $attributes, $accessor)); } /** @@ -35,12 +37,13 @@ public function testInvokingTheGuesser( * string, * FactoryAttributes|string|null, * FactoryAttributes|string|null, + * FactoryAttributes|string|null, * string|null, * }> */ public function arguments(): iterable { - yield 'all arguments set not null should return the default "find" method' => ['find', null, null, null]; - yield 'a string as 1st argument should return the 1st argument' => ['count', 'count', null, null]; + yield 'all arguments set not null should return the default "find" method' => ['find', null, null, null, null]; + yield 'a string as 1st argument should return the 1st argument' => ['count', 'count', null, null, null]; } } diff --git a/tests/Unit/ExpressionLanguage/ArgumentGuesser/Factory/MinArgumentGuesserTest.php b/tests/Unit/ExpressionLanguage/ArgumentGuesser/Factory/MinArgumentGuesserTest.php new file mode 100644 index 0000000..f085848 --- /dev/null +++ b/tests/Unit/ExpressionLanguage/ArgumentGuesser/Factory/MinArgumentGuesserTest.php @@ -0,0 +1,55 @@ + + */ + public function arguments(): iterable + { + yield 'all arguments set not null should return null' => [null, null, null, null, null]; + yield 'a non null method and a numeric value as 2nd argument should return the 2nd argument as int' => [ + 2, + 'find', + '2', + null, + null, + ]; + } +}