mirror of
https://code.mensbeam.com/MensBeam/Arsse.git
synced 2024-12-22 21:22:40 +00:00
Start on tests for Feed
- Makes use of PHP's internal Web server to deliver expected responses from a real server - Windows batch file can be used to run tests (Linux and Mac test runners to come later) - Added PHPUnit to dev dependencies
This commit is contained in:
parent
88c0f4986c
commit
590abaf0ef
10 changed files with 1232 additions and 20 deletions
|
@ -29,7 +29,8 @@
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"mikey179/vfsStream": "^1.6.4",
|
"mikey179/vfsStream": "^1.6.4",
|
||||||
"phake/phake": "^2.3.2"
|
"phake/phake": "^2.3.2",
|
||||||
|
"phpunit/phpunit": "^6.0.5"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
|
|
1049
composer.lock
generated
1049
composer.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -95,6 +95,6 @@ abstract class AbstractStatement implements Statement {
|
||||||
$time = (int) $date;
|
$time = (int) $date;
|
||||||
}
|
}
|
||||||
// ISO 8601 with space in the middle instead of T.
|
// ISO 8601 with space in the middle instead of T.
|
||||||
return date($this->dateFormat($part), $time);
|
return gmdate($this->dateFormat($part), $time);
|
||||||
}
|
}
|
||||||
}
|
}
|
44
lib/Misc/DateFormatter.php
Normal file
44
lib/Misc/DateFormatter.php
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
namespace JKingWeb\Arsse\Misc;
|
||||||
|
|
||||||
|
trait DateFormatter {
|
||||||
|
|
||||||
|
protected function dateTransform($date, string $format = "iso8601", bool $local = false) {
|
||||||
|
$date = $this->dateNormalize($date);
|
||||||
|
$format = strtolower($format);
|
||||||
|
if($format=="unix") return $date;
|
||||||
|
switch ($format) {
|
||||||
|
case 'http': $f = "D, d M Y H:i:s \G\M\T"; break;
|
||||||
|
case 'iso8601': $f = \DateTime::ATOM; break;
|
||||||
|
case 'sql': $f = "Y-m-d H:i:s"; break;
|
||||||
|
case 'date': $f = "Y-m-d"; break;
|
||||||
|
case 'time': $f = "H:i:s"; break;
|
||||||
|
default: $f = \DateTime::ATOM; break;
|
||||||
|
}
|
||||||
|
if($local) {
|
||||||
|
return date($f, $date);
|
||||||
|
} else {
|
||||||
|
return gmdate($f, $date);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function dateNormalize($date) {
|
||||||
|
// convert input to a Unix timestamp
|
||||||
|
if($date instanceof \DateTimeInterface) {
|
||||||
|
$time = $date->getTimestamp();
|
||||||
|
} else if(is_numeric($date)) {
|
||||||
|
$time = (int) $date;
|
||||||
|
} else if($date===null) {
|
||||||
|
return null;
|
||||||
|
} else if(is_string($date)) {
|
||||||
|
$time = strtotime($date);
|
||||||
|
if($time===false) return null;
|
||||||
|
} else if (is_bool($date)) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
$time = (int) $date;
|
||||||
|
}
|
||||||
|
return $time;
|
||||||
|
}
|
||||||
|
}
|
63
tests/Feed/TestFeed.php
Normal file
63
tests/Feed/TestFeed.php
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
namespace JKingWeb\Arsse;
|
||||||
|
Use Phake;
|
||||||
|
|
||||||
|
|
||||||
|
class TestFeed extends \PHPUnit\Framework\TestCase {
|
||||||
|
use Test\Tools;
|
||||||
|
|
||||||
|
protected $base = "http://localhost:8000/Feed/";
|
||||||
|
|
||||||
|
function time(string $t): string {
|
||||||
|
return gmdate("D, d M Y H:i:s \G\M\T", strtotime($t));
|
||||||
|
}
|
||||||
|
|
||||||
|
function setUp() {
|
||||||
|
$this->clearData();
|
||||||
|
Data::$conf = new Conf();
|
||||||
|
}
|
||||||
|
|
||||||
|
function testComputeNextFetchFrom304() {
|
||||||
|
// if less than half an hour, check in 15 minutes
|
||||||
|
$exp = strtotime("now + 15 minutes");
|
||||||
|
$t = strtotime("now");
|
||||||
|
$f = new Feed(null, $this->base."NextFetch/NotModified?t=$t", $this->dateTransform($t, "http"));
|
||||||
|
$this->assertTime($exp, $f->nextFetch);
|
||||||
|
$t = strtotime("now - 29 minutes");
|
||||||
|
$f = new Feed(null, $this->base."NextFetch/NotModified?t=$t", $this->dateTransform($t, "http"));
|
||||||
|
$this->assertTime($exp, $f->nextFetch);
|
||||||
|
// if less than an hour, check in 30 minutes
|
||||||
|
$exp = strtotime("now + 30 minutes");
|
||||||
|
$t = strtotime("now - 30 minutes");
|
||||||
|
$f = new Feed(null, $this->base."NextFetch/NotModified?t=$t", $this->dateTransform($t, "http"));
|
||||||
|
$this->assertTime($exp, $f->nextFetch);
|
||||||
|
$t = strtotime("now - 59 minutes");
|
||||||
|
$f = new Feed(null, $this->base."NextFetch/NotModified?t=$t", $this->dateTransform($t, "http"));
|
||||||
|
$this->assertTime($exp, $f->nextFetch);
|
||||||
|
// if less than three hours, check in an hour
|
||||||
|
$exp = strtotime("now + 1 hour");
|
||||||
|
$t = strtotime("now - 1 hour");
|
||||||
|
$f = new Feed(null, $this->base."NextFetch/NotModified?t=$t", $this->dateTransform($t, "http"));
|
||||||
|
$this->assertTime($exp, $f->nextFetch);
|
||||||
|
$t = strtotime("now - 2 hours 59 minutes");
|
||||||
|
$f = new Feed(null, $this->base."NextFetch/NotModified?t=$t", $this->dateTransform($t, "http"));
|
||||||
|
$this->assertTime($exp, $f->nextFetch);
|
||||||
|
// if more than 36 hours, check in 24 hours
|
||||||
|
$exp = strtotime("now + 1 day");
|
||||||
|
$t = strtotime("now - 36 hours");
|
||||||
|
$f = new Feed(null, $this->base."NextFetch/NotModified?t=$t", $this->dateTransform($t, "http"));
|
||||||
|
$this->assertTime($exp, $f->nextFetch);
|
||||||
|
$t = strtotime("now - 2 years");
|
||||||
|
$f = new Feed(null, $this->base."NextFetch/NotModified?t=$t", $this->dateTransform($t, "http"));
|
||||||
|
$this->assertTime($exp, $f->nextFetch);
|
||||||
|
// otherwise check in three hours
|
||||||
|
$exp = strtotime("now + 3 hours");
|
||||||
|
$t = strtotime("now - 6 hours");
|
||||||
|
$f = new Feed(null, $this->base."NextFetch/NotModified?t=$t", $this->dateTransform($t, "http"));
|
||||||
|
$this->assertTime($exp, $f->nextFetch);
|
||||||
|
$t = strtotime("now - 35 hours");
|
||||||
|
$f = new Feed(null, $this->base."NextFetch/NotModified?t=$t", $this->dateTransform($t, "http"));
|
||||||
|
$this->assertTime($exp, $f->nextFetch);
|
||||||
|
}
|
||||||
|
}
|
4
tests/docroot/Feed/NextFetch/NotModified.php
Normal file
4
tests/docroot/Feed/NextFetch/NotModified.php
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<?php return [
|
||||||
|
'code' => 304,
|
||||||
|
'lastMod' => (int) $_GET['t'],
|
||||||
|
];
|
|
@ -5,6 +5,8 @@ use JKingWeb\Arsse\Exception;
|
||||||
use JKingWeb\Arsse\Data;
|
use JKingWeb\Arsse\Data;
|
||||||
|
|
||||||
trait Tools {
|
trait Tools {
|
||||||
|
use \JKingWeb\Arsse\Misc\DateFormatter;
|
||||||
|
|
||||||
function assertException(string $msg, string $prefix = "", string $type = "Exception") {
|
function assertException(string $msg, string $prefix = "", string $type = "Exception") {
|
||||||
$class = \JKingWeb\Arsse\NS_BASE . ($prefix !== "" ? str_replace("/", "\\", $prefix) . "\\" : "") . $type;
|
$class = \JKingWeb\Arsse\NS_BASE . ($prefix !== "" ? str_replace("/", "\\", $prefix) . "\\" : "") . $type;
|
||||||
$msgID = ($prefix !== "" ? $prefix . "/" : "") . $type. ".$msg";
|
$msgID = ($prefix !== "" ? $prefix . "/" : "") . $type. ".$msg";
|
||||||
|
@ -17,6 +19,12 @@ trait Tools {
|
||||||
$this->expectExceptionCode($code);
|
$this->expectExceptionCode($code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function assertTime($exp, $test) {
|
||||||
|
$exp = $this->dateTransform($exp);
|
||||||
|
$test = $this->dateTransform($test);
|
||||||
|
$this->assertSame($exp, $test);
|
||||||
|
}
|
||||||
|
|
||||||
function clearData(bool $loadLang = true): bool {
|
function clearData(bool $loadLang = true): bool {
|
||||||
$r = new \ReflectionClass(\JKingWeb\Arsse\Data::class);
|
$r = new \ReflectionClass(\JKingWeb\Arsse\Data::class);
|
||||||
$props = array_keys($r->getStaticProperties());
|
$props = array_keys($r->getStaticProperties());
|
||||||
|
|
|
@ -43,5 +43,8 @@
|
||||||
<file>REST/NextCloudNews/TestNCNVersionDiscovery.php</file>
|
<file>REST/NextCloudNews/TestNCNVersionDiscovery.php</file>
|
||||||
<file>REST/NextCloudNews/TestNCNV1_2.php</file>
|
<file>REST/NextCloudNews/TestNCNV1_2.php</file>
|
||||||
</testsuite>
|
</testsuite>
|
||||||
|
<testsuite name="Feed parser">
|
||||||
|
<file>Feed/TestFeed.php</file>
|
||||||
|
</testsuite>
|
||||||
</testsuites>
|
</testsuites>
|
||||||
</phpunit>
|
</phpunit>
|
66
tests/server.php
Normal file
66
tests/server.php
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
namespace JKingWeb\Arsse;
|
||||||
|
require_once __DIR__."/../bootstrap.php";
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
This is a so-called router for the the internal PHP Web server:
|
||||||
|
<http://php.net/manual/en/features.commandline.webserver.php>
|
||||||
|
|
||||||
|
It is used to test feed parsing in a controlled environment,
|
||||||
|
answering specific requests used in tests with the data required
|
||||||
|
to pass the test.
|
||||||
|
|
||||||
|
The parameters of the responses are kept in separate files,
|
||||||
|
which include the following data:
|
||||||
|
|
||||||
|
- Response content
|
||||||
|
- Response code
|
||||||
|
- Content type
|
||||||
|
- Whether to send cache headers
|
||||||
|
- Last modified
|
||||||
|
- Any other headers
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
$defaults = [ // default values for response
|
||||||
|
'code' => 200,
|
||||||
|
'content' => "",
|
||||||
|
'mime' => "application/octet-stream",
|
||||||
|
'lastMod' => time(),
|
||||||
|
'cache' => true,
|
||||||
|
'fields' => [],
|
||||||
|
];
|
||||||
|
|
||||||
|
$url = explode("?",$_SERVER['REQUEST_URI'])[0];
|
||||||
|
$base = BASE."tests".\DIRECTORY_SEPARATOR."docroot";
|
||||||
|
$test = $base.str_replace("/",\DIRECTORY_SEPARATOR,$url).".php";
|
||||||
|
if(!file_exists($test)) {
|
||||||
|
$response = [
|
||||||
|
'code' => 499,
|
||||||
|
'content' => "Test '$test' missing.",
|
||||||
|
'mime' => "application/octet-stream",
|
||||||
|
'lastMod' => time(),
|
||||||
|
'cache' => true,
|
||||||
|
'fields' => [],
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
$response = array_merge($defaults, (include $test));
|
||||||
|
}
|
||||||
|
// set the response code
|
||||||
|
http_response_code((int) $response['code']);
|
||||||
|
// if the response has a body, set the content type and (possibly) the ETag.
|
||||||
|
if(strlen($response['content'])) {
|
||||||
|
header("Content-Type: ".$response['mime']);
|
||||||
|
if($response['cache']) header("ETag: ".md5($response['content']));
|
||||||
|
}
|
||||||
|
// if caching is enabled, set the last-modified date
|
||||||
|
if($response['cache']) header("Last-Modified: ".gmdate("D, d M Y H:i:s \G\M\T", $response['lastMod']));
|
||||||
|
// set any other specified fields verbatim
|
||||||
|
foreach($response['fields'] as $h) {
|
||||||
|
header($h);
|
||||||
|
}
|
||||||
|
// send the content
|
||||||
|
echo $response['content'];
|
10
tests/test.bat
Normal file
10
tests/test.bat
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
@echo off
|
||||||
|
setlocal
|
||||||
|
set base=%~dp0
|
||||||
|
start php -S localhost:8000 "%base%\server.php"
|
||||||
|
php "%base%\..\vendor\phpunit\phpunit\phpunit" -c "%base%\phpunit.xml"
|
||||||
|
timeout /nobreak /t 1 >nul
|
||||||
|
for /f "usebackq tokens=5" %%a in (`netstat -aon ^| find "LISTENING" ^| find ":8000"`) do (
|
||||||
|
taskkill /pid %%a >nul
|
||||||
|
goto :eof
|
||||||
|
)
|
Loading…
Reference in a new issue