From 4e0e36397c871ef0e26f54d695caf9b425c115e3 Mon Sep 17 00:00:00 2001 From: Tim Starling Date: Wed, 5 Jun 2019 09:59:57 +1000 Subject: [PATCH] REST: Testable EntryPoint * Split EntryPoint into a static main() and a non-static execute() * Add tests for execute() Change-Id: I025356b04ddc5a16494f98c446d785d6bb05ab10 --- includes/Rest/EntryPoint.php | 35 ++++++-- .../phpunit/includes/Rest/EntryPointTest.php | 90 +++++++++++++++++++ .../Rest/Handler/HelloHandlerTest.php | 2 +- .../includes/Rest/Handler/testRoutes.json | 6 -- tests/phpunit/includes/Rest/testRoutes.json | 14 +++ 5 files changed, 134 insertions(+), 13 deletions(-) create mode 100644 tests/phpunit/includes/Rest/EntryPointTest.php delete mode 100644 tests/phpunit/includes/Rest/Handler/testRoutes.json create mode 100644 tests/phpunit/includes/Rest/testRoutes.json diff --git a/includes/Rest/EntryPoint.php b/includes/Rest/EntryPoint.php index d5924f06c1..795999a55c 100644 --- a/includes/Rest/EntryPoint.php +++ b/includes/Rest/EntryPoint.php @@ -6,8 +6,16 @@ use ExtensionRegistry; use MediaWiki\MediaWikiServices; use RequestContext; use Title; +use WebResponse; class EntryPoint { + /** @var RequestInterface */ + private $request; + /** @var WebResponse */ + private $webResponse; + /** @var Router */ + private $router; + public static function main() { // URL safety checks global $wgRequest; @@ -21,8 +29,8 @@ class EntryPoint { RequestContext::getMain()->setTitle( $wgTitle ); $services = MediaWikiServices::getInstance(); - $conf = $services->getMainConfig(); + $request = new RequestFromGlobals( [ 'cookiePrefix' => $conf->get( 'CookiePrefix' ) ] ); @@ -36,20 +44,35 @@ class EntryPoint { new ResponseFactory ); - $response = $router->execute( $request ); + $entryPoint = new self( + $request, + $wgRequest->response(), + $router ); + $entryPoint->execute(); + } + + public function __construct( RequestInterface $request, WebResponse $webResponse, + Router $router + ) { + $this->request = $request; + $this->webResponse = $webResponse; + $this->router = $router; + } + + public function execute() { + $response = $this->router->execute( $this->request ); - $webResponse = $wgRequest->response(); - $webResponse->header( + $this->webResponse->header( 'HTTP/' . $response->getProtocolVersion() . ' ' . $response->getStatusCode() . ' ' . $response->getReasonPhrase() ); foreach ( $response->getRawHeaderLines() as $line ) { - $webResponse->header( $line ); + $this->webResponse->header( $line ); } foreach ( $response->getCookies() as $cookie ) { - $webResponse->setCookie( + $this->webResponse->setCookie( $cookie['name'], $cookie['value'], $cookie['expiry'], diff --git a/tests/phpunit/includes/Rest/EntryPointTest.php b/tests/phpunit/includes/Rest/EntryPointTest.php new file mode 100644 index 0000000000..4f87a70b4f --- /dev/null +++ b/tests/phpunit/includes/Rest/EntryPointTest.php @@ -0,0 +1,90 @@ +getMockBuilder( WebResponse::class ) + ->setMethods( [ 'header' ] ) + ->getMock(); + } + + public static function mockHandlerHeader() { + return new class extends Handler { + public function execute() { + $response = $this->getResponseFactory()->create(); + $response->setHeader( 'Foo', 'Bar' ); + return $response; + } + }; + } + + public function testHeader() { + $webResponse = $this->createWebResponse(); + $webResponse->expects( $this->any() ) + ->method( 'header' ) + ->withConsecutive( + [ 'HTTP/1.1 200 OK', true, null ], + [ 'Foo: Bar', true, null ] + ); + + $entryPoint = new EntryPoint( + new RequestData( [ 'uri' => new Uri( '/rest/mock/EntryPoint/header' ) ] ), + $webResponse, + $this->createRouter() ); + $entryPoint->execute(); + $this->assertTrue( true ); + } + + public static function mockHandlerBodyRewind() { + return new class extends Handler { + public function execute() { + $response = $this->getResponseFactory()->create(); + $stream = new Stream( fopen( 'php://memory', 'w+' ) ); + $stream->write( 'hello' ); + $response->setBody( $stream ); + return $response; + } + }; + } + + /** + * Make sure EntryPoint rewinds a seekable body stream before reading. + */ + public function testBodyRewind() { + $entryPoint = new EntryPoint( + new RequestData( [ 'uri' => new Uri( '/rest/mock/EntryPoint/bodyRewind' ) ] ), + $this->createWebResponse(), + $this->createRouter() ); + ob_start(); + $entryPoint->execute(); + $this->assertSame( 'hello', ob_get_clean() ); + } + +} diff --git a/tests/phpunit/includes/Rest/Handler/HelloHandlerTest.php b/tests/phpunit/includes/Rest/Handler/HelloHandlerTest.php index 9719f9ea67..afbaafb757 100644 --- a/tests/phpunit/includes/Rest/Handler/HelloHandlerTest.php +++ b/tests/phpunit/includes/Rest/Handler/HelloHandlerTest.php @@ -49,7 +49,7 @@ class HelloHandlerTest extends MediaWikiTestCase { /** @dataProvider provideTestViaRouter */ public function testViaRouter( $requestInfo, $responseInfo ) { $router = new Router( - [ __DIR__ . '/testRoutes.json' ], + [ __DIR__ . '/../testRoutes.json' ], [], '/rest', new EmptyBagOStuff(), diff --git a/tests/phpunit/includes/Rest/Handler/testRoutes.json b/tests/phpunit/includes/Rest/Handler/testRoutes.json deleted file mode 100644 index 6b440f7759..0000000000 --- a/tests/phpunit/includes/Rest/Handler/testRoutes.json +++ /dev/null @@ -1,6 +0,0 @@ -[ - { - "path": "/user/{name}/hello", - "class": "MediaWiki\\Rest\\Handler\\HelloHandler" - } -] diff --git a/tests/phpunit/includes/Rest/testRoutes.json b/tests/phpunit/includes/Rest/testRoutes.json new file mode 100644 index 0000000000..7e43bb0c5d --- /dev/null +++ b/tests/phpunit/includes/Rest/testRoutes.json @@ -0,0 +1,14 @@ +[ + { + "path": "/user/{name}/hello", + "class": "MediaWiki\\Rest\\Handler\\HelloHandler" + }, + { + "path": "/mock/EntryPoint/header", + "factory": "MediaWiki\\Tests\\Rest\\EntryPointTest::mockHandlerHeader" + }, + { + "path": "/mock/EntryPoint/bodyRewind", + "factory": "MediaWiki\\Tests\\Rest\\EntryPointTest::mockHandlerBodyRewind" + } +] -- 2.20.1