Skip to content

Commit

Permalink
Merge pull request #34 from HarmonyIO/url-validators
Browse files Browse the repository at this point in the history
Implemented URL validation rules
  • Loading branch information
PeeHaa committed Dec 6, 2018
2 parents 383c32f + a1567dc commit 7a355ac
Show file tree
Hide file tree
Showing 5 changed files with 372 additions and 0 deletions.
48 changes: 48 additions & 0 deletions src/Rule/Network/Url/OkResponse.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php declare(strict_types=1);

namespace HarmonyIO\Validation\Rule\Network\Url;

use Amp\Artax\DnsException;
use Amp\Promise;
use Amp\Success;
use HarmonyIO\HttpClient\Client\Client;
use HarmonyIO\HttpClient\Message\Request;
use HarmonyIO\HttpClient\Message\Response;
use HarmonyIO\Validation\Rule\Rule;
use function Amp\call;

final class OkResponse implements Rule
{
/** @var Client */
private $httpClient;

public function __construct(Client $httpClient)
{
$this->httpClient = $httpClient;
}

/**
* {@inheritdoc}
*/
public function validate($value): Promise
{
if (!is_string($value)) {
return new Success(false);
}

return call(function () use ($value) {
if (!yield (new Url())->validate($value)) {
return false;
}

try {
/** @var Response $response */
$response = yield $this->httpClient->request(new Request($value));
} catch (DnsException $e) {
return false;
}

return $response->getNumericalStatusCode() === 200;
});
}
}
22 changes: 22 additions & 0 deletions src/Rule/Network/Url/Url.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php declare(strict_types=1);

namespace HarmonyIO\Validation\Rule\Network\Url;

use Amp\Promise;
use Amp\Success;
use HarmonyIO\Validation\Rule\Rule;

final class Url implements Rule
{
/**
* {@inheritdoc}
*/
public function validate($value): Promise
{
if (!is_string($value)) {
return new Success(false);
}

return new Success(filter_var($value, FILTER_VALIDATE_URL) !== false);
}
}
52 changes: 52 additions & 0 deletions tests/Integration/Rule/Network/Url/OkResponseTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php declare(strict_types=1);

namespace HarmonyIO\ValidationTest\Integration\Rule\Network\Url;

use Amp\Artax\DefaultClient;
use Amp\Redis\Client as RedisClient;
use HarmonyIO\Cache\Provider\Redis;
use HarmonyIO\HttpClient\Client\ArtaxClient;
use HarmonyIO\PHPUnitExtension\TestCase;
use HarmonyIO\Validation\Rule\Network\Url\OkResponse;

class OkResponseTest extends TestCase
{
/** @var ArtaxClient */
private $httpClient;

//phpcs:ignore SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingReturnTypeHint
public function setUp()
{
$this->httpClient = new ArtaxClient(new DefaultClient(), new Redis(new RedisClient('tcp://localhost:6379')));
}

public function testValidateReturnsFalseOnNotFoundResponse(): void
{
$this->assertFalse((new OkResponse($this->httpClient))->validate('http://pieterhordijk.com/dlksjksjfkhdsfjk'));
}

public function testValidateReturnsFalseOnNonExistingDomain(): void
{
$this->assertFalse((new OkResponse($this->httpClient))->validate('http://dkhj3kry43iufhr3e.example.com'));
}

public function testValidateReturnsFalseWhenResponseHasErrorStatusCode(): void
{
$this->assertFalse((new OkResponse($this->httpClient))->validate('https://httpbin.org/status/500'));
}

public function testValidateReturnsTrueOnRedirectedOkResponse(): void
{
$this->assertTrue((new OkResponse($this->httpClient))->validate('http://pieterhordijk.com'));
}

public function testValidateReturnsTrueOnOkResponse(): void
{
$this->assertTrue((new OkResponse($this->httpClient))->validate('https://pieterhordijk.com'));
}

public function testValidateReturnsTrueOnOkResponseWithPath(): void
{
$this->assertTrue((new OkResponse($this->httpClient))->validate('https://pieterhordijk.com/contact'));
}
}
164 changes: 164 additions & 0 deletions tests/Unit/Rule/Network/Url/OkResponseTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
<?php declare(strict_types=1);

namespace HarmonyIO\ValidationTest\Unit\Rule\Network\Url;

use Amp\Success;
use HarmonyIO\HttpClient\Client\Client;
use HarmonyIO\HttpClient\Message\Request;
use HarmonyIO\HttpClient\Message\Response;
use HarmonyIO\PHPUnitExtension\TestCase;
use HarmonyIO\Validation\Rule\Network\Url\OkResponse;
use HarmonyIO\Validation\Rule\Rule;
use PHPUnit\Framework\MockObject\MockObject;
use function Amp\Promise\wait;

class OkResponseTest extends TestCase
{
/** @var MockObject|Client */
private $httpClient;

//phpcs:ignore SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingReturnTypeHint
public function setUp()
{
$this->httpClient = $this->createMock(Client::class);
}

public function testRuleImplementsInterface(): void
{
$this->assertInstanceOf(Rule::class, new OkResponse($this->httpClient));
}

public function testValidateReturnsFalseWhenPassingAnInteger(): void
{
$this->assertFalse((new OkResponse($this->httpClient))->validate(1));
}

public function testValidateReturnsFalseWhenPassingAFloat(): void
{
$this->assertFalse((new OkResponse($this->httpClient))->validate(1.1));
}

public function testValidateReturnsFalseWhenPassingABoolean(): void
{
$this->assertFalse((new OkResponse($this->httpClient))->validate(true));
}

public function testValidateReturnsFalseWhenPassingAnArray(): void
{
$this->assertFalse((new OkResponse($this->httpClient))->validate([]));
}

public function testValidateReturnsFalseWhenPassingAnObject(): void
{
$this->assertFalse((new OkResponse($this->httpClient))->validate(new \DateTimeImmutable()));
}

public function testValidateReturnsFalseWhenPassingNull(): void
{
$this->assertFalse((new OkResponse($this->httpClient))->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 OkResponse($this->httpClient))->validate($resource));

fclose($resource);
}

public function testValidateReturnsFalseWhenPassingACallable(): void
{
$this->assertFalse((new OkResponse($this->httpClient))->validate(static function (): void {
}));
}

public function testValidateReturnsFalseWhenPassingAUrlWithoutProtocol(): void
{
$this->assertFalse((new OkResponse($this->httpClient))->validate('pieterhordijk.com'));
}

public function testValidateReturnsFalseWhenPassingAUrlWithoutHost(): void
{
$this->assertFalse((new OkResponse($this->httpClient))->validate('https://'));
}

public function testValidatePassesUrlToClient(): void
{
$this->httpClient
->method('request')
->willReturnCallback(function (Request $request) {
$this->assertSame('https://pieterhordijk.com', $request->getArtaxRequest()->getUri());

$response = $this->createMock(Response::class);

$response
->method('getBody')
->willReturn('foo')
;

return new Success($response);
})
;

wait((new OkResponse($this->httpClient))->validate('https://pieterhordijk.com'));
}

public function testValidateReturnsFalseWhenRequestResultsInANon200Response(): void
{
$this->httpClient
->method('request')
->willReturnCallback(function (Request $request) {
$this->assertSame('https://pieterhordijk.com/foobar', $request->getArtaxRequest()->getUri());

$response = $this->createMock(Response::class);

$response
->method('getBody')
->willReturn('foo')
;

$response
->method('getNumericalStatusCode')
->willReturn(404)
;

return new Success($response);
})
;

$this->assertFalse((new OkResponse($this->httpClient))->validate('https://pieterhordijk.com/foobar'));
}

public function testValidateReturnsTrueWhenClientReturnsOkResponse(): void
{
$this->httpClient
->method('request')
->willReturnCallback(function (Request $request) {
$this->assertSame('https://pieterhordijk.com/contact', $request->getArtaxRequest()->getUri());

$response = $this->createMock(Response::class);

$response
->method('getBody')
->willReturn('foo')
;

$response
->method('getNumericalStatusCode')
->willReturn(200)
;

return new Success($response);
})
;

$this->assertTrue((new OkResponse($this->httpClient))->validate('https://pieterhordijk.com/contact'));
}
}
86 changes: 86 additions & 0 deletions tests/Unit/Rule/Network/Url/UrlTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?php declare(strict_types=1);

namespace HarmonyIO\ValidationTest\Unit\Rule\Network\Url;

use HarmonyIO\PHPUnitExtension\TestCase;
use HarmonyIO\Validation\Rule\Network\Url\Url;
use HarmonyIO\Validation\Rule\Rule;

class UrlTest extends TestCase
{
public function testRuleImplementsInterface(): void
{
$this->assertInstanceOf(Rule::class, new Url());
}

public function testValidateReturnsFalseWhenPassingAnInteger(): void
{
$this->assertFalse((new Url())->validate(1));
}

public function testValidateReturnsFalseWhenPassingAFloat(): void
{
$this->assertFalse((new Url())->validate(1.1));
}

public function testValidateReturnsFalseWhenPassingABoolean(): void
{
$this->assertFalse((new Url())->validate(true));
}

public function testValidateReturnsFalseWhenPassingAnArray(): void
{
$this->assertFalse((new Url())->validate([]));
}

public function testValidateReturnsFalseWhenPassingAnObject(): void
{
$this->assertFalse((new Url())->validate(new \DateTimeImmutable()));
}

public function testValidateReturnsFalseWhenPassingNull(): void
{
$this->assertFalse((new Url())->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 Url())->validate($resource));

fclose($resource);
}

public function testValidateReturnsFalseWhenPassingACallable(): void
{
$this->assertFalse((new Url())->validate(static function (): void {
}));
}

public function testValidateReturnsFalseWhenPassingAUrlWithoutProtocol(): void
{
$this->assertFalse((new Url())->validate('pieterhordijk.com'));
}

public function testValidateReturnsFalseWhenPassingAUrlWithoutHost(): void
{
$this->assertFalse((new Url())->validate('https://'));
}

public function testValidateReturnsTrueWhenPassingAValidUrl(): void
{
$this->assertTrue((new Url())->validate('https://pieterhordijk.com'));
}

public function testValidateReturnsTrueWhenPassingAValidUrlWithPort(): void
{
$this->assertTrue((new Url())->validate('https://pieterhordijk.com:1337'));
}
}

0 comments on commit 7a355ac

Please sign in to comment.