diff options
author | marvin-borner@live.com | 2018-04-10 21:50:16 +0200 |
---|---|---|
committer | marvin-borner@live.com | 2018-04-10 21:54:48 +0200 |
commit | fc9401f04a3aca5abb22f87ebc210de8afe11d32 (patch) | |
tree | b0b310f3581764ec3955f4e496a05137a32951c3 /assets/php/vendor/react/dns/tests | |
parent | 286d643180672f20526f3dc3bd19d7b751e2fa97 (diff) |
Initial Commit
Diffstat (limited to 'assets/php/vendor/react/dns/tests')
20 files changed, 2386 insertions, 0 deletions
diff --git a/assets/php/vendor/react/dns/tests/CallableStub.php b/assets/php/vendor/react/dns/tests/CallableStub.php new file mode 100644 index 0000000..a34a263 --- /dev/null +++ b/assets/php/vendor/react/dns/tests/CallableStub.php @@ -0,0 +1,10 @@ +<?php + +namespace React\Tests\Dns; + +class CallableStub +{ + public function __invoke() + { + } +} diff --git a/assets/php/vendor/react/dns/tests/Config/ConfigTest.php b/assets/php/vendor/react/dns/tests/Config/ConfigTest.php new file mode 100644 index 0000000..8020408 --- /dev/null +++ b/assets/php/vendor/react/dns/tests/Config/ConfigTest.php @@ -0,0 +1,189 @@ +<?php + +namespace React\Tests\Dns\Config; + +use React\Tests\Dns\TestCase; +use React\Dns\Config\Config; + +class ConfigTest extends TestCase +{ + public function testLoadsSystemDefault() + { + $config = Config::loadSystemConfigBlocking(); + + $this->assertInstanceOf('React\Dns\Config\Config', $config); + } + + public function testLoadsDefaultPath() + { + if (DIRECTORY_SEPARATOR === '\\') { + $this->markTestSkipped('Not supported on Windows'); + } + + $config = Config::loadResolvConfBlocking(); + + $this->assertInstanceOf('React\Dns\Config\Config', $config); + } + + public function testLoadsFromExplicitPath() + { + $config = Config::loadResolvConfBlocking(__DIR__ . '/../Fixtures/etc/resolv.conf'); + + $this->assertEquals(array('8.8.8.8'), $config->nameservers); + } + + /** + * @expectedException RuntimeException + */ + public function testLoadThrowsWhenPathIsInvalid() + { + Config::loadResolvConfBlocking(__DIR__ . '/invalid.conf'); + } + + public function testParsesSingleEntryFile() + { + $contents = 'nameserver 8.8.8.8'; + $expected = array('8.8.8.8'); + + $config = Config::loadResolvConfBlocking('data://text/plain;base64,' . base64_encode($contents)); + $this->assertEquals($expected, $config->nameservers); + } + + public function testParsesNameserverEntriesFromAverageFileCorrectly() + { + $contents = '# +# Mac OS X Notice +# +# This file is not used by the host name and address resolution +# or the DNS query routing mechanisms used by most processes on +# this Mac OS X system. +# +# This file is automatically generated. +# +domain v.cablecom.net +nameserver 127.0.0.1 +nameserver ::1 +'; + $expected = array('127.0.0.1', '::1'); + + $config = Config::loadResolvConfBlocking('data://text/plain;base64,' . base64_encode($contents)); + $this->assertEquals($expected, $config->nameservers); + } + + public function testParsesEmptyFileWithoutNameserverEntries() + { + $contents = ''; + $expected = array(); + + $config = Config::loadResolvConfBlocking('data://text/plain;base64,'); + $this->assertEquals($expected, $config->nameservers); + } + + public function testParsesFileAndIgnoresCommentsAndInvalidNameserverEntries() + { + $contents = ' +# nameserver 1.2.3.4 +; nameserver 2.3.4.5 + +nameserver 3.4.5.6 # nope +nameserver 4.5.6.7 5.6.7.8 + nameserver 6.7.8.9 +NameServer 7.8.9.10 +'; + $expected = array(); + + $config = Config::loadResolvConfBlocking('data://text/plain;base64,' . base64_encode($contents)); + $this->assertEquals($expected, $config->nameservers); + } + + public function testLoadsFromWmicOnWindows() + { + if (DIRECTORY_SEPARATOR !== '\\') { + $this->markTestSkipped('Only on Windows'); + } + + $config = Config::loadWmicBlocking(); + + $this->assertInstanceOf('React\Dns\Config\Config', $config); + } + + public function testLoadsSingleEntryFromWmicOutput() + { + $contents = ' +Node,DNSServerSearchOrder +ACE, +ACE,{192.168.2.1} +ACE, +'; + $expected = array('192.168.2.1'); + + $config = Config::loadWmicBlocking($this->echoCommand($contents)); + + $this->assertEquals($expected, $config->nameservers); + } + + public function testLoadsEmptyListFromWmicOutput() + { + $contents = ' +Node,DNSServerSearchOrder +ACE, +'; + $expected = array(); + + $config = Config::loadWmicBlocking($this->echoCommand($contents)); + + $this->assertEquals($expected, $config->nameservers); + } + + public function testLoadsSingleEntryForMultipleNicsFromWmicOutput() + { + $contents = ' +Node,DNSServerSearchOrder +ACE, +ACE,{192.168.2.1} +ACE, +ACE,{192.168.2.2} +ACE, +'; + $expected = array('192.168.2.1', '192.168.2.2'); + + $config = Config::loadWmicBlocking($this->echoCommand($contents)); + + $this->assertEquals($expected, $config->nameservers); + } + + public function testLoadsMultipleEntriesForSingleNicWithSemicolonFromWmicOutput() + { + $contents = ' +Node,DNSServerSearchOrder +ACE, +ACE,{192.168.2.1;192.168.2.2} +ACE, +'; + $expected = array('192.168.2.1', '192.168.2.2'); + + $config = Config::loadWmicBlocking($this->echoCommand($contents)); + + $this->assertEquals($expected, $config->nameservers); + } + + public function testLoadsMultipleEntriesForSingleNicWithQuotesFromWmicOutput() + { + $contents = ' +Node,DNSServerSearchOrder +ACE, +ACE,{"192.168.2.1","192.168.2.2"} +ACE, +'; + $expected = array('192.168.2.1', '192.168.2.2'); + + $config = Config::loadWmicBlocking($this->echoCommand($contents)); + + $this->assertEquals($expected, $config->nameservers); + } + + private function echoCommand($output) + { + return 'echo ' . escapeshellarg($output); + } +} diff --git a/assets/php/vendor/react/dns/tests/Config/FilesystemFactoryTest.php b/assets/php/vendor/react/dns/tests/Config/FilesystemFactoryTest.php new file mode 100644 index 0000000..bb9eac7 --- /dev/null +++ b/assets/php/vendor/react/dns/tests/Config/FilesystemFactoryTest.php @@ -0,0 +1,70 @@ +<?php + +namespace React\Test\Dns\Config; + +use PHPUnit\Framework\TestCase; +use React\Dns\Config\FilesystemFactory; + +class FilesystemFactoryTest extends TestCase +{ + /** @test */ + public function parseEtcResolvConfShouldParseCorrectly() + { + $contents = '# +# Mac OS X Notice +# +# This file is not used by the host name and address resolution +# or the DNS query routing mechanisms used by most processes on +# this Mac OS X system. +# +# This file is automatically generated. +# +domain v.cablecom.net +nameserver 127.0.0.1 +nameserver 8.8.8.8 +'; + $expected = array('127.0.0.1', '8.8.8.8'); + + $capturedConfig = null; + + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + $factory = new FilesystemFactory($loop); + $factory->parseEtcResolvConf($contents)->then(function ($config) use (&$capturedConfig) { + $capturedConfig = $config; + }); + + $this->assertNotNull($capturedConfig); + $this->assertSame($expected, $capturedConfig->nameservers); + } + + /** @test */ + public function createShouldLoadStuffFromFilesystem() + { + $this->markTestIncomplete('Filesystem API is incomplete'); + + $expected = array('8.8.8.8'); + + $triggerListener = null; + $capturedConfig = null; + + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + $loop + ->expects($this->once()) + ->method('addReadStream') + ->will($this->returnCallback(function ($stream, $listener) use (&$triggerListener) { + $triggerListener = function () use ($stream, $listener) { + call_user_func($listener, $stream); + }; + })); + + $factory = new FilesystemFactory($loop); + $factory->create(__DIR__.'/../Fixtures/etc/resolv.conf')->then(function ($config) use (&$capturedConfig) { + $capturedConfig = $config; + }); + + $triggerListener(); + + $this->assertNotNull($capturedConfig); + $this->assertSame($expected, $capturedConfig->nameservers); + } +} diff --git a/assets/php/vendor/react/dns/tests/Config/HostsFileTest.php b/assets/php/vendor/react/dns/tests/Config/HostsFileTest.php new file mode 100644 index 0000000..ff74ad2 --- /dev/null +++ b/assets/php/vendor/react/dns/tests/Config/HostsFileTest.php @@ -0,0 +1,170 @@ +<?php + +namespace React\Tests\Dns\Config; + +use React\Tests\Dns\TestCase; +use React\Dns\Config\HostsFile; + +class HostsFileTest extends TestCase +{ + public function testLoadsFromDefaultPath() + { + $hosts = HostsFile::loadFromPathBlocking(); + + $this->assertInstanceOf('React\Dns\Config\HostsFile', $hosts); + } + + public function testDefaultShouldHaveLocalhostMapped() + { + if (DIRECTORY_SEPARATOR === '\\') { + $this->markTestSkipped('Not supported on Windows'); + } + + $hosts = HostsFile::loadFromPathBlocking(); + + $this->assertContains('127.0.0.1', $hosts->getIpsForHost('localhost')); + } + + /** + * @expectedException RuntimeException + */ + public function testLoadThrowsForInvalidPath() + { + HostsFile::loadFromPathBlocking('does/not/exist'); + } + + public function testContainsSingleLocalhostEntry() + { + $hosts = new HostsFile('127.0.0.1 localhost'); + + $this->assertEquals(array('127.0.0.1'), $hosts->getIpsForHost('localhost')); + $this->assertEquals(array(), $hosts->getIpsForHost('example.com')); + } + + public function testNonIpReturnsNothingForInvalidHosts() + { + $hosts = new HostsFile('a b'); + + $this->assertEquals(array(), $hosts->getIpsForHost('a')); + $this->assertEquals(array(), $hosts->getIpsForHost('b')); + } + + public function testIgnoresIpv6ZoneId() + { + $hosts = new HostsFile('fe80::1%lo0 localhost'); + + $this->assertEquals(array('fe80::1'), $hosts->getIpsForHost('localhost')); + } + + public function testSkipsComments() + { + $hosts = new HostsFile('# start' . PHP_EOL .'#127.0.0.1 localhost' . PHP_EOL . '127.0.0.2 localhost # example.com'); + + $this->assertEquals(array('127.0.0.2'), $hosts->getIpsForHost('localhost')); + $this->assertEquals(array(), $hosts->getIpsForHost('example.com')); + } + + public function testContainsSingleLocalhostEntryWithCaseIgnored() + { + $hosts = new HostsFile('127.0.0.1 LocalHost'); + + $this->assertEquals(array('127.0.0.1'), $hosts->getIpsForHost('LOCALHOST')); + } + + public function testEmptyFileContainsNothing() + { + $hosts = new HostsFile(''); + + $this->assertEquals(array(), $hosts->getIpsForHost('example.com')); + } + + public function testSingleEntryWithMultipleNames() + { + $hosts = new HostsFile('127.0.0.1 localhost example.com'); + + $this->assertEquals(array('127.0.0.1'), $hosts->getIpsForHost('example.com')); + $this->assertEquals(array('127.0.0.1'), $hosts->getIpsForHost('localhost')); + } + + public function testMergesEntriesOverMultipleLines() + { + $hosts = new HostsFile("127.0.0.1 localhost\n127.0.0.2 localhost\n127.0.0.3 a localhost b\n127.0.0.4 a localhost"); + + $this->assertEquals(array('127.0.0.1', '127.0.0.2', '127.0.0.3', '127.0.0.4'), $hosts->getIpsForHost('localhost')); + } + + public function testMergesIpv4AndIpv6EntriesOverMultipleLines() + { + $hosts = new HostsFile("127.0.0.1 localhost\n::1 localhost"); + + $this->assertEquals(array('127.0.0.1', '::1'), $hosts->getIpsForHost('localhost')); + } + + public function testReverseLookup() + { + $hosts = new HostsFile('127.0.0.1 localhost'); + + $this->assertEquals(array('localhost'), $hosts->getHostsForIp('127.0.0.1')); + $this->assertEquals(array(), $hosts->getHostsForIp('192.168.1.1')); + } + + public function testReverseSkipsComments() + { + $hosts = new HostsFile("# start\n#127.0.0.1 localhosted\n127.0.0.2\tlocalhost\t# example.com\n\t127.0.0.3\t\texample.org\t\t"); + + $this->assertEquals(array(), $hosts->getHostsForIp('127.0.0.1')); + $this->assertEquals(array('localhost'), $hosts->getHostsForIp('127.0.0.2')); + $this->assertEquals(array('example.org'), $hosts->getHostsForIp('127.0.0.3')); + } + + public function testReverseNonIpReturnsNothing() + { + $hosts = new HostsFile('127.0.0.1 localhost'); + + $this->assertEquals(array(), $hosts->getHostsForIp('localhost')); + $this->assertEquals(array(), $hosts->getHostsForIp('127.0.0.1.1')); + } + + public function testReverseNonIpReturnsNothingForInvalidHosts() + { + $hosts = new HostsFile('a b'); + + $this->assertEquals(array(), $hosts->getHostsForIp('a')); + $this->assertEquals(array(), $hosts->getHostsForIp('b')); + } + + public function testReverseLookupReturnsLowerCaseHost() + { + $hosts = new HostsFile('127.0.0.1 LocalHost'); + + $this->assertEquals(array('localhost'), $hosts->getHostsForIp('127.0.0.1')); + } + + public function testReverseLookupChecksNormalizedIpv6() + { + $hosts = new HostsFile('FE80::00a1 localhost'); + + $this->assertEquals(array('localhost'), $hosts->getHostsForIp('fe80::A1')); + } + + public function testReverseLookupIgnoresIpv6ZoneId() + { + $hosts = new HostsFile('fe80::1%lo0 localhost'); + + $this->assertEquals(array('localhost'), $hosts->getHostsForIp('fe80::1')); + } + + public function testReverseLookupReturnsMultipleHostsOverSingleLine() + { + $hosts = new HostsFile("::1 ip6-localhost ip6-loopback"); + + $this->assertEquals(array('ip6-localhost', 'ip6-loopback'), $hosts->getHostsForIp('::1')); + } + + public function testReverseLookupReturnsMultipleHostsOverMultipleLines() + { + $hosts = new HostsFile("::1 ip6-localhost\n::1 ip6-loopback"); + + $this->assertEquals(array('ip6-localhost', 'ip6-loopback'), $hosts->getHostsForIp('::1')); + } +} diff --git a/assets/php/vendor/react/dns/tests/Fixtures/etc/resolv.conf b/assets/php/vendor/react/dns/tests/Fixtures/etc/resolv.conf new file mode 100644 index 0000000..cae093a --- /dev/null +++ b/assets/php/vendor/react/dns/tests/Fixtures/etc/resolv.conf @@ -0,0 +1 @@ +nameserver 8.8.8.8 diff --git a/assets/php/vendor/react/dns/tests/FunctionalResolverTest.php b/assets/php/vendor/react/dns/tests/FunctionalResolverTest.php new file mode 100644 index 0000000..0807e86 --- /dev/null +++ b/assets/php/vendor/react/dns/tests/FunctionalResolverTest.php @@ -0,0 +1,71 @@ +<?php + +namespace React\Tests\Dns; + +use React\Tests\Dns\TestCase; +use React\EventLoop\Factory as LoopFactory; +use React\Dns\Resolver\Resolver; +use React\Dns\Resolver\Factory; + +class FunctionalTest extends TestCase +{ + public function setUp() + { + $this->loop = LoopFactory::create(); + + $factory = new Factory(); + $this->resolver = $factory->create('8.8.8.8', $this->loop); + } + + public function testResolveLocalhostResolves() + { + $promise = $this->resolver->resolve('localhost'); + $promise->then($this->expectCallableOnce(), $this->expectCallableNever()); + + $this->loop->run(); + } + + /** + * @group internet + */ + public function testResolveGoogleResolves() + { + $promise = $this->resolver->resolve('google.com'); + $promise->then($this->expectCallableOnce(), $this->expectCallableNever()); + + $this->loop->run(); + } + + /** + * @group internet + */ + public function testResolveInvalidRejects() + { + $promise = $this->resolver->resolve('example.invalid'); + $promise->then($this->expectCallableNever(), $this->expectCallableOnce()); + + $this->loop->run(); + } + + public function testResolveCancelledRejectsImmediately() + { + $promise = $this->resolver->resolve('google.com'); + $promise->then($this->expectCallableNever(), $this->expectCallableOnce()); + $promise->cancel(); + + $time = microtime(true); + $this->loop->run(); + $time = microtime(true) - $time; + + $this->assertLessThan(0.1, $time); + } + + public function testInvalidResolverDoesNotResolveGoogle() + { + $factory = new Factory(); + $this->resolver = $factory->create('255.255.255.255', $this->loop); + + $promise = $this->resolver->resolve('google.com'); + $promise->then($this->expectCallableNever(), $this->expectCallableOnce()); + } +} diff --git a/assets/php/vendor/react/dns/tests/Model/MessageTest.php b/assets/php/vendor/react/dns/tests/Model/MessageTest.php new file mode 100644 index 0000000..53d6b28 --- /dev/null +++ b/assets/php/vendor/react/dns/tests/Model/MessageTest.php @@ -0,0 +1,30 @@ +<?php + +namespace React\Tests\Dns\Model; + +use PHPUnit\Framework\TestCase; +use React\Dns\Query\Query; +use React\Dns\Model\Message; + +class MessageTest extends TestCase +{ + public function testCreateRequestDesiresRecusion() + { + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); + $request = Message::createRequestForQuery($query); + + $this->assertTrue($request->header->isQuery()); + $this->assertSame(1, $request->header->get('rd')); + } + + public function testCreateResponseWithNoAnswers() + { + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); + $answers = array(); + $request = Message::createResponseWithAnswersForQuery($query, $answers); + + $this->assertFalse($request->header->isQuery()); + $this->assertTrue($request->header->isResponse()); + $this->assertEquals(0, $request->header->get('anCount')); + } +} diff --git a/assets/php/vendor/react/dns/tests/Protocol/BinaryDumperTest.php b/assets/php/vendor/react/dns/tests/Protocol/BinaryDumperTest.php new file mode 100644 index 0000000..bf60ca9 --- /dev/null +++ b/assets/php/vendor/react/dns/tests/Protocol/BinaryDumperTest.php @@ -0,0 +1,48 @@ +<?php + +namespace React\Tests\Dns\Protocol; + +use PHPUnit\Framework\TestCase; +use React\Dns\Protocol\BinaryDumper; +use React\Dns\Model\Message; + +class BinaryDumperTest extends TestCase +{ + public function testRequestToBinary() + { + $data = ""; + $data .= "72 62 01 00 00 01 00 00 00 00 00 00"; // header + $data .= "04 69 67 6f 72 02 69 6f 00"; // question: igor.io + $data .= "00 01 00 01"; // question: type A, class IN + + $expected = $this->formatHexDump(str_replace(' ', '', $data), 2); + + $request = new Message(); + $request->header->set('id', 0x7262); + $request->header->set('rd', 1); + + $request->questions[] = array( + 'name' => 'igor.io', + 'type' => Message::TYPE_A, + 'class' => Message::CLASS_IN, + ); + + $request->prepare(); + + $dumper = new BinaryDumper(); + $data = $dumper->toBinary($request); + $data = $this->convertBinaryToHexDump($data); + + $this->assertSame($expected, $data); + } + + private function convertBinaryToHexDump($input) + { + return $this->formatHexDump(implode('', unpack('H*', $input))); + } + + private function formatHexDump($input) + { + return implode(' ', str_split($input, 2)); + } +} diff --git a/assets/php/vendor/react/dns/tests/Protocol/ParserTest.php b/assets/php/vendor/react/dns/tests/Protocol/ParserTest.php new file mode 100644 index 0000000..195fad2 --- /dev/null +++ b/assets/php/vendor/react/dns/tests/Protocol/ParserTest.php @@ -0,0 +1,343 @@ +<?php + +namespace React\Tests\Dns\Protocol; + +use PHPUnit\Framework\TestCase; +use React\Dns\Protocol\Parser; +use React\Dns\Model\Message; + +class ParserTest extends TestCase +{ + public function setUp() + { + $this->parser = new Parser(); + } + + /** + * @dataProvider provideConvertTcpDumpToBinary + */ + public function testConvertTcpDumpToBinary($expected, $data) + { + $this->assertSame($expected, $this->convertTcpDumpToBinary($data)); + } + + public function provideConvertTcpDumpToBinary() + { + return array( + array(chr(0x72).chr(0x62), "72 62"), + array(chr(0x72).chr(0x62).chr(0x01).chr(0x00), "72 62 01 00"), + array(chr(0x72).chr(0x62).chr(0x01).chr(0x00).chr(0x00).chr(0x01), "72 62 01 00 00 01"), + array(chr(0x01).chr(0x00).chr(0x01), "01 00 01"), + ); + } + + public function testParseRequest() + { + $data = ""; + $data .= "72 62 01 00 00 01 00 00 00 00 00 00"; // header + $data .= "04 69 67 6f 72 02 69 6f 00"; // question: igor.io + $data .= "00 01 00 01"; // question: type A, class IN + + $data = $this->convertTcpDumpToBinary($data); + + $request = $this->parser->parseMessage($data); + + $header = $request->header; + $this->assertSame(0x7262, $header->get('id')); + $this->assertSame(1, $header->get('qdCount')); + $this->assertSame(0, $header->get('anCount')); + $this->assertSame(0, $header->get('nsCount')); + $this->assertSame(0, $header->get('arCount')); + $this->assertSame(0, $header->get('qr')); + $this->assertSame(Message::OPCODE_QUERY, $header->get('opcode')); + $this->assertSame(0, $header->get('aa')); + $this->assertSame(0, $header->get('tc')); + $this->assertSame(1, $header->get('rd')); + $this->assertSame(0, $header->get('ra')); + $this->assertSame(0, $header->get('z')); + $this->assertSame(Message::RCODE_OK, $header->get('rcode')); + + $this->assertCount(1, $request->questions); + $this->assertSame('igor.io', $request->questions[0]['name']); + $this->assertSame(Message::TYPE_A, $request->questions[0]['type']); + $this->assertSame(Message::CLASS_IN, $request->questions[0]['class']); + } + + public function testParseResponse() + { + $data = ""; + $data .= "72 62 81 80 00 01 00 01 00 00 00 00"; // header + $data .= "04 69 67 6f 72 02 69 6f 00"; // question: igor.io + $data .= "00 01 00 01"; // question: type A, class IN + $data .= "c0 0c"; // answer: offset pointer to igor.io + $data .= "00 01 00 01"; // answer: type A, class IN + $data .= "00 01 51 80"; // answer: ttl 86400 + $data .= "00 04"; // answer: rdlength 4 + $data .= "b2 4f a9 83"; // answer: rdata 178.79.169.131 + + $data = $this->convertTcpDumpToBinary($data); + + $response = $this->parser->parseMessage($data); + + $header = $response->header; + $this->assertSame(0x7262, $header->get('id')); + $this->assertSame(1, $header->get('qdCount')); + $this->assertSame(1, $header->get('anCount')); + $this->assertSame(0, $header->get('nsCount')); + $this->assertSame(0, $header->get('arCount')); + $this->assertSame(1, $header->get('qr')); + $this->assertSame(Message::OPCODE_QUERY, $header->get('opcode')); + $this->assertSame(0, $header->get('aa')); + $this->assertSame(0, $header->get('tc')); + $this->assertSame(1, $header->get('rd')); + $this->assertSame(1, $header->get('ra')); + $this->assertSame(0, $header->get('z')); + $this->assertSame(Message::RCODE_OK, $header->get('rcode')); + + $this->assertCount(1, $response->questions); + $this->assertSame('igor.io', $response->questions[0]['name']); + $this->assertSame(Message::TYPE_A, $response->questions[0]['type']); + $this->assertSame(Message::CLASS_IN, $response->questions[0]['class']); + + $this->assertCount(1, $response->answers); + $this->assertSame('igor.io', $response->answers[0]->name); + $this->assertSame(Message::TYPE_A, $response->answers[0]->type); + $this->assertSame(Message::CLASS_IN, $response->answers[0]->class); + $this->assertSame(86400, $response->answers[0]->ttl); + $this->assertSame('178.79.169.131', $response->answers[0]->data); + } + + public function testParseQuestionWithTwoQuestions() + { + $data = ""; + $data .= "04 69 67 6f 72 02 69 6f 00"; // question: igor.io + $data .= "00 01 00 01"; // question: type A, class IN + $data .= "03 77 77 77 04 69 67 6f 72 02 69 6f 00"; // question: www.igor.io + $data .= "00 01 00 01"; // question: type A, class IN + + $data = $this->convertTcpDumpToBinary($data); + + $request = new Message(); + $request->header->set('qdCount', 2); + $request->data = $data; + + $this->parser->parseQuestion($request); + + $this->assertCount(2, $request->questions); + $this->assertSame('igor.io', $request->questions[0]['name']); + $this->assertSame(Message::TYPE_A, $request->questions[0]['type']); + $this->assertSame(Message::CLASS_IN, $request->questions[0]['class']); + $this->assertSame('www.igor.io', $request->questions[1]['name']); + $this->assertSame(Message::TYPE_A, $request->questions[1]['type']); + $this->assertSame(Message::CLASS_IN, $request->questions[1]['class']); + } + + public function testParseAnswerWithInlineData() + { + $data = ""; + $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io + $data .= "00 01 00 01"; // answer: type A, class IN + $data .= "00 01 51 80"; // answer: ttl 86400 + $data .= "00 04"; // answer: rdlength 4 + $data .= "b2 4f a9 83"; // answer: rdata 178.79.169.131 + + $data = $this->convertTcpDumpToBinary($data); + + $response = new Message(); + $response->header->set('anCount', 1); + $response->data = $data; + + $this->parser->parseAnswer($response); + + $this->assertCount(1, $response->answers); + $this->assertSame('igor.io', $response->answers[0]->name); + $this->assertSame(Message::TYPE_A, $response->answers[0]->type); + $this->assertSame(Message::CLASS_IN, $response->answers[0]->class); + $this->assertSame(86400, $response->answers[0]->ttl); + $this->assertSame('178.79.169.131', $response->answers[0]->data); + } + + public function testParseResponseWithCnameAndOffsetPointers() + { + $data = ""; + $data .= "9e 8d 81 80 00 01 00 01 00 00 00 00"; // header + $data .= "04 6d 61 69 6c 06 67 6f 6f 67 6c 65 03 63 6f 6d 00"; // question: mail.google.com + $data .= "00 05 00 01"; // question: type CNAME, class IN + $data .= "c0 0c"; // answer: offset pointer to mail.google.com + $data .= "00 05 00 01"; // answer: type CNAME, class IN + $data .= "00 00 a8 9c"; // answer: ttl 43164 + $data .= "00 0f"; // answer: rdlength 15 + $data .= "0a 67 6f 6f 67 6c 65 6d 61 69 6c 01 6c"; // answer: rdata googlemail.l. + $data .= "c0 11"; // answer: rdata offset pointer to google.com + + $data = $this->convertTcpDumpToBinary($data); + + $response = $this->parser->parseMessage($data); + + $this->assertCount(1, $response->questions); + $this->assertSame('mail.google.com', $response->questions[0]['name']); + $this->assertSame(Message::TYPE_CNAME, $response->questions[0]['type']); + $this->assertSame(Message::CLASS_IN, $response->questions[0]['class']); + + $this->assertCount(1, $response->answers); + $this->assertSame('mail.google.com', $response->answers[0]->name); + $this->assertSame(Message::TYPE_CNAME, $response->answers[0]->type); + $this->assertSame(Message::CLASS_IN, $response->answers[0]->class); + $this->assertSame(43164, $response->answers[0]->ttl); + $this->assertSame('googlemail.l.google.com', $response->answers[0]->data); + } + + public function testParseAAAAResponse() + { + $data = ""; + $data .= "cd 72 81 80 00 01 00 01 00 00 00 00 06"; // header + $data .= "67 6f 6f 67 6c 65 03 63 6f 6d 00"; // question: google.com + $data .= "00 1c 00 01"; // question: type AAAA, class IN + $data .= "c0 0c"; // answer: offset pointer to google.com + $data .= "00 1c 00 01"; // answer: type AAAA, class IN + $data .= "00 00 01 2b"; // answer: ttl 299 + $data .= "00 10"; // answer: rdlength 16 + $data .= "2a 00 14 50 40 09 08 09 00 00 00 00 00 00 20 0e"; // answer: 2a00:1450:4009:809::200e + + $data = $this->convertTcpDumpToBinary($data); + + $response = $this->parser->parseMessage($data); + + $header = $response->header; + $this->assertSame(0xcd72, $header->get('id')); + $this->assertSame(1, $header->get('qdCount')); + $this->assertSame(1, $header->get('anCount')); + $this->assertSame(0, $header->get('nsCount')); + $this->assertSame(0, $header->get('arCount')); + $this->assertSame(1, $header->get('qr')); + $this->assertSame(Message::OPCODE_QUERY, $header->get('opcode')); + $this->assertSame(0, $header->get('aa')); + $this->assertSame(0, $header->get('tc')); + $this->assertSame(1, $header->get('rd')); + $this->assertSame(1, $header->get('ra')); + $this->assertSame(0, $header->get('z')); + $this->assertSame(Message::RCODE_OK, $header->get('rcode')); + + $this->assertCount(1, $response->questions); + $this->assertSame('google.com', $response->questions[0]['name']); + $this->assertSame(Message::TYPE_AAAA, $response->questions[0]['type']); + $this->assertSame(Message::CLASS_IN, $response->questions[0]['class']); + + $this->assertCount(1, $response->answers); + $this->assertSame('google.com', $response->answers[0]->name); + $this->assertSame(Message::TYPE_AAAA, $response->answers[0]->type); + $this->assertSame(Message::CLASS_IN, $response->answers[0]->class); + $this->assertSame(299, $response->answers[0]->ttl); + $this->assertSame('2a00:1450:4009:809::200e', $response->answers[0]->data); + } + + public function testParseResponseWithTwoAnswers() + { + $data = ""; + $data .= "bc 73 81 80 00 01 00 02 00 00 00 00"; // header + $data .= "02 69 6f 0d 77 68 6f 69 73 2d 73 65 72 76 65 72 73 03 6e 65 74 00"; + // question: io.whois-servers.net + $data .= "00 01 00 01"; // question: type A, class IN + $data .= "c0 0c"; // answer: offset pointer to io.whois-servers.net + $data .= "00 05 00 01"; // answer: type CNAME, class IN + $data .= "00 00 00 29"; // answer: ttl 41 + $data .= "00 0e"; // answer: rdlength 14 + $data .= "05 77 68 6f 69 73 03 6e 69 63 02 69 6f 00"; // answer: rdata whois.nic.io + $data .= "c0 32"; // answer: offset pointer to whois.nic.io + $data .= "00 01 00 01"; // answer: type CNAME, class IN + $data .= "00 00 0d f7"; // answer: ttl 3575 + $data .= "00 04"; // answer: rdlength 4 + $data .= "c1 df 4e 98"; // answer: rdata 193.223.78.152 + + $data = $this->convertTcpDumpToBinary($data); + + $response = $this->parser->parseMessage($data); + + $this->assertCount(1, $response->questions); + $this->assertSame('io.whois-servers.net', $response->questions[0]['name']); + $this->assertSame(Message::TYPE_A, $response->questions[0]['type']); + $this->assertSame(Message::CLASS_IN, $response->questions[0]['class']); + + $this->assertCount(2, $response->answers); + + $this->assertSame('io.whois-servers.net', $response->answers[0]->name); + $this->assertSame(Message::TYPE_CNAME, $response->answers[0]->type); + $this->assertSame(Message::CLASS_IN, $response->answers[0]->class); + $this->assertSame(41, $response->answers[0]->ttl); + $this->assertSame('whois.nic.io', $response->answers[0]->data); + + $this->assertSame('whois.nic.io', $response->answers[1]->name); + $this->assertSame(Message::TYPE_A, $response->answers[1]->type); + $this->assertSame(Message::CLASS_IN, $response->answers[1]->class); + $this->assertSame(3575, $response->answers[1]->ttl); + $this->assertSame('193.223.78.152', $response->answers[1]->data); + } + + public function testParsePTRResponse() + { + $data = ""; + $data .= "5d d8 81 80 00 01 00 01 00 00 00 00"; // header + $data .= "01 34 01 34 01 38 01 38 07 69 6e"; // question: 4.4.8.8.in-addr.arpa + $data .= "2d 61 64 64 72 04 61 72 70 61 00"; // question (continued) + $data .= "00 0c 00 01"; // question: type PTR, class IN + $data .= "c0 0c"; // answer: offset pointer to rdata + $data .= "00 0c 00 01"; // answer: type PTR, class IN + $data .= "00 01 51 7f"; // answer: ttl 86399 + $data .= "00 20"; // answer: rdlength 32 + $data .= "13 67 6f 6f 67 6c 65 2d 70 75 62 6c 69 63 2d 64"; // answer: rdata google-public-dns-b.google.com. + $data .= "6e 73 2d 62 06 67 6f 6f 67 6c 65 03 63 6f 6d 00"; + + $data = $this->convertTcpDumpToBinary($data); + + $response = $this->parser->parseMessage($data); + + $header = $response->header; + $this->assertSame(0x5dd8, $header->get('id')); + $this->assertSame(1, $header->get('qdCount')); + $this->assertSame(1, $header->get('anCount')); + $this->assertSame(0, $header->get('nsCount')); + $this->assertSame(0, $header->get('arCount')); + $this->assertSame(1, $header->get('qr')); + $this->assertSame(Message::OPCODE_QUERY, $header->get('opcode')); + $this->assertSame(0, $header->get('aa')); + $this->assertSame(0, $header->get('tc')); + $this->assertSame(1, $header->get('rd')); + $this->assertSame(1, $header->get('ra')); + $this->assertSame(0, $header->get('z')); + $this->assertSame(Message::RCODE_OK, $header->get('rcode')); + + $this->assertCount(1, $response->questions); + $this->assertSame('4.4.8.8.in-addr.arpa', $response->questions[0]['name']); + $this->assertSame(Message::TYPE_PTR, $response->questions[0]['type']); + $this->assertSame(Message::CLASS_IN, $response->questions[0]['class']); + + $this->assertCount(1, $response->answers); + $this->assertSame('4.4.8.8.in-addr.arpa', $response->answers[0]->name); + $this->assertSame(Message::TYPE_PTR, $response->answers[0]->type); + $this->assertSame(Message::CLASS_IN, $response->answers[0]->class); + $this->assertSame(86399, $response->answers[0]->ttl); + $this->assertSame('google-public-dns-b.google.com', $response->answers[0]->data); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testParseIncomplete() + { + $data = ""; + $data .= "72 62 01 00 00 01 00 00 00 00 00 00"; // header + $data .= "04 69 67 6f 72 02 69 6f 00"; // question: igor.io + //$data .= "00 01 00 01"; // question: type A, class IN + + $data = $this->convertTcpDumpToBinary($data); + + $this->parser->parseMessage($data); + } + + private function convertTcpDumpToBinary($input) + { + // sudo ngrep -d en1 -x port 53 + + return pack('H*', str_replace(' ', '', $input)); + } +} diff --git a/assets/php/vendor/react/dns/tests/Query/CachedExecutorTest.php b/assets/php/vendor/react/dns/tests/Query/CachedExecutorTest.php new file mode 100644 index 0000000..d08ed05 --- /dev/null +++ b/assets/php/vendor/react/dns/tests/Query/CachedExecutorTest.php @@ -0,0 +1,100 @@ +<?php + +namespace React\Tests\Dns\Query; + +use React\Tests\Dns\TestCase; +use React\Dns\Query\CachedExecutor; +use React\Dns\Query\Query; +use React\Dns\Model\Message; +use React\Dns\Model\Record; +use React\Promise; + +class CachedExecutorTest extends TestCase +{ + /** + * @covers React\Dns\Query\CachedExecutor + * @test + */ + public function queryShouldDelegateToDecoratedExecutor() + { + $executor = $this->createExecutorMock(); + $executor + ->expects($this->once()) + ->method('query') + ->with('8.8.8.8', $this->isInstanceOf('React\Dns\Query\Query')) + ->will($this->returnValue($this->createPromiseMock())); + + $cache = $this->getMockBuilder('React\Dns\Query\RecordCache') + ->disableOriginalConstructor() + ->getMock(); + $cache + ->expects($this->once()) + ->method('lookup') + ->will($this->returnValue(Promise\reject())); + $cachedExecutor = new CachedExecutor($executor, $cache); + + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); + $cachedExecutor->query('8.8.8.8', $query); + } + + /** + * @covers React\Dns\Query\CachedExecutor + * @test + */ + public function callingQueryTwiceShouldUseCachedResult() + { + $cachedRecords = array(new Record('igor.io', Message::TYPE_A, Message::CLASS_IN)); + + $executor = $this->createExecutorMock(); + $executor + ->expects($this->once()) + ->method('query') + ->will($this->callQueryCallbackWithAddress('178.79.169.131')); + + $cache = $this->getMockBuilder('React\Dns\Query\RecordCache') + ->disableOriginalConstructor() + ->getMock(); + $cache + ->expects($this->at(0)) + ->method('lookup') + ->with($this->isInstanceOf('React\Dns\Query\Query')) + ->will($this->returnValue(Promise\reject())); + $cache + ->expects($this->at(1)) + ->method('storeResponseMessage') + ->with($this->isType('integer'), $this->isInstanceOf('React\Dns\Model\Message')); + $cache + ->expects($this->at(2)) + ->method('lookup') + ->with($this->isInstanceOf('React\Dns\Query\Query')) + ->will($this->returnValue(Promise\resolve($cachedRecords))); + + $cachedExecutor = new CachedExecutor($executor, $cache); + + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); + $cachedExecutor->query('8.8.8.8', $query, function () {}, function () {}); + $cachedExecutor->query('8.8.8.8', $query, function () {}, function () {}); + } + + private function callQueryCallbackWithAddress($address) + { + return $this->returnCallback(function ($nameserver, $query) use ($address) { + $response = new Message(); + $response->header->set('qr', 1); + $response->questions[] = new Record($query->name, $query->type, $query->class); + $response->answers[] = new Record($query->name, $query->type, $query->class, 3600, $address); + + return Promise\resolve($response); + }); + } + + private function createExecutorMock() + { + return $this->getMockBuilder('React\Dns\Query\ExecutorInterface')->getMock(); + } + + private function createPromiseMock() + { + return $this->getMockBuilder('React\Promise\PromiseInterface')->getMock(); + } +} diff --git a/assets/php/vendor/react/dns/tests/Query/ExecutorTest.php b/assets/php/vendor/react/dns/tests/Query/ExecutorTest.php new file mode 100644 index 0000000..0d7ac1d --- /dev/null +++ b/assets/php/vendor/react/dns/tests/Query/ExecutorTest.php @@ -0,0 +1,308 @@ +<?php + +namespace React\Tests\Dns\Query; + +use Clue\React\Block; +use React\Dns\Query\Executor; +use React\Dns\Query\Query; +use React\Dns\Model\Message; +use React\Dns\Model\Record; +use React\Dns\Protocol\BinaryDumper; +use React\Tests\Dns\TestCase; + +class ExecutorTest extends TestCase +{ + private $loop; + private $parser; + private $dumper; + private $executor; + + public function setUp() + { + $this->loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + $this->parser = $this->getMockBuilder('React\Dns\Protocol\Parser')->getMock(); + $this->dumper = new BinaryDumper(); + + $this->executor = new Executor($this->loop, $this->parser, $this->dumper); + } + + /** @test */ + public function queryShouldCreateUdpRequest() + { + $timer = $this->createTimerMock(); + $this->loop + ->expects($this->any()) + ->method('addTimer') + ->will($this->returnValue($timer)); + + $this->executor = $this->createExecutorMock(); + $this->executor + ->expects($this->once()) + ->method('createConnection') + ->with('8.8.8.8:53', 'udp') + ->will($this->returnNewConnectionMock(false)); + + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); + $this->executor->query('8.8.8.8:53', $query); + } + + /** @test */ + public function resolveShouldRejectIfRequestIsLargerThan512Bytes() + { + $query = new Query(str_repeat('a', 512).'.igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); + $promise = $this->executor->query('8.8.8.8:53', $query); + + $this->setExpectedException('RuntimeException', 'DNS query for ' . $query->name . ' failed: Requested transport "tcp" not available, only UDP is supported in this version'); + Block\await($promise, $this->loop); + } + + /** @test */ + public function resolveShouldCloseConnectionWhenCancelled() + { + $conn = $this->createConnectionMock(false); + $conn->expects($this->once())->method('close'); + + $timer = $this->createTimerMock(); + $this->loop + ->expects($this->any()) + ->method('addTimer') + ->will($this->returnValue($timer)); + + $this->executor = $this->createExecutorMock(); + $this->executor + ->expects($this->once()) + ->method('createConnection') + ->with('8.8.8.8:53', 'udp') + ->will($this->returnValue($conn)); + + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); + $promise = $this->executor->query('8.8.8.8:53', $query); + + $promise->cancel(); + + $this->setExpectedException('React\Dns\Query\CancellationException', 'DNS query for igor.io has been cancelled'); + Block\await($promise, $this->loop); + } + + /** @test */ + public function resolveShouldNotStartOrCancelTimerWhenCancelledWithTimeoutIsNull() + { + $this->loop + ->expects($this->never()) + ->method('addTimer'); + + $this->executor = new Executor($this->loop, $this->parser, $this->dumper, null); + + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); + $promise = $this->executor->query('127.0.0.1:53', $query); + + $promise->cancel(); + + $this->setExpectedException('React\Dns\Query\CancellationException', 'DNS query for igor.io has been cancelled'); + Block\await($promise, $this->loop); + } + + /** @test */ + public function resolveShouldRejectIfResponseIsTruncated() + { + $timer = $this->createTimerMock(); + + $this->loop + ->expects($this->any()) + ->method('addTimer') + ->will($this->returnValue($timer)); + + $this->parser + ->expects($this->once()) + ->method('parseMessage') + ->will($this->returnTruncatedResponse()); + + $this->executor = $this->createExecutorMock(); + $this->executor + ->expects($this->once()) + ->method('createConnection') + ->with('8.8.8.8:53', 'udp') + ->will($this->returnNewConnectionMock()); + + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); + $this->executor->query('8.8.8.8:53', $query); + } + + /** @test */ + public function resolveShouldFailIfUdpThrow() + { + $this->loop + ->expects($this->never()) + ->method('addTimer'); + + $this->parser + ->expects($this->never()) + ->method('parseMessage'); + + $this->executor = $this->createExecutorMock(); + $this->executor + ->expects($this->once()) + ->method('createConnection') + ->with('8.8.8.8:53', 'udp') + ->will($this->throwException(new \Exception('Nope'))); + + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); + $promise = $this->executor->query('8.8.8.8:53', $query); + + $this->setExpectedException('RuntimeException', 'DNS query for igor.io failed: Nope'); + Block\await($promise, $this->loop); + } + + /** @test */ + public function resolveShouldCancelTimerWhenFullResponseIsReceived() + { + $conn = $this->createConnectionMock(); + + $this->parser + ->expects($this->once()) + ->method('parseMessage') + ->will($this->returnStandardResponse()); + + $this->executor = $this->createExecutorMock(); + $this->executor + ->expects($this->at(0)) + ->method('createConnection') + ->with('8.8.8.8:53', 'udp') + ->will($this->returnNewConnectionMock()); + + + $timer = $this->createTimerMock(); + + $this->loop + ->expects($this->once()) + ->method('addTimer') + ->with(5, $this->isInstanceOf('Closure')) + ->will($this->returnValue($timer)); + + $this->loop + ->expects($this->once()) + ->method('cancelTimer') + ->with($timer); + + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); + $this->executor->query('8.8.8.8:53', $query); + } + + /** @test */ + public function resolveShouldCloseConnectionOnTimeout() + { + $this->executor = $this->createExecutorMock(); + $this->executor + ->expects($this->at(0)) + ->method('createConnection') + ->with('8.8.8.8:53', 'udp') + ->will($this->returnNewConnectionMock(false)); + + $timer = $this->createTimerMock(); + + $this->loop + ->expects($this->never()) + ->method('cancelTimer'); + + $this->loop + ->expects($this->once()) + ->method('addTimer') + ->with(5, $this->isInstanceOf('Closure')) + ->will($this->returnCallback(function ($time, $callback) use (&$timerCallback, $timer) { + $timerCallback = $callback; + return $timer; + })); + + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); + $promise = $this->executor->query('8.8.8.8:53', $query); + + $this->assertNotNull($timerCallback); + $timerCallback(); + + $this->setExpectedException('React\Dns\Query\TimeoutException', 'DNS query for igor.io timed out'); + Block\await($promise, $this->loop); + } + + private function returnStandardResponse() + { + $that = $this; + $callback = function ($data) use ($that) { + $response = new Message(); + $that->convertMessageToStandardResponse($response); + return $response; + }; + + return $this->returnCallback($callback); + } + + private function returnTruncatedResponse() + { + $that = $this; + $callback = function ($data) use ($that) { + $response = new Message(); + $that->convertMessageToTruncatedResponse($response); + return $response; + }; + + return $this->returnCallback($callback); + } + + public function convertMessageToStandardResponse(Message $response) + { + $response->header->set('qr', 1); + $response->questions[] = new Record('igor.io', Message::TYPE_A, Message::CLASS_IN); + $response->answers[] = new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.131'); + $response->prepare(); + + return $response; + } + + public function convertMessageToTruncatedResponse(Message $response) + { + $this->convertMessageToStandardResponse($response); + $response->header->set('tc', 1); + $response->prepare(); + + return $response; + } + + private function returnNewConnectionMock($emitData = true) + { + $conn = $this->createConnectionMock($emitData); + + $callback = function () use ($conn) { + return $conn; + }; + + return $this->returnCallback($callback); + } + + private function createConnectionMock($emitData = true) + { + $conn = $this->getMockBuilder('React\Stream\DuplexStreamInterface')->getMock(); + $conn + ->expects($this->any()) + ->method('on') + ->with('data', $this->isInstanceOf('Closure')) + ->will($this->returnCallback(function ($name, $callback) use ($emitData) { + $emitData && $callback(null); + })); + + return $conn; + } + + private function createTimerMock() + { + return $this->getMockBuilder( + interface_exists('React\EventLoop\TimerInterface') ? 'React\EventLoop\TimerInterface' : 'React\EventLoop\Timer\TimerInterface' + )->getMock(); + } + + private function createExecutorMock() + { + return $this->getMockBuilder('React\Dns\Query\Executor') + ->setConstructorArgs(array($this->loop, $this->parser, $this->dumper)) + ->setMethods(array('createConnection')) + ->getMock(); + } +} diff --git a/assets/php/vendor/react/dns/tests/Query/HostsFileExecutorTest.php b/assets/php/vendor/react/dns/tests/Query/HostsFileExecutorTest.php new file mode 100644 index 0000000..70d877e --- /dev/null +++ b/assets/php/vendor/react/dns/tests/Query/HostsFileExecutorTest.php @@ -0,0 +1,126 @@ +<?php + +namespace React\Tests\Dns\Query; + +use React\Tests\Dns\TestCase; +use React\Dns\Query\HostsFileExecutor; +use React\Dns\Query\Query; +use React\Dns\Model\Message; + +class HostsFileExecutorTest extends TestCase +{ + private $hosts; + private $fallback; + private $executor; + + public function setUp() + { + $this->hosts = $this->getMockBuilder('React\Dns\Config\HostsFile')->disableOriginalConstructor()->getMock(); + $this->fallback = $this->getMockBuilder('React\Dns\Query\ExecutorInterface')->getMock(); + $this->executor = new HostsFileExecutor($this->hosts, $this->fallback); + } + + public function testDoesNotTryToGetIpsForMxQuery() + { + $this->hosts->expects($this->never())->method('getIpsForHost'); + $this->fallback->expects($this->once())->method('query'); + + $this->executor->query('8.8.8.8', new Query('google.com', Message::TYPE_MX, Message::CLASS_IN, 0)); + } + + public function testFallsBackIfNoIpsWereFound() + { + $this->hosts->expects($this->once())->method('getIpsForHost')->willReturn(array()); + $this->fallback->expects($this->once())->method('query'); + + $this->executor->query('8.8.8.8', new Query('google.com', Message::TYPE_A, Message::CLASS_IN, 0)); + } + + public function testReturnsResponseMessageIfIpsWereFound() + { + $this->hosts->expects($this->once())->method('getIpsForHost')->willReturn(array('127.0.0.1')); + $this->fallback->expects($this->never())->method('query'); + + $ret = $this->executor->query('8.8.8.8', new Query('google.com', Message::TYPE_A, Message::CLASS_IN, 0)); + } + + public function testFallsBackIfNoIpv4Matches() + { + $this->hosts->expects($this->once())->method('getIpsForHost')->willReturn(array('::1')); + $this->fallback->expects($this->once())->method('query'); + + $ret = $this->executor->query('8.8.8.8', new Query('google.com', Message::TYPE_A, Message::CLASS_IN, 0)); + } + + public function testReturnsResponseMessageIfIpv6AddressesWereFound() + { + $this->hosts->expects($this->once())->method('getIpsForHost')->willReturn(array('::1')); + $this->fallback->expects($this->never())->method('query'); + + $ret = $this->executor->query('8.8.8.8', new Query('google.com', Message::TYPE_AAAA, Message::CLASS_IN, 0)); + } + + public function testFallsBackIfNoIpv6Matches() + { + $this->hosts->expects($this->once())->method('getIpsForHost')->willReturn(array('127.0.0.1')); + $this->fallback->expects($this->once())->method('query'); + + $ret = $this->executor->query('8.8.8.8', new Query('google.com', Message::TYPE_AAAA, Message::CLASS_IN, 0)); + } + + public function testDoesReturnReverseIpv4Lookup() + { + $this->hosts->expects($this->once())->method('getHostsForIp')->with('127.0.0.1')->willReturn(array('localhost')); + $this->fallback->expects($this->never())->method('query'); + + $this->executor->query('8.8.8.8', new Query('1.0.0.127.in-addr.arpa', Message::TYPE_PTR, Message::CLASS_IN, 0)); + } + + public function testFallsBackIfNoReverseIpv4Matches() + { + $this->hosts->expects($this->once())->method('getHostsForIp')->with('127.0.0.1')->willReturn(array()); + $this->fallback->expects($this->once())->method('query'); + + $this->executor->query('8.8.8.8', new Query('1.0.0.127.in-addr.arpa', Message::TYPE_PTR, Message::CLASS_IN, 0)); + } + + public function testDoesReturnReverseIpv6Lookup() + { + $this->hosts->expects($this->once())->method('getHostsForIp')->with('2a02:2e0:3fe:100::6')->willReturn(array('ip6-localhost')); + $this->fallback->expects($this->never())->method('query'); + + $this->executor->query('8.8.8.8', new Query('6.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.0.e.f.3.0.0.e.2.0.2.0.a.2.ip6.arpa', Message::TYPE_PTR, Message::CLASS_IN, 0)); + } + + public function testFallsBackForInvalidAddress() + { + $this->hosts->expects($this->never())->method('getHostsForIp'); + $this->fallback->expects($this->once())->method('query'); + + $this->executor->query('8.8.8.8', new Query('example.com', Message::TYPE_PTR, Message::CLASS_IN, 0)); + } + + public function testReverseFallsBackForInvalidIpv4Address() + { + $this->hosts->expects($this->never())->method('getHostsForIp'); + $this->fallback->expects($this->once())->method('query'); + + $this->executor->query('8.8.8.8', new Query('::1.in-addr.arpa', Message::TYPE_PTR, Message::CLASS_IN, 0)); + } + + public function testReverseFallsBackForInvalidLengthIpv6Address() + { + $this->hosts->expects($this->never())->method('getHostsForIp'); + $this->fallback->expects($this->once())->method('query'); + + $this->executor->query('8.8.8.8', new Query('abcd.ip6.arpa', Message::TYPE_PTR, Message::CLASS_IN, 0)); + } + + public function testReverseFallsBackForInvalidHexIpv6Address() + { + $this->hosts->expects($this->never())->method('getHostsForIp'); + $this->fallback->expects($this->once())->method('query'); + + $this->executor->query('8.8.8.8', new Query('zZz.ip6.arpa', Message::TYPE_PTR, Message::CLASS_IN, 0)); + } +} diff --git a/assets/php/vendor/react/dns/tests/Query/RecordBagTest.php b/assets/php/vendor/react/dns/tests/Query/RecordBagTest.php new file mode 100644 index 0000000..83b8934 --- /dev/null +++ b/assets/php/vendor/react/dns/tests/Query/RecordBagTest.php @@ -0,0 +1,64 @@ +<?php + +namespace React\Tests\Dns\Query; + +use PHPUnit\Framework\TestCase; +use React\Dns\Query\RecordBag; +use React\Dns\Model\Message; +use React\Dns\Model\Record; + +class RecordBagTest extends TestCase +{ + /** + * @covers React\Dns\Query\RecordBag + * @test + */ + public function emptyBagShouldBeEmpty() + { + $recordBag = new RecordBag(); + + $this->assertSame(array(), $recordBag->all()); + } + + /** + * @covers React\Dns\Query\RecordBag + * @test + */ + public function setShouldSetTheValue() + { + $currentTime = 1345656451; + + $recordBag = new RecordBag(); + $recordBag->set($currentTime, new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600)); + + $records = $recordBag->all(); + $this->assertCount(1, $records); + $this->assertSame('igor.io', $records[0]->name); + $this->assertSame(Message::TYPE_A, $records[0]->type); + $this->assertSame(Message::CLASS_IN, $records[0]->class); + } + + /** + * @covers React\Dns\Query\RecordBag + * @test + */ + public function setShouldSetManyValues() + { + $currentTime = 1345656451; + + $recordBag = new RecordBag(); + $recordBag->set($currentTime, new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.131')); + $recordBag->set($currentTime, new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.132')); + + $records = $recordBag->all(); + $this->assertCount(2, $records); + $this->assertSame('igor.io', $records[0]->name); + $this->assertSame(Message::TYPE_A, $records[0]->type); + $this->assertSame(Message::CLASS_IN, $records[0]->class); + $this->assertSame('178.79.169.131', $records[0]->data); + $this->assertSame('igor.io', $records[1]->name); + $this->assertSame(Message::TYPE_A, $records[1]->type); + $this->assertSame(Message::CLASS_IN, $records[1]->class); + $this->assertSame('178.79.169.132', $records[1]->data); + } +} diff --git a/assets/php/vendor/react/dns/tests/Query/RecordCacheTest.php b/assets/php/vendor/react/dns/tests/Query/RecordCacheTest.php new file mode 100644 index 0000000..399fbe8 --- /dev/null +++ b/assets/php/vendor/react/dns/tests/Query/RecordCacheTest.php @@ -0,0 +1,123 @@ +<?php + +namespace React\Tests\Dns\Query; + +use PHPUnit\Framework\TestCase; +use React\Cache\ArrayCache; +use React\Dns\Model\Message; +use React\Dns\Model\Record; +use React\Dns\Query\RecordCache; +use React\Dns\Query\Query; +use React\Promise\PromiseInterface; + +class RecordCacheTest extends TestCase +{ + /** + * @covers React\Dns\Query\RecordCache + * @test + */ + public function lookupOnEmptyCacheShouldReturnNull() + { + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); + + $cache = new RecordCache(new ArrayCache()); + $promise = $cache->lookup($query); + + $this->assertInstanceOf('React\Promise\RejectedPromise', $promise); + } + + /** + * @covers React\Dns\Query\RecordCache + * @test + */ + public function storeRecordShouldMakeLookupSucceed() + { + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); + + $cache = new RecordCache(new ArrayCache()); + $cache->storeRecord($query->currentTime, new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.131')); + $promise = $cache->lookup($query); + + $this->assertInstanceOf('React\Promise\FulfilledPromise', $promise); + $cachedRecords = $this->getPromiseValue($promise); + + $this->assertCount(1, $cachedRecords); + $this->assertSame('178.79.169.131', $cachedRecords[0]->data); + } + + /** + * @covers React\Dns\Query\RecordCache + * @test + */ + public function storeTwoRecordsShouldReturnBoth() + { + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); + + $cache = new RecordCache(new ArrayCache()); + $cache->storeRecord($query->currentTime, new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.131')); + $cache->storeRecord($query->currentTime, new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.132')); + $promise = $cache->lookup($query); + + $this->assertInstanceOf('React\Promise\FulfilledPromise', $promise); + $cachedRecords = $this->getPromiseValue($promise); + + $this->assertCount(2, $cachedRecords); + $this->assertSame('178.79.169.131', $cachedRecords[0]->data); + $this->assertSame('178.79.169.132', $cachedRecords[1]->data); + } + + /** + * @covers React\Dns\Query\RecordCache + * @test + */ + public function storeResponseMessageShouldStoreAllAnswerValues() + { + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); + + $response = new Message(); + $response->answers[] = new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.131'); + $response->answers[] = new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.132'); + $response->prepare(); + + $cache = new RecordCache(new ArrayCache()); + $cache->storeResponseMessage($query->currentTime, $response); + $promise = $cache->lookup($query); + + $this->assertInstanceOf('React\Promise\FulfilledPromise', $promise); + $cachedRecords = $this->getPromiseValue($promise); + + $this->assertCount(2, $cachedRecords); + $this->assertSame('178.79.169.131', $cachedRecords[0]->data); + $this->assertSame('178.79.169.132', $cachedRecords[1]->data); + } + + /** + * @covers React\Dns\Query\RecordCache + * @test + */ + public function expireShouldExpireDeadRecords() + { + $cachedTime = 1345656451; + $currentTime = $cachedTime + 3605; + + $cache = new RecordCache(new ArrayCache()); + $cache->storeRecord($cachedTime, new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.131')); + $cache->expire($currentTime); + + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, $currentTime); + $promise = $cache->lookup($query); + + $this->assertInstanceOf('React\Promise\RejectedPromise', $promise); + } + + private function getPromiseValue(PromiseInterface $promise) + { + $capturedValue = null; + + $promise->then(function ($value) use (&$capturedValue) { + $capturedValue = $value; + }); + + return $capturedValue; + } +} diff --git a/assets/php/vendor/react/dns/tests/Query/RetryExecutorTest.php b/assets/php/vendor/react/dns/tests/Query/RetryExecutorTest.php new file mode 100644 index 0000000..8950f84 --- /dev/null +++ b/assets/php/vendor/react/dns/tests/Query/RetryExecutorTest.php @@ -0,0 +1,197 @@ +<?php + +namespace React\Tests\Dns\Query; + +use React\Tests\Dns\TestCase; +use React\Dns\Query\RetryExecutor; +use React\Dns\Query\Query; +use React\Dns\Model\Message; +use React\Dns\Query\TimeoutException; +use React\Dns\Model\Record; +use React\Promise; +use React\Promise\Deferred; +use React\Dns\Query\CancellationException; + +class RetryExecutorTest extends TestCase +{ + /** + * @covers React\Dns\Query\RetryExecutor + * @test + */ + public function queryShouldDelegateToDecoratedExecutor() + { + $executor = $this->createExecutorMock(); + $executor + ->expects($this->once()) + ->method('query') + ->with('8.8.8.8', $this->isInstanceOf('React\Dns\Query\Query')) + ->will($this->returnValue($this->expectPromiseOnce())); + + $retryExecutor = new RetryExecutor($executor, 2); + + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); + $retryExecutor->query('8.8.8.8', $query); + } + + /** + * @covers React\Dns\Query\RetryExecutor + * @test + */ + public function queryShouldRetryQueryOnTimeout() + { + $response = $this->createStandardResponse(); + + $executor = $this->createExecutorMock(); + $executor + ->expects($this->exactly(2)) + ->method('query') + ->with('8.8.8.8', $this->isInstanceOf('React\Dns\Query\Query')) + ->will($this->onConsecutiveCalls( + $this->returnCallback(function ($domain, $query) { + return Promise\reject(new TimeoutException("timeout")); + }), + $this->returnCallback(function ($domain, $query) use ($response) { + return Promise\resolve($response); + }) + )); + + $callback = $this->createCallableMock(); + $callback + ->expects($this->once()) + ->method('__invoke') + ->with($this->isInstanceOf('React\Dns\Model\Message')); + + $errorback = $this->expectCallableNever(); + + $retryExecutor = new RetryExecutor($executor, 2); + + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); + $retryExecutor->query('8.8.8.8', $query)->then($callback, $errorback); + } + + /** + * @covers React\Dns\Query\RetryExecutor + * @test + */ + public function queryShouldStopRetryingAfterSomeAttempts() + { + $executor = $this->createExecutorMock(); + $executor + ->expects($this->exactly(3)) + ->method('query') + ->with('8.8.8.8', $this->isInstanceOf('React\Dns\Query\Query')) + ->will($this->returnCallback(function ($domain, $query) { + return Promise\reject(new TimeoutException("timeout")); + })); + + $callback = $this->expectCallableNever(); + + $errorback = $this->createCallableMock(); + $errorback + ->expects($this->once()) + ->method('__invoke') + ->with($this->isInstanceOf('RuntimeException')); + + $retryExecutor = new RetryExecutor($executor, 2); + + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); + $retryExecutor->query('8.8.8.8', $query)->then($callback, $errorback); + } + + /** + * @covers React\Dns\Query\RetryExecutor + * @test + */ + public function queryShouldForwardNonTimeoutErrors() + { + $executor = $this->createExecutorMock(); + $executor + ->expects($this->once()) + ->method('query') + ->with('8.8.8.8', $this->isInstanceOf('React\Dns\Query\Query')) + ->will($this->returnCallback(function ($domain, $query) { + return Promise\reject(new \Exception); + })); + + $callback = $this->expectCallableNever(); + + $errorback = $this->createCallableMock(); + $errorback + ->expects($this->once()) + ->method('__invoke') + ->with($this->isInstanceOf('Exception')); + + $retryExecutor = new RetryExecutor($executor, 2); + + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); + $retryExecutor->query('8.8.8.8', $query)->then($callback, $errorback); + } + + /** + * @covers React\Dns\Query\RetryExecutor + * @test + */ + public function queryShouldCancelQueryOnCancel() + { + $cancelled = 0; + + $executor = $this->createExecutorMock(); + $executor + ->expects($this->once()) + ->method('query') + ->with('8.8.8.8', $this->isInstanceOf('React\Dns\Query\Query')) + ->will($this->returnCallback(function ($domain, $query) use (&$cancelled) { + $deferred = new Deferred(function ($resolve, $reject) use (&$cancelled) { + ++$cancelled; + $reject(new CancellationException('Cancelled')); + }); + + return $deferred->promise(); + }) + ); + + $retryExecutor = new RetryExecutor($executor, 2); + + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); + $promise = $retryExecutor->query('8.8.8.8', $query); + + $promise->then($this->expectCallableNever(), $this->expectCallableOnce()); + + $this->assertEquals(0, $cancelled); + $promise->cancel(); + $this->assertEquals(1, $cancelled); + } + + protected function expectPromiseOnce($return = null) + { + $mock = $this->createPromiseMock(); + $mock + ->expects($this->once()) + ->method('then') + ->will($this->returnValue($return)); + + return $mock; + } + + protected function createExecutorMock() + { + return $this->getMockBuilder('React\Dns\Query\ExecutorInterface')->getMock(); + } + + protected function createPromiseMock() + { + return $this->getMockBuilder('React\Promise\PromiseInterface')->getMock(); + } + + protected function createStandardResponse() + { + $response = new Message(); + $response->header->set('qr', 1); + $response->questions[] = new Record('igor.io', Message::TYPE_A, Message::CLASS_IN); + $response->answers[] = new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.131'); + $response->prepare(); + + return $response; + } +} + diff --git a/assets/php/vendor/react/dns/tests/Query/TimeoutExecutorTest.php b/assets/php/vendor/react/dns/tests/Query/TimeoutExecutorTest.php new file mode 100644 index 0000000..0d37fb4 --- /dev/null +++ b/assets/php/vendor/react/dns/tests/Query/TimeoutExecutorTest.php @@ -0,0 +1,115 @@ +<?php + +namespace React\Tests\Dns\Query; + +use React\Dns\Query\TimeoutExecutor; +use React\Dns\Query\Query; +use React\Dns\Model\Message; +use React\Promise\Deferred; +use React\Dns\Query\CancellationException; +use React\Tests\Dns\TestCase; +use React\EventLoop\Factory; +use React\Promise; + +class TimeoutExecutorTest extends TestCase +{ + public function setUp() + { + $this->loop = Factory::create(); + + $this->wrapped = $this->getMockBuilder('React\Dns\Query\ExecutorInterface')->getMock(); + + $this->executor = new TimeoutExecutor($this->wrapped, 5.0, $this->loop); + } + + public function testCancellingPromiseWillCancelWrapped() + { + $cancelled = 0; + + $this->wrapped + ->expects($this->once()) + ->method('query') + ->will($this->returnCallback(function ($domain, $query) use (&$cancelled) { + $deferred = new Deferred(function ($resolve, $reject) use (&$cancelled) { + ++$cancelled; + $reject(new CancellationException('Cancelled')); + }); + + return $deferred->promise(); + })); + + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); + $promise = $this->executor->query('8.8.8.8:53', $query); + + $this->assertEquals(0, $cancelled); + $promise->cancel(); + $this->assertEquals(1, $cancelled); + + $promise->then($this->expectCallableNever(), $this->expectCallableOnce()); + } + + public function testResolvesPromiseWhenWrappedResolves() + { + $this->wrapped + ->expects($this->once()) + ->method('query') + ->willReturn(Promise\resolve('0.0.0.0')); + + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); + $promise = $this->executor->query('8.8.8.8:53', $query); + + $promise->then($this->expectCallableOnce(), $this->expectCallableNever()); + } + + public function testRejectsPromiseWhenWrappedRejects() + { + $this->wrapped + ->expects($this->once()) + ->method('query') + ->willReturn(Promise\reject(new \RuntimeException())); + + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); + $promise = $this->executor->query('8.8.8.8:53', $query); + + $promise->then($this->expectCallableNever(), $this->expectCallableOnceWith(new \RuntimeException())); + } + + public function testWrappedWillBeCancelledOnTimeout() + { + $this->executor = new TimeoutExecutor($this->wrapped, 0, $this->loop); + + $cancelled = 0; + + $this->wrapped + ->expects($this->once()) + ->method('query') + ->will($this->returnCallback(function ($domain, $query) use (&$cancelled) { + $deferred = new Deferred(function ($resolve, $reject) use (&$cancelled) { + ++$cancelled; + $reject(new CancellationException('Cancelled')); + }); + + return $deferred->promise(); + })); + + $callback = $this->expectCallableNever(); + + $errorback = $this->createCallableMock(); + $errorback + ->expects($this->once()) + ->method('__invoke') + ->with($this->logicalAnd( + $this->isInstanceOf('React\Dns\Query\TimeoutException'), + $this->attribute($this->equalTo('DNS query for igor.io timed out'), 'message') + )); + + $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN, 1345656451); + $this->executor->query('8.8.8.8:53', $query)->then($callback, $errorback); + + $this->assertEquals(0, $cancelled); + + $this->loop->run(); + + $this->assertEquals(1, $cancelled); + } +} diff --git a/assets/php/vendor/react/dns/tests/Resolver/FactoryTest.php b/assets/php/vendor/react/dns/tests/Resolver/FactoryTest.php new file mode 100644 index 0000000..acaeac0 --- /dev/null +++ b/assets/php/vendor/react/dns/tests/Resolver/FactoryTest.php @@ -0,0 +1,131 @@ +<?php + +namespace React\Tests\Dns\Resolver; + +use React\Dns\Resolver\Factory; +use React\Tests\Dns\TestCase; +use React\Dns\Query\HostsFileExecutor; + +class FactoryTest extends TestCase +{ + /** @test */ + public function createShouldCreateResolver() + { + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + + $factory = new Factory(); + $resolver = $factory->create('8.8.8.8:53', $loop); + + $this->assertInstanceOf('React\Dns\Resolver\Resolver', $resolver); + } + + /** @test */ + public function createWithoutPortShouldCreateResolverWithDefaultPort() + { + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + + $factory = new Factory(); + $resolver = $factory->create('8.8.8.8', $loop); + + $this->assertInstanceOf('React\Dns\Resolver\Resolver', $resolver); + $this->assertSame('8.8.8.8:53', $this->getResolverPrivateMemberValue($resolver, 'nameserver')); + } + + /** @test */ + public function createCachedShouldCreateResolverWithCachedExecutor() + { + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + + $factory = new Factory(); + $resolver = $factory->createCached('8.8.8.8:53', $loop); + + $this->assertInstanceOf('React\Dns\Resolver\Resolver', $resolver); + $executor = $this->getResolverPrivateExecutor($resolver); + $this->assertInstanceOf('React\Dns\Query\CachedExecutor', $executor); + $recordCache = $this->getCachedExecutorPrivateMemberValue($executor, 'cache'); + $recordCacheCache = $this->getRecordCachePrivateMemberValue($recordCache, 'cache'); + $this->assertInstanceOf('React\Cache\CacheInterface', $recordCacheCache); + $this->assertInstanceOf('React\Cache\ArrayCache', $recordCacheCache); + } + + /** @test */ + public function createCachedShouldCreateResolverWithCachedExecutorWithCustomCache() + { + $cache = $this->getMockBuilder('React\Cache\CacheInterface')->getMock(); + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + + $factory = new Factory(); + $resolver = $factory->createCached('8.8.8.8:53', $loop, $cache); + + $this->assertInstanceOf('React\Dns\Resolver\Resolver', $resolver); + $executor = $this->getResolverPrivateExecutor($resolver); + $this->assertInstanceOf('React\Dns\Query\CachedExecutor', $executor); + $recordCache = $this->getCachedExecutorPrivateMemberValue($executor, 'cache'); + $recordCacheCache = $this->getRecordCachePrivateMemberValue($recordCache, 'cache'); + $this->assertInstanceOf('React\Cache\CacheInterface', $recordCacheCache); + $this->assertSame($cache, $recordCacheCache); + } + + /** + * @test + * @dataProvider factoryShouldAddDefaultPortProvider + */ + public function factoryShouldAddDefaultPort($input, $expected) + { + $loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock(); + + $factory = new Factory(); + $resolver = $factory->create($input, $loop); + + $this->assertInstanceOf('React\Dns\Resolver\Resolver', $resolver); + $this->assertSame($expected, $this->getResolverPrivateMemberValue($resolver, 'nameserver')); + } + + public static function factoryShouldAddDefaultPortProvider() + { + return array( + array('8.8.8.8', '8.8.8.8:53'), + array('1.2.3.4:5', '1.2.3.4:5'), + array('localhost', 'localhost:53'), + array('localhost:1234', 'localhost:1234'), + array('::1', '[::1]:53'), + array('[::1]:53', '[::1]:53') + ); + } + + private function getResolverPrivateExecutor($resolver) + { + $executor = $this->getResolverPrivateMemberValue($resolver, 'executor'); + + // extract underlying executor that may be wrapped in multiple layers of hosts file executors + while ($executor instanceof HostsFileExecutor) { + $reflector = new \ReflectionProperty('React\Dns\Query\HostsFileExecutor', 'fallback'); + $reflector->setAccessible(true); + + $executor = $reflector->getValue($executor); + } + + return $executor; + } + + private function getResolverPrivateMemberValue($resolver, $field) + { + $reflector = new \ReflectionProperty('React\Dns\Resolver\Resolver', $field); + $reflector->setAccessible(true); + return $reflector->getValue($resolver); + } + + private function getCachedExecutorPrivateMemberValue($resolver, $field) + { + $reflector = new \ReflectionProperty('React\Dns\Query\CachedExecutor', $field); + $reflector->setAccessible(true); + return $reflector->getValue($resolver); + } + + private function getRecordCachePrivateMemberValue($resolver, $field) + { + $reflector = new \ReflectionProperty('React\Dns\Query\RecordCache', $field); + $reflector->setAccessible(true); + return $reflector->getValue($resolver); + } +} diff --git a/assets/php/vendor/react/dns/tests/Resolver/ResolveAliasesTest.php b/assets/php/vendor/react/dns/tests/Resolver/ResolveAliasesTest.php new file mode 100644 index 0000000..b5175e3 --- /dev/null +++ b/assets/php/vendor/react/dns/tests/Resolver/ResolveAliasesTest.php @@ -0,0 +1,100 @@ +<?php + +namespace React\Tests\Dns\Resolver; + +use PHPUnit\Framework\TestCase; +use React\Dns\Resolver\Resolver; +use React\Dns\Query\Query; +use React\Dns\Model\Message; +use React\Dns\Model\Record; + +class ResolveAliasesTest extends TestCase +{ + /** + * @covers React\Dns\Resolver\Resolver::resolveAliases + * @dataProvider provideAliasedAnswers + */ + public function testResolveAliases(array $expectedAnswers, array $answers, $name) + { + $executor = $this->createExecutorMock(); + $resolver = new Resolver('8.8.8.8:53', $executor); + + $answers = $resolver->resolveAliases($answers, $name); + + $this->assertEquals($expectedAnswers, $answers); + } + + public function provideAliasedAnswers() + { + return array( + array( + array('178.79.169.131'), + array( + new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.131'), + ), + 'igor.io', + ), + array( + array('178.79.169.131', '178.79.169.132', '178.79.169.133'), + array( + new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.131'), + new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.132'), + new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.133'), + ), + 'igor.io', + ), + array( + array('178.79.169.131'), + array( + new Record('igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.131'), + new Record('foo.igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.131'), + new Record('bar.igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.131'), + ), + 'igor.io', + ), + array( + array(), + array( + new Record('foo.igor.io', Message::TYPE_A, Message::CLASS_IN), + new Record('bar.igor.io', Message::TYPE_A, Message::CLASS_IN), + ), + 'igor.io', + ), + array( + array('178.79.169.131'), + array( + new Record('igor.io', Message::TYPE_CNAME, Message::CLASS_IN, 3600, 'foo.igor.io'), + new Record('foo.igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.131'), + ), + 'igor.io', + ), + array( + array('178.79.169.131'), + array( + new Record('igor.io', Message::TYPE_CNAME, Message::CLASS_IN, 3600, 'foo.igor.io'), + new Record('foo.igor.io', Message::TYPE_CNAME, Message::CLASS_IN, 3600, 'bar.igor.io'), + new Record('bar.igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.131'), + ), + 'igor.io', + ), + array( + array('178.79.169.131', '178.79.169.132', '178.79.169.133'), + array( + new Record('igor.io', Message::TYPE_CNAME, Message::CLASS_IN, 3600, 'foo.igor.io'), + new Record('foo.igor.io', Message::TYPE_CNAME, Message::CLASS_IN, 3600, 'bar.igor.io'), + new Record('bar.igor.io', Message::TYPE_CNAME, Message::CLASS_IN, 3600, 'baz.igor.io'), + new Record('bar.igor.io', Message::TYPE_CNAME, Message::CLASS_IN, 3600, 'qux.igor.io'), + new Record('baz.igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.131'), + new Record('baz.igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.132'), + new Record('qux.igor.io', Message::TYPE_A, Message::CLASS_IN, 3600, '178.79.169.133'), + ), + 'igor.io', + ), + ); + } + + private function createExecutorMock() + { + return $this->getMockBuilder('React\Dns\Query\ExecutorInterface')->getMock(); + } +} diff --git a/assets/php/vendor/react/dns/tests/Resolver/ResolverTest.php b/assets/php/vendor/react/dns/tests/Resolver/ResolverTest.php new file mode 100644 index 0000000..e11509b --- /dev/null +++ b/assets/php/vendor/react/dns/tests/Resolver/ResolverTest.php @@ -0,0 +1,129 @@ +<?php + +namespace React\Tests\Dns\Resolver; + +use React\Dns\Resolver\Resolver; +use React\Dns\Query\Query; +use React\Dns\Model\Message; +use React\Dns\Model\Record; +use React\Promise; +use React\Tests\Dns\TestCase; + +class ResolverTest extends TestCase +{ + /** @test */ + public function resolveShouldQueryARecords() + { + $executor = $this->createExecutorMock(); + $executor + ->expects($this->once()) + ->method('query') + ->with($this->anything(), $this->isInstanceOf('React\Dns\Query\Query')) + ->will($this->returnCallback(function ($nameserver, $query) { + $response = new Message(); + $response->header->set('qr', 1); + $response->questions[] = new Record($query->name, $query->type, $query->class); + $response->answers[] = new Record($query->name, $query->type, $query->class, 3600, '178.79.169.131'); + + return Promise\resolve($response); + })); + + $resolver = new Resolver('8.8.8.8:53', $executor); + $resolver->resolve('igor.io')->then($this->expectCallableOnceWith('178.79.169.131')); + } + + /** @test */ + public function resolveShouldQueryARecordsAndIgnoreCase() + { + $executor = $this->createExecutorMock(); + $executor + ->expects($this->once()) + ->method('query') + ->with($this->anything(), $this->isInstanceOf('React\Dns\Query\Query')) + ->will($this->returnCallback(function ($nameserver, $query) { + $response = new Message(); + $response->header->set('qr', 1); + $response->questions[] = new Record('Blog.wyrihaximus.net', $query->type, $query->class); + $response->answers[] = new Record('Blog.wyrihaximus.net', $query->type, $query->class, 3600, '178.79.169.131'); + + return Promise\resolve($response); + })); + + $resolver = new Resolver('8.8.8.8:53', $executor); + $resolver->resolve('blog.wyrihaximus.net')->then($this->expectCallableOnceWith('178.79.169.131')); + } + + /** @test */ + public function resolveShouldFilterByName() + { + $executor = $this->createExecutorMock(); + $executor + ->expects($this->once()) + ->method('query') + ->with($this->anything(), $this->isInstanceOf('React\Dns\Query\Query')) + ->will($this->returnCallback(function ($nameserver, $query) { + $response = new Message(); + $response->header->set('qr', 1); + $response->questions[] = new Record($query->name, $query->type, $query->class); + $response->answers[] = new Record('foo.bar', $query->type, $query->class, 3600, '178.79.169.131'); + + return Promise\resolve($response); + })); + + $errback = $this->expectCallableOnceWith($this->isInstanceOf('React\Dns\RecordNotFoundException')); + + $resolver = new Resolver('8.8.8.8:53', $executor); + $resolver->resolve('igor.io')->then($this->expectCallableNever(), $errback); + } + + /** @test */ + public function resolveWithNoAnswersShouldThrowException() + { + $executor = $this->createExecutorMock(); + $executor + ->expects($this->once()) + ->method('query') + ->with($this->anything(), $this->isInstanceOf('React\Dns\Query\Query')) + ->will($this->returnCallback(function ($nameserver, $query) { + $response = new Message(); + $response->header->set('qr', 1); + $response->questions[] = new Record($query->name, $query->type, $query->class); + + return Promise\resolve($response); + })); + + $errback = $this->expectCallableOnceWith($this->isInstanceOf('React\Dns\RecordNotFoundException')); + + $resolver = new Resolver('8.8.8.8:53', $executor); + $resolver->resolve('igor.io')->then($this->expectCallableNever(), $errback); + } + + /** + * @test + */ + public function resolveWithNoAnswersShouldCallErrbackIfGiven() + { + $executor = $this->createExecutorMock(); + $executor + ->expects($this->once()) + ->method('query') + ->with($this->anything(), $this->isInstanceOf('React\Dns\Query\Query')) + ->will($this->returnCallback(function ($nameserver, $query) { + $response = new Message(); + $response->header->set('qr', 1); + $response->questions[] = new Record($query->name, $query->type, $query->class); + + return Promise\resolve($response); + })); + + $errback = $this->expectCallableOnceWith($this->isInstanceOf('React\Dns\RecordNotFoundException')); + + $resolver = new Resolver('8.8.8.8:53', $executor); + $resolver->resolve('igor.io')->then($this->expectCallableNever(), $errback); + } + + private function createExecutorMock() + { + return $this->getMockBuilder('React\Dns\Query\ExecutorInterface')->getMock(); + } +} diff --git a/assets/php/vendor/react/dns/tests/TestCase.php b/assets/php/vendor/react/dns/tests/TestCase.php new file mode 100644 index 0000000..a5a22bf --- /dev/null +++ b/assets/php/vendor/react/dns/tests/TestCase.php @@ -0,0 +1,61 @@ +<?php + +namespace React\Tests\Dns; + +use PHPUnit\Framework\TestCase as BaseTestCase; + +abstract class TestCase extends BaseTestCase +{ + protected function expectCallableOnce() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke'); + + return $mock; + } + + protected function expectCallableOnceWith($value) + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($value); + + return $mock; + } + + protected function expectCallableNever() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->never()) + ->method('__invoke'); + + return $mock; + } + + protected function createCallableMock() + { + return $this->getMockBuilder('React\Tests\Dns\CallableStub')->getMock(); + } + + public function setExpectedException($exception, $exceptionMessage = '', $exceptionCode = null) + { + if (method_exists($this, 'expectException')) { + // PHPUnit 5 + $this->expectException($exception); + if ($exceptionMessage !== '') { + $this->expectExceptionMessage($exceptionMessage); + } + if ($exceptionCode !== null) { + $this->expectExceptionCode($exceptionCode); + } + } else { + // legacy PHPUnit 4 + parent::setExpectedException($exception, $exceptionMessage, $exceptionCode); + } + } +} |