Skip to content

Commit

Permalink
Implemented numerical range validation rule
Browse files Browse the repository at this point in the history
  • Loading branch information
PeeHaa committed Dec 3, 2018
1 parent 96ddf46 commit c0c622d
Show file tree
Hide file tree
Showing 6 changed files with 272 additions and 0 deletions.
16 changes: 16 additions & 0 deletions src/Exception/InvalidNumericValue.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php declare(strict_types=1);

namespace HarmonyIO\Validation\Exception;

class InvalidNumericValue extends Exception
{
private const MESSAGE = 'Value (`%s`) must be a numeric value.';

/**
* @param mixed $value
*/
public function __construct($value)
{
parent::__construct(sprintf(self::MESSAGE, $value));
}
}
25 changes: 25 additions & 0 deletions src/Exception/InvalidNumericalRange.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php declare(strict_types=1);

namespace HarmonyIO\Validation\Exception;

final class InvalidNumericalRange extends Exception
{
private const MESSAGE_TEMPLATE = 'The minimum (`%s`) can not be greater than the maximum (`%s`).';

/**
* @param mixed $minimum
* @param mixed $maximum
*/
public function __construct($minimum, $maximum)
{
if (!is_numeric($minimum)) {
throw new InvalidNumericValue($minimum);
}

if (!is_numeric($maximum)) {
throw new InvalidNumericValue($maximum);
}

parent::__construct(sprintf(self::MESSAGE_TEMPLATE, $minimum, $maximum));
}
}
54 changes: 54 additions & 0 deletions src/Rule/Numeric/Range.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php declare(strict_types=1);

namespace HarmonyIO\Validation\Rule\Numeric;

use Amp\Promise;
use HarmonyIO\Validation\Exception\InvalidNumericalRange;
use HarmonyIO\Validation\Exception\InvalidNumericValue;
use HarmonyIO\Validation\Rule\Rule;
use function Amp\call;

final class Range implements Rule
{
/** @var mixed */
private $minimumValue;

/** @var mixed */
private $maximumValue;

/**
* @param mixed $minimumValue
* @param mixed $maximumValue
*/
public function __construct($minimumValue, $maximumValue)
{
if (!is_numeric($minimumValue)) {
throw new InvalidNumericValue($minimumValue);
}

if (!is_numeric($maximumValue)) {
throw new InvalidNumericValue($maximumValue);
}

if ($minimumValue > $maximumValue) {
throw new InvalidNumericalRange($minimumValue, $maximumValue);
}

$this->minimumValue = $minimumValue;
$this->maximumValue = $maximumValue;
}

/**
* {@inheritdoc}
*/
public function validate($value): Promise
{
return call(function () use ($value) {
if (!yield (new NumericType())->validate($value)) {
return false;
}

return $value >= $this->minimumValue && $value <= $this->maximumValue;
});
}
}
19 changes: 19 additions & 0 deletions tests/Unit/Exception/InvalidNumericValueTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php declare(strict_types=1);

namespace HarmonyIO\ValidationTest\Unit\Exception;

use HarmonyIO\PHPUnitExtension\TestCase;
use HarmonyIO\Validation\Exception\InvalidNumericValue;

class InvalidNumericValueTest extends TestCase
{
public function testMessageIsFormattedCorrectly(): void
{
$typeException = new InvalidNumericValue('randomstring');

$this->assertSame(
'Value (`randomstring`) must be a numeric value.',
$typeException->getMessage()
);
}
}
36 changes: 36 additions & 0 deletions tests/Unit/Exception/InvalidNumericalRangeTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php declare(strict_types=1);

namespace HarmonyIO\ValidationTest\Unit\Exception;

use HarmonyIO\PHPUnitExtension\TestCase;
use HarmonyIO\Validation\Exception\InvalidNumericalRange;
use HarmonyIO\Validation\Exception\InvalidNumericValue;

class InvalidNumericalRangeTest extends TestCase
{
public function testConstructorThrowsUpWhenPassingANonNumericalMinimumValue(): void
{
$this->expectException(InvalidNumericValue::class);
$this->expectExceptionMessage('Value (`not a numerical value`) must be a numeric value.');

new InvalidNumericalRange('not a numerical value', 18);
}

public function testConstructorThrowsUpWhenPassingANonNumericalMaximumValue(): void
{
$this->expectException(InvalidNumericValue::class);
$this->expectExceptionMessage('Value (`not a numerical value`) must be a numeric value.');

new InvalidNumericalRange(18, 'not a numerical value');
}

public function testMessageIsFormattedCorrectly(): void
{
$typeException = new InvalidNumericalRange(21, 18);

$this->assertSame(
'The minimum (`21`) can not be greater than the maximum (`18`).',
$typeException->getMessage()
);
}
}
122 changes: 122 additions & 0 deletions tests/Unit/Rule/Numeric/RangeTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
<?php declare(strict_types=1);

namespace HarmonyIO\ValidationTest\Unit\Rule\Numeric;

use HarmonyIO\PHPUnitExtension\TestCase;
use HarmonyIO\Validation\Exception\InvalidNumericalRange;
use HarmonyIO\Validation\Exception\InvalidNumericValue;
use HarmonyIO\Validation\Rule\Numeric\Range;
use HarmonyIO\Validation\Rule\Rule;

class RangeTest extends TestCase
{
public function testConstructorThrowsOnNonNumericMinimumValue(): void
{
$this->expectException(InvalidNumericValue::class);
$this->expectExceptionMessage('Value (`one`) must be a numeric value.');

new Range('one', 2);
}

public function testConstructorThrowsOnNonNumericMaximumValue(): void
{
$this->expectException(InvalidNumericValue::class);
$this->expectExceptionMessage('Value (`two`) must be a numeric value.');

new Range(1, 'two');
}

public function testConstructorThrowsWhenMinimumValueIsGreaterThanMaximumValue(): void
{
$this->expectException(InvalidNumericalRange::class);
$this->expectExceptionMessage('The minimum (`51`) can not be greater than the maximum (`50`).');

new Range(51, 50);
}

public function testRuleImplementsInterface(): void
{
$this->assertInstanceOf(Rule::class, new Range(13, 16));
}

public function testValidateReturnsTrueWhenPassingAnInteger(): void
{
$this->assertTrue((new Range(13, 16))->validate(14));
}

public function testValidateReturnsTrueWhenPassingAFloat(): void
{
$this->assertTrue((new Range(13, 16))->validate(14.1));
}

public function testValidateReturnsFalseWhenPassingABoolean(): void
{
$this->assertFalse((new Range(13, 16))->validate(true));
}

public function testValidateReturnsFalseWhenPassingAnArray(): void
{
$this->assertFalse((new Range(13, 16))->validate([]));
}

public function testValidateReturnsFalseWhenPassingAnObject(): void
{
$this->assertFalse((new Range(13, 16))->validate(new \DateTimeImmutable()));
}

public function testValidateReturnsFalseWhenPassingNull(): void
{
$this->assertFalse((new Range(13, 16))->validate(null));
}

public function testValidateReturnsFalseWhenPassingAResource(): void
{
$resource = fopen('php://memory', 'r');

if ($resource === false) {
$this->fail('Could not open the memory stream used for the test');

return;
}

$this->assertFalse((new Range(13, 16))->validate($resource));

fclose($resource);
}

public function testValidateReturnsFalseWhenPassingACallable(): void
{
$this->assertFalse((new Range(13, 16))->validate(static function (): void {
}));
}

public function testValidateReturnsTrueWhenPassingAnIntegerAsAString(): void
{
$this->assertTrue((new Range(13, 16))->validate('14'));
}

public function testValidateReturnsTrueWhenPassingAFloatAsAString(): void
{
$this->assertTrue((new Range(13, 16))->validate('14.1'));
}

public function testValidateReturnsFalseWhenPassingAFloatValueLessThanTheMinimumValue(): void
{
$this->assertFalse((new Range(13, 16))->validate(12.9));
}

public function testValidateReturnsFalseWhenPassingAFloatValueMoreThanTheMaximumValue(): void
{
$this->assertFalse((new Range(13, 16))->validate(16.1));
}

public function testValidateReturnsFalseWhenPassingAnIntegerValueLessThanTheMinimumValue(): void
{
$this->assertFalse((new Range(13, 16))->validate(12));
}

public function testValidateReturnsFalseWhenPassingAnIntegerValueMoreThanTheMaximumValue(): void
{
$this->assertFalse((new Range(13, 16))->validate(17));
}
}

0 comments on commit c0c622d

Please sign in to comment.