Browse Source

Start on HTTP parser tests

master
J. King 4 years ago
parent
commit
ba8a3a6183
  1. 25
      lib/Parser/HTTP/Message.php
  2. 36
      tests/cases/AbstractParserTestCase.php
  3. 31
      tests/cases/HTTP/HTTPTest.php
  4. 20
      tests/cases/HTTP/http.yaml
  5. 43
      tests/cases/README.md
  6. 3
      tests/phpunit.dist.xml
  7. 4
      vendor-bin/phpunit/composer.json
  8. 499
      vendor-bin/phpunit/composer.lock

25
lib/Parser/HTTP/Message.php

@ -14,12 +14,15 @@ use Psr\Http\Message\MessageInterface;
class Message {
protected const TYPE_PATTERN = '/^[A-Za-z0-9!#$%&\'*+\-\.\^_`|~]+\/[A-Za-z0-9!#$%&\'*+\-\.\^_`|~]+\s*(;.*)?$/s';
protected const DATE_PATTERN = '/^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun), \d\d (?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d{4} \d\d:\d\d:\d\d GMT|(?:Mon|Tues|Wednes|Thurs|Fri|Satur|Sun)day, \d\d-(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-\d\d \d\d:\d\d:\d\d GMT|(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun) (?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) (?:\d\d| \d) \d\d:\d\d:\d\d \d{4})$/';
protected const DATE_PATTERN = '/^(?|(Mon|Tue|Wed|Thu|Fri|Sat|Sun), \d\d (?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d{4} \d\d:\d\d:\d\d GMT|(Mon|Tues|Wednes|Thurs|Fri|Satur|Sun)day, \d\d-(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-\d\d \d\d:\d\d:\d\d GMT|(Mon|Tue|Wed|Thu|Fri|Sat|Sun) (?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) (?:\d\d| \d) \d\d:\d\d:\d\d \d{4})$/';
protected const ETAG_PATTERN = '/^.+$/';
protected const CCON_PATTERN = '/(?:^|,)\s*[^=,]+(?:=(?:"(?:\\"|[^"])*"[^,]*|[^,]*))?/';
protected const SDAY_MAP = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
protected const FDAY_MAP = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
/** @var \Psr\Http\Message\MessageInterface */
protected $msg;
protected $url;
public function __construct(MessageInterface $msg, string $url = null) {
$this->msg = $msg;
@ -29,6 +32,7 @@ class Message {
}
public function parse(FeedStruct $feed = null): FeedStruct {
$feed = $feed ?? new FeedStruct;
$feed->meta->url = strlen($this->url ?? "") ? new Url($this->url) : null;
$feed->meta->type = $this->getContentType();
$feed->meta->date = $this->getDate();
@ -55,9 +59,17 @@ class Message {
}
protected function parseDate(string $name): ?Date {
$d = $this->parseHeader($name, self::DATE_PATTERN);
if ($d) {
return Date::createFromString($d[0]);
foreach ($this->msg->getHeader($name) as $h) {
if (preg_match(self::DATE_PATTERN, $h, $match)) {
$out = Date::createFromString($match[0]);
if ($out) {
// ensure the weekday is the correct day for the date
$day = $out->format("w");
if (self::SDAY_MAP[$day] === $match[1] || self::FDAY_MAP[$day] === $match[1]) {
return $out;
}
}
}
}
return null;
}
@ -88,7 +100,8 @@ class Message {
public function getMaxAge(): ?\DateInterval {
$c = $this->parseHeader("Cache-Control", self::CCON_PATTERN, true);
var_export($c);
exit;
//var_export($c);
//exit;
return null;
}
}

36
tests/cases/AbstractParserTestCase.php

@ -6,35 +6,6 @@
declare(strict_types=1);
namespace MensBeam\Lax\TestCase;
/* Test format is as follows:
Each test is a YAML map with the following keys:
- `input`: The test input as a string
- `output`: The result of the parsing upon success; described in more detail below
- `exception`: The exception ID thrown upon failure
- `type`: An HTTP Content-Type (with or without parameters) for the document
- `doc_url`: A fictitious URL where a newsfeed might be located, used for relative URL resolution
The 'input' key along with either 'output' or 'exception' are required for all tests.
The test output is necessarily mangled due to the limits of YAML:
- Any field which should be an absolute URL should be written as a string,
which will be transformed accordingly. Relative URLs should be represented
as a sequence with the relative part first, followed by the base that should
be applied to it
- Any collections should be represented as sequences of maps, which will
all be transformed accordingly
- Rich text can either be supplied as a string (which will yield a Text object
with plain-text content) or as a map with any of the properties of the
Text class listed
The transformations as performed by the `makeFeed` and `makeEntry` methods
of the abstract test case.
*/
use MensBeam\Lax\Feed;
use MensBeam\Lax\Date;
use MensBeam\Lax\Text;
@ -52,6 +23,7 @@ use MensBeam\Lax\Enclosure\Collection as EnclosureCollection;
use MensBeam\Lax\Parser\Exception;
use Symfony\Component\Yaml\Yaml;
use Symfony\Component\Yaml\Parser as YamlParser;
use GuzzleHttp\Psr7\Response;
class AbstractParserTestCase extends \PHPUnit\Framework\TestCase {
protected function provideParserTests(string $glob): iterable {
@ -62,6 +34,10 @@ class AbstractParserTestCase extends \PHPUnit\Framework\TestCase {
} else {
$test->output = $this->makeFeed($test->output);
}
if (is_object($test->input)) {
assert((isset($test->input->head) && $test->input->head instanceof \stdClass) || (isset($test->input->body) && is_string($test->input->body)), "Input is not in a correct format");
$test->input = new Response($test->input->status ?? 200, (array) ($test->input->head ?? []), $test->input->body ?? null);
}
yield "$file: {$description}" => [
$test->input,
$test->type ?? "",
@ -203,6 +179,8 @@ class AbstractParserTestCase extends \PHPUnit\Framework\TestCase {
foreach ($meta as $k => $v) {
if ($k === 'url') {
$m->$k = new Url($v);
} elseif (in_array($k, ["date", "lastModified", "expires"])) {
$m->$k = new Date($v);
} else {
$m->$k = $v;
}

31
tests/cases/HTTP/HTTPTest.php

@ -0,0 +1,31 @@
<?php
/** @license MIT
* Copyright 2018 J. King
* See LICENSE and AUTHORS files for details */
declare(strict_types=1);
namespace MensBeam\Lax\TestCase\JSON;
use GuzzleHttp\Psr7\Response;
/** @covers MensBeam\Lax\Parser\HTTP\Message */
class HTTPTest extends \MensBeam\Lax\TestCase\AbstractParserTestCase {
/** @dataProvider provideHTTPMessages */
public function testParseAnHttpMessage(Response $input, ?string $url, $exp): void {
$p = new \MensBeam\Lax\Parser\HTTP\Message($input, $url);
if ($exp instanceof \Exception) {
$this->expectExceptionObject($exp);
$p->parse();
} else {
$act = $p->parse();
$this->assertEquals($exp, $act);
}
}
public function provideHTTPMessages(): iterable {
foreach ($this->provideParserTests(__DIR__."/*.yaml") as $k => $t) {
array_splice($t, 1, 1);
yield $k => $t;
}
}
}

20
tests/cases/HTTP/http.yaml

@ -0,0 +1,20 @@
Dates:
input:
head:
Date: 'Thu, 05 Mar 2020 23:08:55 GMT'
Expires: 'Fri, 06 Mar 2020 09:22:00 GMT'
Last-Modified: 'Tue, 03 Mar 2020 06:14:00 GMT'
output:
meta:
date: '2020-03-05T23:08:55Z'
expires: '2020-03-06T09:22:00Z'
lastModified: '2020-03-03T06:14:00Z'
Invalid dates:
input:
head:
Date: 'Thu, 05 Mar 2020 25:08:55 GMT'
Expires: 'Fri, 06 Mar 2020 09:22:00 EST'
Last-Modified: 'Wed, 03 Mar 2020 06:14:00 GMT'
output:
meta: {}

43
tests/cases/README.md

@ -0,0 +1,43 @@
# The Lax test suite
## Feed parsing tests
The tests under the `JSON`, `XML`, and `HTTP` directories are parser tests
with a shared format and test harness. The test harness is largely contained
in the `AbstractParserTestCase.php` file, which contains the logic to
transform the tests themselves into native PHP input and output.
Tests are series of YAML files each containing a map (an object), each entry
an individual test which is itself a map. Tests have the following keys:
- `input`: The test input as a string, or a map (see below)
- `output`: The result of the parsing upon success; described
in more detail below
- `exception`: The exception ID thrown upon failure
- `type`: An HTTP Content-Type (with or without parameters) for
the document
- `doc_url`: A fictitious URL where a newsfeed might be located,
used for relative URL resolution
The 'input' key along with either 'output' or 'exception' are required for
all tests. All other keys may or may not be omitted in a test.
The test output is necessarily mangled due to the limits of YAML:
- Any field which should be an absolute URL is written as a string; relative
URLs are represented as a sequence with the relative part first, followed
by the base that is be applied to it
- Any collections are represented as sequences of maps
- Rich text can either be supplied as a string (which represents a Text object
with plain-text content) or as a map with any of the properties of the
Text class listed
If the test input is a map, it represents a response from an HTTP server,
with the following keys:
- `status`: The response status code. This is usually omitted
and should be assumed to be 200 unless specified
- `head`: The map of header-field in the response. Each entry
may be a string, or a sequence of strings
- `body`: The message body as a a string; it may be omitted
if not relevant to the functionality being tested

3
tests/phpunit.dist.xml

@ -31,5 +31,8 @@
<testsuite name="XML">
<directory>cases/XML</directory>
</testsuite>
<testsuite name="HTTP">
<directory>cases/HTTP</directory>
</testsuite>
</testsuites>
</phpunit>

4
vendor-bin/phpunit/composer.json

@ -1,6 +1,8 @@
{
"require-dev": {
"phpunit/phpunit": "^9.0",
"symfony/yaml": "^5.0"
"symfony/yaml": "^5.0",
"guzzlehttp/guzzle": "^6.5",
"guzzlehttp/psr7": "^1.6"
}
}

499
vendor-bin/phpunit/composer.lock

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "5f6ef85b0137813e2008ed286079c104",
"content-hash": "4259aa457f6935c16ad776bc1a3897ea",
"packages": [],
"packages-dev": [
{
@ -63,6 +63,195 @@
],
"time": "2019-10-21T16:45:58+00:00"
},
{
"name": "guzzlehttp/guzzle",
"version": "6.5.3",
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle.git",
"reference": "aab4ebd862aa7d04f01a4b51849d657db56d882e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/aab4ebd862aa7d04f01a4b51849d657db56d882e",
"reference": "aab4ebd862aa7d04f01a4b51849d657db56d882e",
"shasum": ""
},
"require": {
"ext-json": "*",
"guzzlehttp/promises": "^1.0",
"guzzlehttp/psr7": "^1.6.1",
"php": ">=5.5",
"symfony/polyfill-intl-idn": "^1.11"
},
"require-dev": {
"ext-curl": "*",
"phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0",
"psr/log": "^1.1"
},
"suggest": {
"psr/log": "Required for using the Log middleware"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "6.5-dev"
}
},
"autoload": {
"psr-4": {
"GuzzleHttp\\": "src/"
},
"files": [
"src/functions_include.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
}
],
"description": "Guzzle is a PHP HTTP client library",
"homepage": "http://guzzlephp.org/",
"keywords": [
"client",
"curl",
"framework",
"http",
"http client",
"rest",
"web service"
],
"time": "2020-04-18T10:38:46+00:00"
},
{
"name": "guzzlehttp/promises",
"version": "v1.3.1",
"source": {
"type": "git",
"url": "https://github.com/guzzle/promises.git",
"reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646",
"reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646",
"shasum": ""
},
"require": {
"php": ">=5.5.0"
},
"require-dev": {
"phpunit/phpunit": "^4.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.4-dev"
}
},
"autoload": {
"psr-4": {
"GuzzleHttp\\Promise\\": "src/"
},
"files": [
"src/functions_include.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
}
],
"description": "Guzzle promises library",
"keywords": [
"promise"
],
"time": "2016-12-20T10:07:11+00:00"
},
{
"name": "guzzlehttp/psr7",
"version": "1.6.1",
"source": {
"type": "git",
"url": "https://github.com/guzzle/psr7.git",
"reference": "239400de7a173fe9901b9ac7c06497751f00727a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/239400de7a173fe9901b9ac7c06497751f00727a",
"reference": "239400de7a173fe9901b9ac7c06497751f00727a",
"shasum": ""
},
"require": {
"php": ">=5.4.0",
"psr/http-message": "~1.0",
"ralouphie/getallheaders": "^2.0.5 || ^3.0.0"
},
"provide": {
"psr/http-message-implementation": "1.0"
},
"require-dev": {
"ext-zlib": "*",
"phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8"
},
"suggest": {
"zendframework/zend-httphandlerrunner": "Emit PSR-7 responses"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.6-dev"
}
},
"autoload": {
"psr-4": {
"GuzzleHttp\\Psr7\\": "src/"
},
"files": [
"src/functions_include.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
},
{
"name": "Tobias Schultze",
"homepage": "https://github.com/Tobion"
}
],
"description": "PSR-7 message implementation that also provides common utility methods",
"keywords": [
"http",
"message",
"psr-7",
"request",
"response",
"stream",
"uri",
"url"
],
"time": "2019-07-01T23:21:34+00:00"
},
{
"name": "myclabs/deep-copy",
"version": "1.9.5",
@ -825,6 +1014,96 @@
],
"time": "2020-04-03T14:40:04+00:00"
},
{
"name": "psr/http-message",
"version": "1.0.1",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-message.git",
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Http\\Message\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interface for HTTP messages",
"homepage": "https://github.com/php-fig/http-message",
"keywords": [
"http",
"http-message",
"psr",
"psr-7",
"request",
"response"
],
"time": "2016-08-06T14:39:51+00:00"
},
{
"name": "ralouphie/getallheaders",
"version": "3.0.3",
"source": {
"type": "git",
"url": "https://github.com/ralouphie/getallheaders.git",
"reference": "120b605dfeb996808c31b6477290a714d356e822"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
"reference": "120b605dfeb996808c31b6477290a714d356e822",
"shasum": ""
},
"require": {
"php": ">=5.6"
},
"require-dev": {
"php-coveralls/php-coveralls": "^2.1",
"phpunit/phpunit": "^5 || ^6.5"
},
"type": "library",
"autoload": {
"files": [
"src/getallheaders.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Ralph Khattar",
"email": "ralph.khattar@gmail.com"
}
],
"description": "A polyfill for getallheaders.",
"time": "2019-03-08T08:55:37+00:00"
},
{
"name": "sebastian/code-unit",
"version": "1.0.0",
@ -1561,6 +1840,224 @@
],
"time": "2020-02-27T09:26:54+00:00"
},
{
"name": "symfony/polyfill-intl-idn",
"version": "v1.15.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-idn.git",
"reference": "47bd6aa45beb1cd7c6a16b7d1810133b728bdfcf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/47bd6aa45beb1cd7c6a16b7d1810133b728bdfcf",
"reference": "47bd6aa45beb1cd7c6a16b7d1810133b728bdfcf",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"symfony/polyfill-mbstring": "^1.3",
"symfony/polyfill-php72": "^1.10"
},
"suggest": {
"ext-intl": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.15-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Intl\\Idn\\": ""
},
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Laurent Bassin",
"email": "laurent@bassin.info"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"idn",
"intl",
"polyfill",
"portable",
"shim"
],
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-03-09T19:04:49+00:00"
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.15.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "81ffd3a9c6d707be22e3012b827de1c9775fc5ac"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/81ffd3a9c6d707be22e3012b827de1c9775fc5ac",
"reference": "81ffd3a9c6d707be22e3012b827de1c9775fc5ac",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"suggest": {
"ext-mbstring": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.15-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Mbstring\\": ""
},
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for the Mbstring extension",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"mbstring",
"polyfill",
"portable",
"shim"
],
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-03-09T19:04:49+00:00"
},
{
"name": "symfony/polyfill-php72",
"version": "v1.15.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php72.git",
"reference": "37b0976c78b94856543260ce09b460a7bc852747"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/37b0976c78b94856543260ce09b460a7bc852747",
"reference": "37b0976c78b94856543260ce09b460a7bc852747",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.15-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Php72\\": ""
},
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"polyfill",
"portable",
"shim"
],
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-02-27T09:26:54+00:00"
},
{
"name": "symfony/yaml",
"version": "v5.0.7",

Loading…
Cancel
Save