Skip to content

Commit

Permalink
App::run() now calls sendResponse() and terminate() methods, which ar…
Browse files Browse the repository at this point in the history
…e now them private. Added AppTesting::getResponse()
  • Loading branch information
iuravic committed Jun 18, 2017
1 parent cfe05c8 commit 1fdc5b6
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 50 deletions.
52 changes: 16 additions & 36 deletions src/Core/App.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
HttpException,
ContainerServiceNotFound
};
use Psr\Http\Message\RequestInterface;

class App
{
Expand All @@ -40,7 +41,7 @@ class App
/**
* @var \Psr\Http\Message\ResponseInterface $response
*/
private $response;
protected $response;

/**
* @var \Duktig\Core\DI\ContainerInterface $container
Expand Down Expand Up @@ -90,39 +91,33 @@ public function __construct(

/**
* Application entry point. Takes the request as param, or if it is not
* provided, it is created one from the globals, and runs it throught the
* whole app stack.
*
* After running this method, the internal variable $response is set, which
* can then be fetched with the method $this->getResponse().
*
* In the end, the method $this->terminate() should be called, as the last
* method in the app chain.
* provided, one is created from the globals, and runs it throught the
* whole app stack. The response is then sent to the browser and the app
* business is finished.
*
* @param ServerRequestInterface $request [optional] If null, creates it from
* the globals
* @throws Throwable
* @return App
*/
public function run(ServerRequestInterface $request = null) : App
public function run(ServerRequestInterface $request = null) : void
{
if (null === $request) {
$request = $this->getContainer()
->get(ServerRequestFactoryInterface::class)
->createServerRequestFromArray($_SERVER);
}
try {
$response = $this->handleRequest($request);
$this->response = $this->handleRequest($request);
} catch (\Throwable $e) {
if ($this->config->getParam('env') != 'prod') {
throw $e;
}
$this->exceptionHandler->report($e);
$response = $this->exceptionHandler->throwableToResponse($e);
$this->response = $this->exceptionHandler->throwableToResponse($e);
}

$this->response = $response;
return $this;
$this->sendResponse($this->response);
$this->terminate();
}

/**
Expand Down Expand Up @@ -203,17 +198,6 @@ private function getFullMiddlewareStack(ServerRequestInterface $request,
return $stack;
}

/**
* Gets the response from the app. After the run() method is called, the
* internal response parameter is set, and can be retrieved here.
*
* @return ResponseInterface|NULL
*/
public function getResponse() : ?ResponseInterface
{
return $this->response;
}

protected function getContainer() : ContainerInterface
{
return $this->container;
Expand All @@ -222,26 +206,22 @@ protected function getContainer() : ContainerInterface
/**
* Sends the response to the browser.
*
* @param ResponseInterface $response [optional] If no $response param is given,
* it sends the internal response parameter.
* @param ResponseInterface $response
* @return void|\Duktig\Core\App
*/
public function sendResponse(ResponseInterface $response = null) : App
private function sendResponse(ResponseInterface $response) : App
{
$response = $response ?? $this->response;
if ($response !== null) {
$this->eventDispatcher->dispatch(new EventBeforeAppReponseSending($response));
$this->responseSender->sendResponse($response);
$this->eventDispatcher->dispatch(new EventAfterAppReponseSending($response));
}
$this->eventDispatcher->dispatch(new EventBeforeAppReponseSending($response));
$this->responseSender->sendResponse($response);
$this->eventDispatcher->dispatch(new EventAfterAppReponseSending($response));
return $this;
}

/**
* Finishes up the app business. It is the last method to be called after
* every request.
*/
public function terminate() : void
private function terminate() : void
{
$this->eventDispatcher->dispatch(new EventSimple('duktig.core.app.beforeTerminate'));
}
Expand Down
6 changes: 6 additions & 0 deletions tests/AppTesting.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

use Duktig\Core\App;
use Duktig\Core\DI\ContainerInterface;
use Psr\Http\Message\ResponseInterface;

/**
* This class is intended to be used in test environment instead of the
Expand All @@ -16,4 +17,9 @@ public function getContainer() : ContainerInterface
{
return parent::getContainer();
}

public function getResponse() : ResponseInterface
{
return $this->response;
}
}
30 changes: 16 additions & 14 deletions tests/functional/Core/AppTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
namespace Duktig\Core;

use PHPUnit\Framework\TestCase;
use Duktig\Test\AppTesting;
use Psr\Http\Message\ResponseInterface;
use Duktig\Http\Factory\Adapter\Guzzle\GuzzleServerRequestFactory;
use Duktig\Core\Exception\HttpException;
Expand All @@ -15,7 +16,7 @@ public function setUp()
parent::setUp();
$this->app = (new AppFactory())->make(
__DIR__.'/../../Config/configTest.php',
App::class
AppTesting::class
);
}

Expand All @@ -34,7 +35,9 @@ public function testApplicationCreated()
public function testConvertsRequestIntoResponse()
{
$request = $this->getRequest('/route-with-callable-handler');
ob_start();
$this->app->run($request);
ob_end_clean();
$response = $this->app->getResponse();
$this->assertInstanceOf(ResponseInterface::class, $response,
"getResponse() should have returned ResponseInterface object but didn't");
Expand All @@ -43,21 +46,21 @@ public function testConvertsRequestIntoResponse()
public function testGetsResponseFromRouteWithACallableHandler()
{
$request = $this->getRequest('/route-with-callable-handler');
$this->app->run($request)->sendResponse();
$this->app->run($request);
$this->expectOutputRegex('/Response body set by the callable handler/');
}

public function testGetsResponseFromRouteWithAResolvableController()
{
$request = $this->getRequest('/resolvable/page/testUP1/testUP2?conf=qval1&testqp2=qval2');
$this->app->run($request)->sendResponse();
$this->app->run($request);
$this->expectOutputRegex('/Body set by ResolvableController::pageAction/');
}

public function testUriAndQueryParamsArePassedToController()
{
$request = $this->getRequest('/resolvable/page/testUP1/testUP2?conf=qval1&testqp2=qval2');
$this->app->run($request)->sendResponse();
$this->app->run($request);
$string = 'Uri params: '.json_encode(['uriParam1' => 'testUP1','uriParam2' => 'testUP2'])
."\n".'Query params: '.json_encode(['conf' => 'qval1','testqp2' => 'qval2']);
$this->expectOutputRegex('/'.$string.'/');
Expand All @@ -66,7 +69,7 @@ public function testUriAndQueryParamsArePassedToController()
public function testResponseIsRenderedInTemplate()
{
$request = $this->getRequest('/template-test');
$this->app->run($request)->sendResponse();
$this->app->run($request);
$this->expectOutputRegex('/<h1>Test Template<\/h1>/');
$this->expectOutputRegex('/<p>Lets render some text: testing123<\/p>/');
}
Expand Down Expand Up @@ -103,14 +106,14 @@ public function testThrowsExceptionIfNoRouteIsMatched()
public function testServiceWithDependencyIsResolved()
{
$request = $this->getRequest('/dependency-test');
$this->app->run($request)->sendResponse();
$this->app->run($request);
$this->expectOutputRegex('/Logging service provided instanceof Monolog\\\Logger/');
}

public function testCustomServiceWithDependencyIsResolved()
{
$request = $this->getRequest('/custom-service-resolution-test');
$this->app->run($request)->sendResponse();
$this->app->run($request);
$this->expectOutputRegex(
'/Custom service lazy loaded and injected with its own dependencies'
.' Duktig\\\Test\\\Helpers\\\Service\\\TestService/'
Expand All @@ -124,20 +127,20 @@ public function testThrowsExceptionIfAppCannotResolveAService()
$this->expectExceptionMessage(
"Unable to get: Duktig\Test\Helpers\Controller\InvalidDependencyController"
);
$this->app->run($request)->sendResponse();
$this->app->run($request);
}

public function testRunsApplicationGlobalMiddleware()
{
$request = $this->getRequest('/resolvable/page/testUP1/testUP2?conf=qval1&testqp2=qval2');
$this->app->run($request)->sendResponse();
$this->app->run($request);
$this->expectOutputRegex('/<!-- Response modified by TestAppMiddleware -->/');
}

public function testRunsRouteSpecificMiddleware()
{
$request = $this->getRequest('/route-specific-middleware-test');
$this->app->run($request)->sendResponse();
$this->app->run($request);
$this->expectOutputRegex('/Response body modified by TestRouteSpecificMiddleware/');
}

Expand All @@ -148,20 +151,20 @@ public function testThrowsExceptionIfAppCannotResolveRouteMiddleware()
$this->expectExceptionMessage(
"Unable to resolve middleware Duktig\Test\Helpers\Middleware\NonexistantMiddleware"
);
$this->app->run($request)->sendResponse();
$this->app->run($request);
}

public function testACoreEventOnAppTerminateIsDispatchedAndTheListenerIsExecuted()
{
$request = $this->getRequest('/resolvable/page/testUP1/testUP2?conf=qval1&testqp2=qval2');
$this->app->run($request)->sendResponse()->terminate();
$this->app->run($request);
$this->expectOutputRegex('/Output echoed by custom listener for event duktig.core.app.beforeTerminate/');
}

public function testEventIsRegisteredProgrammaticallyAndTheListenerIsExecuted()
{
$request = $this->getRequest('/custom-event-test');
$this->app->run($request)->sendResponse();
$this->app->run($request);
$this->expectOutputRegex('/EventTestListener has altered the response when EventTest was triggered/');
}

Expand All @@ -177,7 +180,6 @@ public function testEventIsRegisteredInConfigAndListenerAsCallableIsExecuted()
public function testThrowsExceptionIfListenerCannotBeResolved()
{
$request = $this->getRequest('/event-with-invalid-listener');
// $this->expectException(\InvalidArgumentException::class);
$this->expectException(\Psr\Container\NotFoundExceptionInterface::class);
$this->expectExceptionMessage(
"Invalid service as listener 'Duktig\Test\Event\ListenerWhichDoesNotExist'"
Expand Down

0 comments on commit 1fdc5b6

Please sign in to comment.