Skip to content

Commit

Permalink
Support an optional "min" argument for the FactoryAdapter to be able …
Browse files Browse the repository at this point in the history
…to handle functions like "randomSet"
  • Loading branch information
J-Ben87 committed Sep 5, 2023
1 parent d61a3c5 commit f3667af
Show file tree
Hide file tree
Showing 12 changed files with 202 additions and 18 deletions.
5 changes: 5 additions & 0 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,19 @@ 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 {
if (null === $method) {
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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,18 @@ 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 {
if (\is_array($method)) {
return $method;
}

if (\is_array($min)) {
return $min;
}

if (\is_array($attributes)) {
return $attributes;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace Presta\BehatEvaluator\ExpressionLanguage\ArgumentGuesser\Factory;

final class MinArgumentGuesser implements ArgumentGuesserInterface
{
public function __invoke(
array|string|null $method,
array|string|null $min,
array|string|null $attributes,
string|null $accessor,
): int|null {
if (\is_numeric($min)) {
return (int)$min;
}

return null;
}
}
25 changes: 21 additions & 4 deletions src/ExpressionLanguage/Evaluator/FactoryEvaluator.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Presta\BehatEvaluator\ExpressionLanguage\ArgumentGuesser\Factory\AccessorArgumentGuesser;
use Presta\BehatEvaluator\ExpressionLanguage\ArgumentGuesser\Factory\AttributesArgumentGuesser;
use Presta\BehatEvaluator\ExpressionLanguage\ArgumentGuesser\Factory\MethodArgumentGuesser;
use Presta\BehatEvaluator\ExpressionLanguage\ArgumentGuesser\Factory\MinArgumentGuesser;
use Presta\BehatEvaluator\Foundry\FactoryClassFactory;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Zenstruck\Foundry\Proxy;
Expand All @@ -22,22 +23,36 @@ public function __construct(private readonly FactoryClassFactory $factoryClassFa
/**
* @param array<string, mixed> $arguments
* @param string|array<string, mixed>|null $method
* @param string|array<string, mixed>|null $min
* @param string|array<string, mixed>|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);

Expand All @@ -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),
};

Expand Down
30 changes: 30 additions & 0 deletions tests/Integration/Adapter/FactoryAdapterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -81,6 +90,27 @@ public function values(): iterable
2,
'<factory("user", "count")>',
];
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(),
),
),
'<factory("user", "randomSet", 2)>',
];
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']),
),
),
'<factory("user", "randomSet", 1, {"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',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,42 +18,63 @@ 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));
}

/**
* @return iterable<string, array{
* 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' => [
'lastname',
'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',
];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,34 +18,46 @@ 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));
}

/**
* @return iterable<string, array{
* 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,
];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,30 +17,33 @@ 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));
}

/**
* @return iterable<string, array{
* 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];
}
}
Loading

0 comments on commit f3667af

Please sign in to comment.