Skip to content
This repository was archived by the owner on Jan 29, 2020. It is now read-only.

Commit 2db114f

Browse files
committed
Merge branch 'hotfix/error-middleware-pipe'
Close #278
2 parents a656721 + dacfab0 commit 2db114f

File tree

3 files changed

+229
-2
lines changed

3 files changed

+229
-2
lines changed

src/Application.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ public function pipeErrorHandler($path, $middleware = null)
339339
$middleware = $this->prepareMiddleware($middleware, $this->container, $forError = true);
340340
}
341341

342-
$this->pipe($path, $middleware);
342+
parent::pipe($path, $middleware);
343343

344344
return $this;
345345
}

src/ErrorMiddlewarePipe.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public function __construct(MiddlewarePipe $pipeline)
6262
* @param callable $out
6363
* @return Response
6464
*/
65-
public function __invoke($error, Request $request, Response $response, callable $out = null)
65+
public function __invoke($error, Request $request, Response $response, callable $out)
6666
{
6767
// Decorate instances with Stratigility decorators; required to work
6868
// with Next implementation.
Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
<?php
2+
/**
3+
* Zend Framework (http://framework.zend.com/)
4+
*
5+
* @see https://github.com/zendframework/zend-expressive for the canonical source repository
6+
* @copyright Copyright (c) 2015-2016 Zend Technologies USA Inc. (http://www.zend.com)
7+
* @license https://github.com/zendframework/zend-expressive/blob/master/LICENSE.md New BSD License
8+
*/
9+
10+
namespace ZendTest\Expressive\Container;
11+
12+
use PHPUnit_Framework_TestCase as TestCase;
13+
use Prophecy\Argument;
14+
use Psr\Http\Message\ServerRequestInterface;
15+
use Psr\Http\Message\ResponseInterface;
16+
use Zend\Diactoros\Response;
17+
use Zend\Diactoros\Response\EmitterInterface;
18+
use Zend\Diactoros\ServerRequest;
19+
use Zend\Expressive\Container\ApplicationFactory;
20+
use Zend\Expressive\Router\Route;
21+
use Zend\Expressive\Router\RouteResult;
22+
use Zend\Expressive\Router\RouterInterface;
23+
use ZendTest\Expressive\ContainerTrait;
24+
25+
/**
26+
* Integration test case for Application instances generated by ApplicationFactory.
27+
*
28+
* This test case is to verify that various configuration scenarios provide an
29+
* application that will behave in specific ways, including:
30+
*
31+
* - Given a middleware spec resolving to error middleware, that middleware
32+
* will be executed in error conditions.
33+
* - Given a middleware spec resolving to an error middleware pipeline, that
34+
* pipeline will be executed in error conditions.
35+
*/
36+
class ApplicationFactoryIntegrationTest extends TestCase
37+
{
38+
use ContainerTrait;
39+
40+
protected $container;
41+
42+
public function setUp()
43+
{
44+
$this->container = $this->mockContainerInterface();
45+
$this->factory = new ApplicationFactory();
46+
47+
$this->router = $this->prophesize(RouterInterface::class);
48+
$this->emitter = $this->prophesize(EmitterInterface::class);
49+
50+
$this->injectServiceInContainer($this->container, RouterInterface::class, $this->router->reveal());
51+
$this->injectServiceInContainer($this->container, EmitterInterface::class, $this->emitter->reveal());
52+
}
53+
54+
public function testConfiguredErrorMiddlewarePipeIsExecutedWhenMiddlewareCallsNextWithError()
55+
{
56+
$always = function ($request, $response, $next) {
57+
$response = $next($request, $response);
58+
return $response->withHeader('X-Always', 'true');
59+
};
60+
61+
$routeResultSpy = function ($request, $response, $next) {
62+
$result = $request->getAttribute(RouteResult::class, false);
63+
$value = $result ? $result->getMatchedRouteName() : 'not-found';
64+
return $next($request, $response->withHeader('X-Route-Result', $value));
65+
};
66+
67+
$needsAuthentication = function ($request, $response, $next) {
68+
return $next($request, $response, '401');
69+
};
70+
71+
$unauthorized = function ($error, $request, $response, callable $next = null) {
72+
$this->assertEquals('401', $error);
73+
$response->getBody()->write('Error middleware called');
74+
return $response->withStatus(401);
75+
};
76+
77+
$finalHandler = function ($request, $response, $err = null) {
78+
$this->fail('Should not hit final handler, but did');
79+
};
80+
81+
$routeResult = $this->prophesize(RouteResult::class);
82+
$routeResult->isFailure()->willReturn(false);
83+
$routeResult->getMatchedParams()->willReturn([]);
84+
$routeResult->getMatchedRouteName()->willReturn('needs-auth');
85+
$routeResult->getMatchedMiddleware()->willReturn('NeedsAuthentication');
86+
$this->router->match(Argument::type(ServerRequestInterface::class))->willReturn($routeResult->reveal());
87+
88+
$this->injectServiceInContainer($this->container, 'Always', $always);
89+
$this->injectServiceInContainer($this->container, 'RouteResultSpy', $routeResultSpy);
90+
$this->injectServiceInContainer($this->container, 'NeedsAuthentication', $needsAuthentication);
91+
$this->injectServiceInContainer($this->container, 'Unauthorized', $unauthorized);
92+
$this->injectServiceInContainer($this->container, 'Zend\Expressive\FinalHandler', $finalHandler);
93+
94+
$config = [
95+
'routes' => [
96+
'name' => 'needs-auth',
97+
'path' => '/needs/authentication',
98+
'middleware' => 'NeedsAuthentication',
99+
'allowed_methods' => ['GET'],
100+
],
101+
'middleware_pipeline' => [
102+
'always' => [
103+
'middleware' => [
104+
'Always',
105+
],
106+
'priority' => 10000,
107+
],
108+
'routing' => [
109+
'middleware' => [
110+
ApplicationFactory::ROUTING_MIDDLEWARE,
111+
'RouteResultSpy',
112+
ApplicationFactory::DISPATCH_MIDDLEWARE,
113+
],
114+
'priority' => 1,
115+
],
116+
'error' => [
117+
'middleware' => [
118+
'Unauthorized',
119+
],
120+
'priority' => -10000,
121+
'error' => true,
122+
],
123+
],
124+
];
125+
$this->injectServiceInContainer($this->container, 'config', $config);
126+
127+
$app = $this->factory->__invoke($this->container->reveal());
128+
129+
$request = new ServerRequest([], [], 'http://example.com/needs/authentication', 'GET');
130+
$response = new Response();
131+
132+
$response = $app($request, $response);
133+
$this->assertInstanceOf(ResponseInterface::class, $response);
134+
$this->assertEquals(401, $response->getStatusCode(), 'Unexpected response');
135+
$this->assertTrue($response->hasHeader('X-Always'));
136+
$this->assertEquals('true', $response->getHeaderLine('X-Always'));
137+
$this->assertTrue($response->hasHeader('X-Route-Result'));
138+
$this->assertEquals('needs-auth', $response->getHeaderLine('X-Route-Result'));
139+
$this->assertEquals('Error middleware called', (string) $response->getBody());
140+
}
141+
142+
public function testConfiguredErrorMiddlewareIsExecutedWhenMiddlewareCallsNextWithError()
143+
{
144+
$always = function ($request, $response, $next) {
145+
$response = $next($request, $response);
146+
return $response->withHeader('X-Always', 'true');
147+
};
148+
149+
$routeResultSpy = function ($request, $response, $next) {
150+
$result = $request->getAttribute(RouteResult::class, false);
151+
$value = $result ? $result->getMatchedRouteName() : 'not-found';
152+
return $next($request, $response->withHeader('X-Route-Result', $value));
153+
};
154+
155+
$needsAuthentication = function ($request, $response, $next) {
156+
return $next($request, $response, '401');
157+
};
158+
159+
$unauthorized = function ($error, $request, $response, callable $next = null) {
160+
$this->assertEquals('401', $error);
161+
$response->getBody()->write('Error middleware called');
162+
return $response->withStatus(401);
163+
};
164+
165+
$finalHandler = function ($request, $response, $err = null) {
166+
$this->fail('Should not hit final handler, but did');
167+
};
168+
169+
$routeResult = $this->prophesize(RouteResult::class);
170+
$routeResult->isFailure()->willReturn(false);
171+
$routeResult->getMatchedParams()->willReturn([]);
172+
$routeResult->getMatchedRouteName()->willReturn('needs-auth');
173+
$routeResult->getMatchedMiddleware()->willReturn('NeedsAuthentication');
174+
$this->router->match(Argument::type(ServerRequestInterface::class))->willReturn($routeResult->reveal());
175+
176+
$this->injectServiceInContainer($this->container, 'Always', $always);
177+
$this->injectServiceInContainer($this->container, 'RouteResultSpy', $routeResultSpy);
178+
$this->injectServiceInContainer($this->container, 'NeedsAuthentication', $needsAuthentication);
179+
$this->injectServiceInContainer($this->container, 'Unauthorized', $unauthorized);
180+
$this->injectServiceInContainer($this->container, 'Zend\Expressive\FinalHandler', $finalHandler);
181+
182+
$config = [
183+
'routes' => [
184+
'name' => 'needs-auth',
185+
'path' => '/needs/authentication',
186+
'middleware' => 'NeedsAuthentication',
187+
'allowed_methods' => ['GET'],
188+
],
189+
'middleware_pipeline' => [
190+
'always' => [
191+
'middleware' => [
192+
'Always',
193+
],
194+
'priority' => 10000,
195+
],
196+
'routing' => [
197+
'middleware' => [
198+
ApplicationFactory::ROUTING_MIDDLEWARE,
199+
'RouteResultSpy',
200+
ApplicationFactory::DISPATCH_MIDDLEWARE,
201+
],
202+
'priority' => 1,
203+
],
204+
'error' => [
205+
'middleware' => 'Unauthorized',
206+
'priority' => -10000,
207+
'error' => true,
208+
],
209+
],
210+
];
211+
$this->injectServiceInContainer($this->container, 'config', $config);
212+
213+
$app = $this->factory->__invoke($this->container->reveal());
214+
215+
$request = new ServerRequest([], [], 'http://example.com/needs/authentication', 'GET');
216+
$response = new Response();
217+
218+
$response = $app($request, $response);
219+
$this->assertInstanceOf(ResponseInterface::class, $response);
220+
$this->assertEquals(401, $response->getStatusCode(), 'Unexpected response');
221+
$this->assertTrue($response->hasHeader('X-Always'));
222+
$this->assertEquals('true', $response->getHeaderLine('X-Always'));
223+
$this->assertTrue($response->hasHeader('X-Route-Result'));
224+
$this->assertEquals('needs-auth', $response->getHeaderLine('X-Route-Result'));
225+
$this->assertEquals('Error middleware called', (string) $response->getBody());
226+
}
227+
}

0 commit comments

Comments
 (0)