From 35e6a680a9b1293a12d769d289c21d971af3b2b8 Mon Sep 17 00:00:00 2001 From: Thomas Flori <thflori@gmail.com> Date: Sun, 5 Aug 2018 13:59:28 +0200 Subject: [PATCH] add tests and .travis.yml and .gitattributes --- .gitattributes | 5 + .travis.yml | 23 ++ phpunit.xml | 20 ++ tests/ClientRequestTest.php | 194 +++++++++++++ tests/ClientResponseTest.php | 250 +++++++++++++++++ tests/ServerRequestTest.php | 522 +++++++++++++++++++++++++++++++++++ tests/ServerResponseTest.php | 10 + 7 files changed, 1024 insertions(+) create mode 100644 .gitattributes create mode 100644 .travis.yml create mode 100644 phpunit.xml create mode 100644 tests/ClientRequestTest.php create mode 100644 tests/ClientResponseTest.php create mode 100644 tests/ServerRequestTest.php create mode 100644 tests/ServerResponseTest.php diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..09dfee1 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +/tests export-ignore +.gitattributes export-ignore +.gitignore export-ignore +.travis.yml export-ignore +phpunit.xml export-ignore diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..cae9ee3 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,23 @@ +language: php +php: +- 7.1 +- 7.2 + +sudo: false + +cache: + directories: + - $HOME/.composer/cache + +matrix: + fast_finish: true + +before_script: +- composer install --no-interaction +- sh -c 'if [ "$TRAVIS_PHP_VERSION" = "7.1" ]; then composer require satooshi/php-coveralls:~0.6@stable; fi;' +- mkdir -p build/logs + +script: +- composer code-style +- vendor/bin/phpunit -c phpunit.xml --coverage-clover=build/logs/clover.xml --coverage-text +- sh -c 'if [ "$TRAVIS_PHP_VERSION" = "7.1" ]; then php vendor/bin/coveralls -v; fi;' diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..8cd9d23 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<phpunit bootstrap="vendor/autoload.php" + backupGlobals="true" + backupStaticAttributes="false" + verbose="true" + colors="true" + convertErrorsToExceptions="true" + convertNoticesToExceptions="true" + convertWarningsToExceptions="true" + processIsolation="false" + stopOnFailure="false"> + <testsuite name="tests"> + <directory>./tests/</directory> + </testsuite> + <filter> + <whitelist processUncoveredFilesFromWhitelist="true"> + <directory suffix=".php">./src/</directory> + </whitelist> + </filter> +</phpunit> diff --git a/tests/ClientRequestTest.php b/tests/ClientRequestTest.php new file mode 100644 index 0000000..c35d1ba --- /dev/null +++ b/tests/ClientRequestTest.php @@ -0,0 +1,194 @@ +<?php + +namespace Tal\Test; + +use GuzzleHttp\Psr7; +use PHPUnit\Framework\TestCase; +use Tal\ClientRequest as Request; +use GuzzleHttp\Psr7\Uri; + +class RequestTest extends TestCase +{ + public function testRequestUriMayBeString() + { + $r = new Request('GET', '/'); + $this->assertEquals('/', (string) $r->getUri()); + } + + public function testRequestUriMayBeUri() + { + $uri = new Uri('/'); + $r = new Request('GET', $uri); + $this->assertSame($uri, $r->getUri()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testValidateRequestUri() + { + new Request('GET', '///'); + } + + public function testCanConstructWithBody() + { + $r = new Request('GET', '/', [], 'baz'); + $this->assertInstanceOf('Psr\Http\Message\StreamInterface', $r->getBody()); + $this->assertEquals('baz', (string) $r->getBody()); + } + + public function testNullBody() + { + $r = new Request('GET', '/', [], null); + $this->assertInstanceOf('Psr\Http\Message\StreamInterface', $r->getBody()); + $this->assertSame('', (string) $r->getBody()); + } + + public function testFalseyBody() + { + $r = new Request('GET', '/', [], '0'); + $this->assertInstanceOf('Psr\Http\Message\StreamInterface', $r->getBody()); + $this->assertSame('0', (string) $r->getBody()); + } + + public function testConstructorDoesNotReadStreamBody() + { + $streamIsRead = false; + $body = Psr7\FnStream::decorate(Psr7\stream_for(''), [ + '__toString' => function () use (&$streamIsRead) { + $streamIsRead = true; + return ''; + } + ]); + + $r = new Request('GET', '/', [], $body); + $this->assertFalse($streamIsRead); + $this->assertSame($body, $r->getBody()); + } + + public function testCapitalizesMethod() + { + $r = new Request('get', '/'); + $this->assertEquals('GET', $r->getMethod()); + } + + public function testCapitalizesWithMethod() + { + $r = new Request('GET', '/'); + $this->assertEquals('PUT', $r->withMethod('put')->getMethod()); + } + + public function testWithUri() + { + $r1 = new Request('GET', '/'); + $u1 = $r1->getUri(); + $u2 = new Uri('http://www.example.com'); + $r2 = $r1->withUri($u2); + $this->assertNotSame($r1, $r2); + $this->assertSame($u2, $r2->getUri()); + $this->assertSame($u1, $r1->getUri()); + } + + public function testSameInstanceWhenSameUri() + { + $r1 = new Request('GET', 'http://foo.com'); + $r2 = $r1->withUri($r1->getUri()); + $this->assertSame($r1, $r2); + } + + public function testWithRequestTarget() + { + $r1 = new Request('GET', '/'); + $r2 = $r1->withRequestTarget('*'); + $this->assertEquals('*', $r2->getRequestTarget()); + $this->assertEquals('/', $r1->getRequestTarget()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testRequestTargetDoesNotAllowSpaces() + { + $r1 = new Request('GET', '/'); + $r1->withRequestTarget('/foo bar'); + } + + public function testRequestTargetDefaultsToSlash() + { + $r1 = new Request('GET', ''); + $this->assertEquals('/', $r1->getRequestTarget()); + $r2 = new Request('GET', '*'); + $this->assertEquals('*', $r2->getRequestTarget()); + $r3 = new Request('GET', 'http://foo.com/bar baz/'); + $this->assertEquals('/bar%20baz/', $r3->getRequestTarget()); + } + + public function testBuildsRequestTarget() + { + $r1 = new Request('GET', 'http://foo.com/baz?bar=bam'); + $this->assertEquals('/baz?bar=bam', $r1->getRequestTarget()); + } + + public function testBuildsRequestTargetWithFalseyQuery() + { + $r1 = new Request('GET', 'http://foo.com/baz?0'); + $this->assertEquals('/baz?0', $r1->getRequestTarget()); + } + + public function testHostIsAddedFirst() + { + $r = new Request('GET', 'http://foo.com/baz?bar=bam', ['Foo' => 'Bar']); + $this->assertEquals([ + 'Host' => ['foo.com'], + 'Foo' => ['Bar'] + ], $r->getHeaders()); + } + + public function testCanGetHeaderAsCsv() + { + $r = new Request('GET', 'http://foo.com/baz?bar=bam', [ + 'Foo' => ['a', 'b', 'c'] + ]); + $this->assertEquals('a, b, c', $r->getHeaderLine('Foo')); + $this->assertEquals('', $r->getHeaderLine('Bar')); + } + + public function testHostIsNotOverwrittenWhenPreservingHost() + { + $r = new Request('GET', 'http://foo.com/baz?bar=bam', ['Host' => 'a.com']); + $this->assertEquals(['Host' => ['a.com']], $r->getHeaders()); + $r2 = $r->withUri(new Uri('http://www.foo.com/bar'), true); + $this->assertEquals('a.com', $r2->getHeaderLine('Host')); + } + + public function testOverridesHostWithUri() + { + $r = new Request('GET', 'http://foo.com/baz?bar=bam'); + $this->assertEquals(['Host' => ['foo.com']], $r->getHeaders()); + $r2 = $r->withUri(new Uri('http://www.baz.com/bar')); + $this->assertEquals('www.baz.com', $r2->getHeaderLine('Host')); + } + + public function testAggregatesHeaders() + { + $r = new Request('GET', '', [ + 'ZOO' => 'zoobar', + 'zoo' => ['foobar', 'zoobar'] + ]); + $this->assertEquals(['ZOO' => ['zoobar', 'foobar', 'zoobar']], $r->getHeaders()); + $this->assertEquals('zoobar, foobar, zoobar', $r->getHeaderLine('zoo')); + } + + public function testAddsPortToHeader() + { + $r = new Request('GET', 'http://foo.com:8124/bar'); + $this->assertEquals('foo.com:8124', $r->getHeaderLine('host')); + } + + public function testAddsPortToHeaderAndReplacePreviousPort() + { + $r = new Request('GET', 'http://foo.com:8124/bar'); + $r = $r->withUri(new Uri('http://foo.com:8125/bar')); + $this->assertEquals('foo.com:8125', $r->getHeaderLine('host')); + } +} diff --git a/tests/ClientResponseTest.php b/tests/ClientResponseTest.php new file mode 100644 index 0000000..0c30ecc --- /dev/null +++ b/tests/ClientResponseTest.php @@ -0,0 +1,250 @@ +<?php + +namespace Tal\Test; + +use GuzzleHttp\Psr7; +use PHPUnit\Framework\TestCase; +use Tal\ClientResponse as Response; + +class ClientResponseTest extends TestCase +{ + public function testDefaultConstructor() + { + $r = new Response(); + $this->assertSame(200, $r->getStatusCode()); + $this->assertSame('1.1', $r->getProtocolVersion()); + $this->assertSame('OK', $r->getReasonPhrase()); + $this->assertSame([], $r->getHeaders()); + $this->assertInstanceOf('Psr\Http\Message\StreamInterface', $r->getBody()); + $this->assertSame('', (string) $r->getBody()); + } + + public function testCanConstructWithStatusCode() + { + $r = new Response(404); + $this->assertSame(404, $r->getStatusCode()); + $this->assertSame('Not Found', $r->getReasonPhrase()); + } + + public function testConstructorDoesNotReadStreamBody() + { + $streamIsRead = false; + $body = Psr7\FnStream::decorate(Psr7\stream_for(''), [ + '__toString' => function () use (&$streamIsRead) { + $streamIsRead = true; + return ''; + } + ]); + + $r = new Response(200, [], $body); + $this->assertFalse($streamIsRead); + $this->assertSame($body, $r->getBody()); + } + + public function testStatusCanBeNumericString() + { + $r = new Response('404'); + $r2 = $r->withStatus('201'); + $this->assertSame(404, $r->getStatusCode()); + $this->assertSame('Not Found', $r->getReasonPhrase()); + $this->assertSame(201, $r2->getStatusCode()); + $this->assertSame('Created', $r2->getReasonPhrase()); + } + + public function testCanConstructWithHeaders() + { + $r = new Response(200, ['Foo' => 'Bar']); + $this->assertSame(['Foo' => ['Bar']], $r->getHeaders()); + $this->assertSame('Bar', $r->getHeaderLine('Foo')); + $this->assertSame(['Bar'], $r->getHeader('Foo')); + } + + public function testCanConstructWithHeadersAsArray() + { + $r = new Response(200, [ + 'Foo' => ['baz', 'bar'] + ]); + $this->assertSame(['Foo' => ['baz', 'bar']], $r->getHeaders()); + $this->assertSame('baz, bar', $r->getHeaderLine('Foo')); + $this->assertSame(['baz', 'bar'], $r->getHeader('Foo')); + } + + public function testCanConstructWithBody() + { + $r = new Response(200, [], 'baz'); + $this->assertInstanceOf('Psr\Http\Message\StreamInterface', $r->getBody()); + $this->assertSame('baz', (string) $r->getBody()); + } + + public function testNullBody() + { + $r = new Response(200, [], null); + $this->assertInstanceOf('Psr\Http\Message\StreamInterface', $r->getBody()); + $this->assertSame('', (string) $r->getBody()); + } + + public function testFalseyBody() + { + $r = new Response(200, [], '0'); + $this->assertInstanceOf('Psr\Http\Message\StreamInterface', $r->getBody()); + $this->assertSame('0', (string) $r->getBody()); + } + + public function testCanConstructWithReason() + { + $r = new Response(200, [], null, '1.1', 'bar'); + $this->assertSame('bar', $r->getReasonPhrase()); + + $r = new Response(200, [], null, '1.1', '0'); + $this->assertSame('0', $r->getReasonPhrase(), 'Falsey reason works'); + } + + public function testCanConstructWithProtocolVersion() + { + $r = new Response(200, [], null, '1000'); + $this->assertSame('1000', $r->getProtocolVersion()); + } + + public function testWithStatusCodeAndNoReason() + { + $r = (new Response())->withStatus(201); + $this->assertSame(201, $r->getStatusCode()); + $this->assertSame('Created', $r->getReasonPhrase()); + } + + public function testWithStatusCodeAndReason() + { + $r = (new Response())->withStatus(201, 'Foo'); + $this->assertSame(201, $r->getStatusCode()); + $this->assertSame('Foo', $r->getReasonPhrase()); + + $r = (new Response())->withStatus(201, '0'); + $this->assertSame(201, $r->getStatusCode()); + $this->assertSame('0', $r->getReasonPhrase(), 'Falsey reason works'); + } + + public function testWithProtocolVersion() + { + $r = (new Response())->withProtocolVersion('1000'); + $this->assertSame('1000', $r->getProtocolVersion()); + } + + public function testSameInstanceWhenSameProtocol() + { + $r = new Response(); + $this->assertSame($r, $r->withProtocolVersion('1.1')); + } + + public function testWithBody() + { + $b = Psr7\stream_for('0'); + $r = (new Response())->withBody($b); + $this->assertInstanceOf('Psr\Http\Message\StreamInterface', $r->getBody()); + $this->assertSame('0', (string) $r->getBody()); + } + + public function testSameInstanceWhenSameBody() + { + $r = new Response(); + $b = $r->getBody(); + $this->assertSame($r, $r->withBody($b)); + } + + public function testWithHeader() + { + $r = new Response(200, ['Foo' => 'Bar']); + $r2 = $r->withHeader('baZ', 'Bam'); + $this->assertSame(['Foo' => ['Bar']], $r->getHeaders()); + $this->assertSame(['Foo' => ['Bar'], 'baZ' => ['Bam']], $r2->getHeaders()); + $this->assertSame('Bam', $r2->getHeaderLine('baz')); + $this->assertSame(['Bam'], $r2->getHeader('baz')); + } + + public function testWithHeaderAsArray() + { + $r = new Response(200, ['Foo' => 'Bar']); + $r2 = $r->withHeader('baZ', ['Bam', 'Bar']); + $this->assertSame(['Foo' => ['Bar']], $r->getHeaders()); + $this->assertSame(['Foo' => ['Bar'], 'baZ' => ['Bam', 'Bar']], $r2->getHeaders()); + $this->assertSame('Bam, Bar', $r2->getHeaderLine('baz')); + $this->assertSame(['Bam', 'Bar'], $r2->getHeader('baz')); + } + + public function testWithHeaderReplacesDifferentCase() + { + $r = new Response(200, ['Foo' => 'Bar']); + $r2 = $r->withHeader('foO', 'Bam'); + $this->assertSame(['Foo' => ['Bar']], $r->getHeaders()); + $this->assertSame(['foO' => ['Bam']], $r2->getHeaders()); + $this->assertSame('Bam', $r2->getHeaderLine('foo')); + $this->assertSame(['Bam'], $r2->getHeader('foo')); + } + + public function testWithAddedHeader() + { + $r = new Response(200, ['Foo' => 'Bar']); + $r2 = $r->withAddedHeader('foO', 'Baz'); + $this->assertSame(['Foo' => ['Bar']], $r->getHeaders()); + $this->assertSame(['Foo' => ['Bar', 'Baz']], $r2->getHeaders()); + $this->assertSame('Bar, Baz', $r2->getHeaderLine('foo')); + $this->assertSame(['Bar', 'Baz'], $r2->getHeader('foo')); + } + + public function testWithAddedHeaderAsArray() + { + $r = new Response(200, ['Foo' => 'Bar']); + $r2 = $r->withAddedHeader('foO', ['Baz', 'Bam']); + $this->assertSame(['Foo' => ['Bar']], $r->getHeaders()); + $this->assertSame(['Foo' => ['Bar', 'Baz', 'Bam']], $r2->getHeaders()); + $this->assertSame('Bar, Baz, Bam', $r2->getHeaderLine('foo')); + $this->assertSame(['Bar', 'Baz', 'Bam'], $r2->getHeader('foo')); + } + + public function testWithAddedHeaderThatDoesNotExist() + { + $r = new Response(200, ['Foo' => 'Bar']); + $r2 = $r->withAddedHeader('nEw', 'Baz'); + $this->assertSame(['Foo' => ['Bar']], $r->getHeaders()); + $this->assertSame(['Foo' => ['Bar'], 'nEw' => ['Baz']], $r2->getHeaders()); + $this->assertSame('Baz', $r2->getHeaderLine('new')); + $this->assertSame(['Baz'], $r2->getHeader('new')); + } + + public function testWithoutHeaderThatExists() + { + $r = new Response(200, ['Foo' => 'Bar', 'Baz' => 'Bam']); + $r2 = $r->withoutHeader('foO'); + $this->assertTrue($r->hasHeader('foo')); + $this->assertSame(['Foo' => ['Bar'], 'Baz' => ['Bam']], $r->getHeaders()); + $this->assertFalse($r2->hasHeader('foo')); + $this->assertSame(['Baz' => ['Bam']], $r2->getHeaders()); + } + + public function testWithoutHeaderThatDoesNotExist() + { + $r = new Response(200, ['Baz' => 'Bam']); + $r2 = $r->withoutHeader('foO'); + $this->assertSame($r, $r2); + $this->assertFalse($r2->hasHeader('foo')); + $this->assertSame(['Baz' => ['Bam']], $r2->getHeaders()); + } + + public function testSameInstanceWhenRemovingMissingHeader() + { + $r = new Response(); + $this->assertSame($r, $r->withoutHeader('foo')); + } + + public function testHeaderValuesAreTrimmed() + { + $r1 = new Response(200, ['OWS' => " \t \tFoo\t \t "]); + $r2 = (new Response())->withHeader('OWS', " \t \tFoo\t \t "); + $r3 = (new Response())->withAddedHeader('OWS', " \t \tFoo\t \t ");; + + foreach ([$r1, $r2, $r3] as $r) { + $this->assertSame(['OWS' => ['Foo']], $r->getHeaders()); + $this->assertSame('Foo', $r->getHeaderLine('OWS')); + $this->assertSame(['Foo'], $r->getHeader('OWS')); + } + } +} diff --git a/tests/ServerRequestTest.php b/tests/ServerRequestTest.php new file mode 100644 index 0000000..9e30186 --- /dev/null +++ b/tests/ServerRequestTest.php @@ -0,0 +1,522 @@ +<?php + +namespace Tal\Test; + +use PHPUnit\Framework\TestCase; +use Tal\ServerRequest; +use GuzzleHttp\Psr7\UploadedFile; +use GuzzleHttp\Psr7\Uri; + +class ServerRequestTest extends TestCase +{ + public function dataNormalizeFiles() + { + return [ + 'Single file' => [ + [ + 'file' => [ + 'name' => 'MyFile.txt', + 'type' => 'text/plain', + 'tmp_name' => '/tmp/php/php1h4j1o', + 'error' => '0', + 'size' => '123' + ] + ], + [ + 'file' => new UploadedFile( + '/tmp/php/php1h4j1o', + 123, + UPLOAD_ERR_OK, + 'MyFile.txt', + 'text/plain' + ) + ] + ], + 'Empty file' => [ + [ + 'image_file' => [ + 'name' => '', + 'type' => '', + 'tmp_name' => '', + 'error' => '4', + 'size' => '0' + ] + ], + [ + 'image_file' => new UploadedFile( + '', + 0, + UPLOAD_ERR_NO_FILE, + '', + '' + ) + ] + ], + 'Already Converted' => [ + [ + 'file' => new UploadedFile( + '/tmp/php/php1h4j1o', + 123, + UPLOAD_ERR_OK, + 'MyFile.txt', + 'text/plain' + ) + ], + [ + 'file' => new UploadedFile( + '/tmp/php/php1h4j1o', + 123, + UPLOAD_ERR_OK, + 'MyFile.txt', + 'text/plain' + ) + ] + ], + 'Already Converted array' => [ + [ + 'file' => [ + new UploadedFile( + '/tmp/php/php1h4j1o', + 123, + UPLOAD_ERR_OK, + 'MyFile.txt', + 'text/plain' + ), + new UploadedFile( + '', + 0, + UPLOAD_ERR_NO_FILE, + '', + '' + ) + ], + ], + [ + 'file' => [ + new UploadedFile( + '/tmp/php/php1h4j1o', + 123, + UPLOAD_ERR_OK, + 'MyFile.txt', + 'text/plain' + ), + new UploadedFile( + '', + 0, + UPLOAD_ERR_NO_FILE, + '', + '' + ) + ], + ] + ], + 'Multiple files' => [ + [ + 'text_file' => [ + 'name' => 'MyFile.txt', + 'type' => 'text/plain', + 'tmp_name' => '/tmp/php/php1h4j1o', + 'error' => '0', + 'size' => '123' + ], + 'image_file' => [ + 'name' => '', + 'type' => '', + 'tmp_name' => '', + 'error' => '4', + 'size' => '0' + ] + ], + [ + 'text_file' => new UploadedFile( + '/tmp/php/php1h4j1o', + 123, + UPLOAD_ERR_OK, + 'MyFile.txt', + 'text/plain' + ), + 'image_file' => new UploadedFile( + '', + 0, + UPLOAD_ERR_NO_FILE, + '', + '' + ) + ] + ], + 'Nested files' => [ + [ + 'file' => [ + 'name' => [ + 0 => 'MyFile.txt', + 1 => 'Image.png', + ], + 'type' => [ + 0 => 'text/plain', + 1 => 'image/png', + ], + 'tmp_name' => [ + 0 => '/tmp/php/hp9hskjhf', + 1 => '/tmp/php/php1h4j1o', + ], + 'error' => [ + 0 => '0', + 1 => '0', + ], + 'size' => [ + 0 => '123', + 1 => '7349', + ], + ], + 'nested' => [ + 'name' => [ + 'other' => 'Flag.txt', + 'test' => [ + 0 => 'Stuff.txt', + 1 => '', + ], + ], + 'type' => [ + 'other' => 'text/plain', + 'test' => [ + 0 => 'text/plain', + 1 => '', + ], + ], + 'tmp_name' => [ + 'other' => '/tmp/php/hp9hskjhf', + 'test' => [ + 0 => '/tmp/php/asifu2gp3', + 1 => '', + ], + ], + 'error' => [ + 'other' => '0', + 'test' => [ + 0 => '0', + 1 => '4', + ], + ], + 'size' => [ + 'other' => '421', + 'test' => [ + 0 => '32', + 1 => '0', + ] + ] + ], + ], + [ + 'file' => [ + 0 => new UploadedFile( + '/tmp/php/hp9hskjhf', + 123, + UPLOAD_ERR_OK, + 'MyFile.txt', + 'text/plain' + ), + 1 => new UploadedFile( + '/tmp/php/php1h4j1o', + 7349, + UPLOAD_ERR_OK, + 'Image.png', + 'image/png' + ), + ], + 'nested' => [ + 'other' => new UploadedFile( + '/tmp/php/hp9hskjhf', + 421, + UPLOAD_ERR_OK, + 'Flag.txt', + 'text/plain' + ), + 'test' => [ + 0 => new UploadedFile( + '/tmp/php/asifu2gp3', + 32, + UPLOAD_ERR_OK, + 'Stuff.txt', + 'text/plain' + ), + 1 => new UploadedFile( + '', + 0, + UPLOAD_ERR_NO_FILE, + '', + '' + ), + ] + ] + ] + ] + ]; + } + + /** + * @dataProvider dataNormalizeFiles + */ + public function testNormalizeFiles($files, $expected) + { + $result = ServerRequest::normalizeFiles($files); + + $this->assertEquals($expected, $result); + } + + public function testNormalizeFilesRaisesException() + { + self::expectException('InvalidArgumentException'); + self::expectExceptionMessage('Invalid value in files specification'); + + ServerRequest::normalizeFiles(['test' => 'something']); + } + + public function dataGetUriFromGlobals() + { + $server = [ + 'REQUEST_URI' => '/blog/article.php?id=10&user=foo', + 'SERVER_PORT' => '443', + 'SERVER_ADDR' => '217.112.82.20', + 'SERVER_NAME' => 'www.example.org', + 'SERVER_PROTOCOL' => 'HTTP/1.1', + 'REQUEST_METHOD' => 'POST', + 'QUERY_STRING' => 'id=10&user=foo', + 'DOCUMENT_ROOT' => '/path/to/your/server/root/', + 'HTTP_HOST' => 'www.example.org', + 'HTTPS' => 'on', + 'REMOTE_ADDR' => '193.60.168.69', + 'REMOTE_PORT' => '5390', + 'SCRIPT_NAME' => '/blog/article.php', + 'SCRIPT_FILENAME' => '/path/to/your/server/root/blog/article.php', + 'PHP_SELF' => '/blog/article.php', + ]; + + return [ + 'HTTPS request' => [ + 'https://www.example.org/blog/article.php?id=10&user=foo', + $server, + ], + 'HTTPS request with different on value' => [ + 'https://www.example.org/blog/article.php?id=10&user=foo', + array_merge($server, ['HTTPS' => '1']), + ], + 'HTTP request' => [ + 'http://www.example.org/blog/article.php?id=10&user=foo', + array_merge($server, ['HTTPS' => 'off', 'SERVER_PORT' => '80']), + ], + 'HTTP_HOST missing -> fallback to SERVER_NAME' => [ + 'https://www.example.org/blog/article.php?id=10&user=foo', + array_merge($server, ['HTTP_HOST' => null]), + ], + 'HTTP_HOST and SERVER_NAME missing -> fallback to SERVER_ADDR' => [ + 'https://217.112.82.20/blog/article.php?id=10&user=foo', + array_merge($server, ['HTTP_HOST' => null, 'SERVER_NAME' => null]), + ], + 'No query String' => [ + 'https://www.example.org/blog/article.php', + array_merge($server, ['REQUEST_URI' => '/blog/article.php', 'QUERY_STRING' => '']), + ], + 'Host header with port' => [ + 'https://www.example.org:8324/blog/article.php?id=10&user=foo', + array_merge($server, ['HTTP_HOST' => 'www.example.org:8324']), + ], + 'Different port with SERVER_PORT' => [ + 'https://www.example.org:8324/blog/article.php?id=10&user=foo', + array_merge($server, ['SERVER_PORT' => '8324']), + ], + 'REQUEST_URI missing query string' => [ + 'https://www.example.org/blog/article.php?id=10&user=foo', + array_merge($server, ['REQUEST_URI' => '/blog/article.php']), + ], + 'Empty server variable' => [ + 'http://localhost', + [], + ], + ]; + } + + /** + * @dataProvider dataGetUriFromGlobals + */ + public function testGetUriFromGlobals($expected, $serverParams) + { + $_SERVER = $serverParams; + + $this->assertEquals(new Uri($expected), ServerRequest::getUriFromGlobals()); + } + + public function testFromGlobals() + { + $_SERVER = [ + 'REQUEST_URI' => '/blog/article.php?id=10&user=foo', + 'SERVER_PORT' => '443', + 'SERVER_ADDR' => '217.112.82.20', + 'SERVER_NAME' => 'www.example.org', + 'SERVER_PROTOCOL' => 'HTTP/1.1', + 'REQUEST_METHOD' => 'POST', + 'QUERY_STRING' => 'id=10&user=foo', + 'DOCUMENT_ROOT' => '/path/to/your/server/root/', + 'HTTP_HOST' => 'www.example.org', + 'HTTPS' => 'on', + 'REMOTE_ADDR' => '193.60.168.69', + 'REMOTE_PORT' => '5390', + 'SCRIPT_NAME' => '/blog/article.php', + 'SCRIPT_FILENAME' => '/path/to/your/server/root/blog/article.php', + 'PHP_SELF' => '/blog/article.php', + ]; + + $_COOKIE = [ + 'logged-in' => 'yes!' + ]; + + $_POST = [ + 'name' => 'Pesho', + 'email' => 'pesho@example.com', + ]; + + $_GET = [ + 'id' => 10, + 'user' => 'foo', + ]; + + $_FILES = [ + 'file' => [ + 'name' => 'MyFile.txt', + 'type' => 'text/plain', + 'tmp_name' => '/tmp/php/php1h4j1o', + 'error' => UPLOAD_ERR_OK, + 'size' => 123, + ] + ]; + + $server = ServerRequest::fromGlobals(); + + $this->assertSame('POST', $server->getMethod()); + $this->assertEquals(['Host' => ['www.example.org']], $server->getHeaders()); + $this->assertSame('', (string) $server->getBody()); + $this->assertSame('1.1', $server->getProtocolVersion()); + $this->assertEquals($_COOKIE, $server->getCookieParams()); + $this->assertEquals($_POST, $server->getParsedBody()); + $this->assertEquals($_GET, $server->getQueryParams()); + + $this->assertEquals( + new Uri('https://www.example.org/blog/article.php?id=10&user=foo'), + $server->getUri() + ); + + $expectedFiles = [ + 'file' => new UploadedFile( + '/tmp/php/php1h4j1o', + 123, + UPLOAD_ERR_OK, + 'MyFile.txt', + 'text/plain' + ), + ]; + + $this->assertEquals($expectedFiles, $server->getUploadedFiles()); + } + + public function testUploadedFiles() + { + $request1 = new ServerRequest('GET', '/'); + + $files = [ + 'file' => new UploadedFile('test', 123, UPLOAD_ERR_OK) + ]; + + $request2 = $request1->withUploadedFiles($files); + + $this->assertNotSame($request2, $request1); + $this->assertSame([], $request1->getUploadedFiles()); + $this->assertSame($files, $request2->getUploadedFiles()); + } + + public function testServerParams() + { + $params = ['name' => 'value']; + + $request = new ServerRequest('GET', '/', [], null, '1.1', $params); + $this->assertSame($params, $request->getServerParams()); + } + + public function testCookieParams() + { + $request1 = new ServerRequest('GET', '/'); + + $params = ['name' => 'value']; + + $request2 = $request1->withCookieParams($params); + + $this->assertNotSame($request2, $request1); + $this->assertEmpty($request1->getCookieParams()); + $this->assertSame($params, $request2->getCookieParams()); + } + + public function testQueryParams() + { + $request1 = new ServerRequest('GET', '/'); + + $params = ['name' => 'value']; + + $request2 = $request1->withQueryParams($params); + + $this->assertNotSame($request2, $request1); + $this->assertEmpty($request1->getQueryParams()); + $this->assertSame($params, $request2->getQueryParams()); + } + + public function testParsedBody() + { + $request1 = new ServerRequest('GET', '/'); + + $params = ['name' => 'value']; + + $request2 = $request1->withParsedBody($params); + + $this->assertNotSame($request2, $request1); + $this->assertEmpty($request1->getParsedBody()); + $this->assertSame($params, $request2->getParsedBody()); + } + + public function testAttributes() + { + $request1 = new ServerRequest('GET', '/'); + + $request2 = $request1->withAttribute('name', 'value'); + $request3 = $request2->withAttribute('other', 'otherValue'); + $request4 = $request3->withoutAttribute('other'); + $request5 = $request3->withoutAttribute('unknown'); + + $this->assertNotSame($request2, $request1); + $this->assertNotSame($request3, $request2); + $this->assertNotSame($request4, $request3); + $this->assertSame($request5, $request3); + + $this->assertSame([], $request1->getAttributes()); + $this->assertNull($request1->getAttribute('name')); + $this->assertSame( + 'something', + $request1->getAttribute('name', 'something'), + 'Should return the default value' + ); + + $this->assertSame('value', $request2->getAttribute('name')); + $this->assertSame(['name' => 'value'], $request2->getAttributes()); + $this->assertEquals(['name' => 'value', 'other' => 'otherValue'], $request3->getAttributes()); + $this->assertSame(['name' => 'value'], $request4->getAttributes()); + } + + public function testNullAttribute() + { + $request = (new ServerRequest('GET', '/'))->withAttribute('name', null); + + $this->assertSame(['name' => null], $request->getAttributes()); + $this->assertNull($request->getAttribute('name', 'different-default')); + + $requestWithoutAttribute = $request->withoutAttribute('name'); + + $this->assertSame([], $requestWithoutAttribute->getAttributes()); + $this->assertSame('different-default', $requestWithoutAttribute->getAttribute('name', 'different-default')); + } +} diff --git a/tests/ServerResponseTest.php b/tests/ServerResponseTest.php new file mode 100644 index 0000000..3057e3c --- /dev/null +++ b/tests/ServerResponseTest.php @@ -0,0 +1,10 @@ +<?php + +namespace Tal\Test; + +use PHPUnit\Framework\TestCase; + +class ServerResponseTest extends TestCase +{ + +} -- GitLab