-
Notifications
You must be signed in to change notification settings - Fork 136
StaticValidator test is incorrect (false positive) #16
Comments
Question, @alexdenvir : Does #15 resolve the issue? or does further diligence need to be done in the |
@weierophinney - Yes, #15 resolved the issue, although it was only one way to resolve it. As explained in that PR - the Between validator currently requires options to be passed through the constructor, but it only checks that 2 options have been set (because the rest of the if statement currently always evaluates to true). protected $options =>
array(5) {
'inclusive' =>
bool(true)
'min' =>
int(0)
'max' =>
int(9223372036854775807)
[0] =>
int(1)
[1] =>
int(10)
} Because Between also has default options (which were removed in #15), the validator still works, but not as expected, thus the false positive on this test. My PR resolved this by removing the default options, and throwing an exception in the /**
* Returns true if and only if $value meets the validation requirements
*
* If $value fails validation, then this method returns false, and
* getMessages() will return an array of messages that explain why the
* validation failed.
*
* @param mixed $value
* @return bool
* @throws Exception\RuntimeException If validation of $value is impossible
*/
public function isValid($value); Another option is to rework the Between constructor and remove the default options: /**
* Options for the between validator
*
* @var array
*/
protected $options = array(
'inclusive' => true, // Whether to do inclusive comparisons, allowing equivalence to min and/or max
'min' => null,
'max' => null,
);
/**
* Sets validator options
* Accepts the following option keys:
* 'min' => scalar, minimum border
* 'max' => scalar, maximum border
* 'inclusive' => boolean, inclusive border values
*
* @param array|Traversable $options
*
* @throws Exception\InvalidArgumentException
*/
public function __construct($options = null)
{
if ($options instanceof Traversable) {
$options = ArrayUtils::iteratorToArray($options);
}
if (!is_array($options)) {
$options = func_get_args();
$temp['min'] = array_shift($options);
if (!empty($options)) {
$temp['max'] = array_shift($options);
}
if (!empty($options)) {
$temp['inclusive'] = array_shift($options);
}
$options = $temp;
}
parent::__construct($options);
if ($this->getMin() === null || $this->getMax() === null) {
throw new Exception\InvalidArgumentException("Missing option. 'min' and 'max' have to be given");
}
} Alternatively, StaticValidator could be updated to check if the options passed to it is an associative array, and if not pass each array value as a separate scalar value. /**
* @param mixed $value
* @param string $classBaseName
* @param array $args OPTIONAL
* @return bool
*/
public static function execute($value, $classBaseName, array $args = array())
public static function execute($value, $classBaseName, array $args = array())
{
$plugins = static::getPluginManager();
if (array_keys($args) !== range(0, count($args) - 1)) {
$validator = $plugins->get($classBaseName, ...$args);
} else {
$validator = $plugins->get($classBaseName, $args);
}
return $validator->isValid($value);
} Obviously I'm not saying the minimum version of php to 5.6 (at least yet 😛). I am happy to throw together a new pull request to address this with whatever option I've given or alternatives suggested by others is preferred |
Forgot to mention, obviously all the suggestions I've made require changes to tests as well |
@alexdenvir We can use
The main thing right now is that the tests are simply written incorrectly, and need to be updated to illustrate correct usage. We can add a few more test cases as well to validate that they are passing the options correctly (e.g., use -5 -> -1 as the range for the Between validator). I'm noting that on my todo. |
@weierophinney Yeah, the argument unpacking suggestion was put together purely to show an alternative (which would need more work than what's shown here). I honestly didn't think of using ArrayUtils to check if an array is associative or not, makes sense really (although I cant see I'm going to throw together another pull request that will fix up the tests. I still think the Between validator needs to be fixed as well though - perhaps an alternate version of the reworked constructor/no defaults suggestion I made above - at least until stateless validators become a thing: public function __construct($options = null)
{
if ($options instanceof Traversable) {
$options = ArrayUtils::iteratorToArray($options);
}
if (!is_array($options)) {
$options = func_get_args();
}
if (ArrayUtils::hasNumericKeys($options)) {
$temp['min'] = array_shift($options);
if (!empty($options)) {
$temp['max'] = array_shift($options);
}
if (!empty($options)) {
$temp['inclusive'] = array_shift($options);
}
$options = $temp;
}
parent::__construct($options);
if ($this->getMin() === null || $this->getMax() === null) {
throw new Exception\InvalidArgumentException("Missing option. 'min' and 'max' have to be given");
}
} |
Previously, the tests were incorrectly passing a non-associative array of options to `execute()`, which implied that they would be passed as constructor arguments, which was not the case. In this particular case, there was a false positive, as the value being validated was valid for the default values present in the instance. This patch removes that assertion, and splits the test into two, one for validating properly formed calls to the method with options as an associative array, and one for calling it with non-associative options. In the second, the test asserts that an `InvalidArgumentException` is expected.
Forwarding my message posted on #26 for a single thread of discussion: I see three cases here:
Because argument name is Provide a constructor argument list allow to instantiate constructors with different constructor signatures and not only the ones based in AbstractValidator. My opinion is won't fix. |
As noted on #26, I disagree with all 3 assertions.
However, we decided when refactoring for ZF2 to make this a static class. Additionally, we decided that all validators — indeed, anything that would be managed by a plugin manager, including filters, view helpers, controller plugins, and more — would allow passing an array of options to the constructor; this allowed us to create a simpler paradigm for plugins, as you can then pass in named arguments, instead of needing to know argument order. We refactored plugins to allow discrete arguments, with all arguments optional, and the first allowing overloading so that an associative array of options detailing all constructor options (and otherwise) could be passed in at once. If your plugin does not follow that paradigm, and you want to be able to pass in options, you must write a factory, and the factory constructor must accept the creation options to its constructor; you can then decide what to do with them when the factory is invoked. The points I'm making here are:
This is the only supported, documented use case. The tests were incorrect, and we were not validating that we had valid arguments passed to |
I've had a read over of #26, and I'm happy putting my 👍 to it as issue reporter that it fixes the error with the test. The
Having read this comment, does it make sense for |
Previously, the tests were incorrectly passing a non-associative array of options to `execute()`, which implied that they would be passed as constructor arguments, which was not the case. In this particular case, there was a false positive, as the value being validated was valid for the default values present in the instance. This patch removes that assertion, and splits the test into two, one for validating properly formed calls to the method with options as an associative array, and one for calling it with non-associative options. In the second, the test asserts that an `InvalidArgumentException` is expected.
Opening an issue as a result of findings in my pull request #15. Here the issue has been removed, but not explicitly addressed.
There is a test in StaticValidatorTest (and ValidatorChainTest) which test the StaticValidator and Between:
The first assertion implies (or at least creates some confusion, as evidenced in the pull request) that you can pass an array of options in, and they are passed to the actual validator as individual parameters. This is definitely not the case.
In this example, the Between validator will end up with an option called '0', with a value of '1', and an option called '1', with a value of '10'. The test currently passes, because the Between validator has a default value for min and max (0 and PHP_INT_MAX respectively).
To show more detail:
I should point out that in my pull request, I have removed this assertion from the tests, as the Between validator no longer has default values.
The text was updated successfully, but these errors were encountered: