3 namespace Drupal\Tests\redirect\Unit;
5 use Drupal\Core\Language\Language;
6 use Drupal\redirect\EventSubscriber\RedirectRequestSubscriber;
7 use Drupal\Tests\UnitTestCase;
8 use PHPUnit_Framework_MockObject_MockObject;
9 use Symfony\Component\HttpFoundation\RedirectResponse;
10 use Symfony\Component\HttpFoundation\Request;
11 use Symfony\Component\HttpFoundation\Response;
12 use Symfony\Component\HttpKernel\Event\GetResponseEvent;
13 use Symfony\Component\HttpKernel\Event\PostResponseEvent;
16 * Tests the redirect logic.
20 * @coversDefaultClass Drupal\redirect\EventSubscriber\RedirectRequestSubscriber
22 class RedirectRequestSubscriberTest extends UnitTestCase {
25 * @covers ::onKernelRequestCheckRedirect
26 * @dataProvider getRedirectData
28 public function testRedirectLogicWithQueryRetaining($request_uri, $request_query, $redirect_uri, $redirect_query) {
30 // The expected final query. This query must contain values defined
31 // by the redirect entity and values from the accessed url.
32 $final_query = $redirect_query + $request_query;
34 $url = $this->getMockBuilder('Drupal\Core\Url')
35 ->disableOriginalConstructor()
38 $url->expects($this->once())
39 ->method('setAbsolute')
43 $url->expects($this->once())
46 ->willReturn($redirect_query);
48 $url->expects($this->once())
50 ->with('query', $final_query);
52 $url->expects($this->once())
54 ->willReturn($redirect_uri);
56 $redirect = $this->getRedirectStub($url);
57 $event = $this->callOnKernelRequestCheckRedirect($redirect, $request_uri, $request_query, TRUE);
59 $this->assertTrue($event->getResponse() instanceof RedirectResponse);
60 $response = $event->getResponse();
61 $this->assertEquals('/test-path', $response->getTargetUrl());
62 $this->assertEquals(301, $response->getStatusCode());
63 $this->assertEquals(1, $response->headers->get('X-Redirect-ID'));
67 * @covers ::onKernelRequestCheckRedirect
68 * @dataProvider getRedirectData
70 public function testRedirectLogicWithoutQueryRetaining($request_uri, $request_query, $redirect_uri) {
72 $url = $this->getMockBuilder('Drupal\Core\Url')
73 ->disableOriginalConstructor()
76 $url->expects($this->once())
77 ->method('setAbsolute')
81 // No query retaining, so getOption should not be called.
82 $url->expects($this->never())
83 ->method('getOption');
84 $url->expects($this->never())
85 ->method('setOption');
87 $url->expects($this->once())
89 ->willReturn($redirect_uri);
91 $redirect = $this->getRedirectStub($url);
92 $event = $this->callOnKernelRequestCheckRedirect($redirect, $request_uri, $request_query, FALSE);
94 $this->assertTrue($event->getResponse() instanceof RedirectResponse);
95 $response = $event->getResponse();
96 $this->assertEquals($redirect_uri, $response->getTargetUrl());
97 $this->assertEquals(301, $response->getStatusCode());
98 $this->assertEquals(1, $response->headers->get('X-Redirect-ID'));
102 * Data provider for both tests.
104 public function getRedirectData() {
106 ['non-existing', ['key' => 'val'], '/test-path', ['dummy' => 'value']],
107 ['non-existing/', ['key' => 'val'], '/test-path', ['dummy' => 'value']],
108 ['system/files/file.txt', [], '/test-path', []],
113 * Instantiates the subscriber and runs onKernelRequestCheckRedirect()
116 * The redirect entity.
117 * @param $request_uri
118 * The URI of the request.
119 * @param array $request_query
120 * The query that is supposed to come via request.
121 * @param bool $retain_query
122 * Flag if to retain the query through the redirect.
124 * @return \Symfony\Component\HttpKernel\Event\GetResponseEvent
125 * THe response event.
127 protected function callOnKernelRequestCheckRedirect($redirect, $request_uri, $request_query, $retain_query) {
129 $event = $this->getGetResponseEventStub($request_uri, http_build_query($request_query));
130 $request = $event->getRequest();
132 $checker = $this->getMockBuilder('Drupal\redirect\RedirectChecker')
133 ->disableOriginalConstructor()
135 $checker->expects($this->any())
136 ->method('canRedirect')
137 ->will($this->returnValue(TRUE));
139 $context = $this->getMock('Symfony\Component\Routing\RequestContext');
141 $inbound_path_processor = $this->getMockBuilder('Drupal\Core\PathProcessor\InboundPathProcessorInterface')
142 ->disableOriginalConstructor()
144 $inbound_path_processor->expects($this->any())
145 ->method('processInbound')
146 ->with($request->getPathInfo(), $request)
147 ->willReturnCallback(function ($path, Request $request) {
148 if (strpos($path, '/system/files/') === 0 && !$request->query->has('file')) {
149 // Private files paths are split by the inbound path processor and the
150 // relative file path is moved to the 'file' query string parameter.
151 // This is because the route system does not allow an arbitrary amount
153 // @see \Drupal\system\PathProcessor\PathProcessorFiles::processInbound()
154 $path = '/system/files';
159 $alias_manager = $this->getMockBuilder('Drupal\Core\Path\AliasManager')
160 ->disableOriginalConstructor()
162 $module_handler = $this->getMockBuilder('Drupal\Core\Extension\ModuleHandlerInterface')
164 $entity_manager = $this->getMockBuilder('Drupal\Core\Entity\EntityManagerInterface')
167 $subscriber = new RedirectRequestSubscriber(
168 $this->getRedirectRepositoryStub('findMatchingRedirect', $redirect),
169 $this->getLanguageManagerStub(),
170 $this->getConfigFactoryStub(array('redirect.settings' => array('passthrough_querystring' => $retain_query))),
176 $inbound_path_processor
179 // Run the main redirect method.
180 $subscriber->onKernelRequestCheckRedirect($event);
185 * Gets the redirect repository mock object.
188 * Method to mock - either load() or findMatchingRedirect().
190 * The redirect object to be returned.
192 * @return PHPUnit_Framework_MockObject_MockObject
193 * The redirect repository.
195 protected function getRedirectRepositoryStub($method, $redirect) {
196 $repository = $this->getMockBuilder('Drupal\redirect\RedirectRepository')
197 ->disableOriginalConstructor()
200 if ($method === 'findMatchingRedirect') {
201 $repository->expects($this->any())
203 ->willReturnCallback(function ($source_path) use ($redirect) {
204 // No redirect with source path 'system/files' exists. The stored
205 // redirect has 'system/files/file.txt' as source path.
206 return $source_path === 'system/files' ? NULL : $redirect;
210 $repository->expects($this->any())
212 ->will($this->returnValue($redirect));
219 * Gets the redirect mock object.
222 * Url to be returned from getRedirectUrl
223 * @param int $status_code
224 * The redirect status code.
226 * @return PHPUnit_Framework_MockObject_MockObject
227 * The mocked redirect object.
229 protected function getRedirectStub($url, $status_code = 301) {
230 $redirect = $this->getMockBuilder('Drupal\redirect\Entity\Redirect')
231 ->disableOriginalConstructor()
233 $redirect->expects($this->once())
234 ->method('getRedirectUrl')
235 ->will($this->returnValue($url));
236 $redirect->expects($this->any())
237 ->method('getStatusCode')
238 ->will($this->returnValue($status_code));
239 $redirect->expects($this->any())
242 $redirect->expects($this->once())
243 ->method('getCacheTags')
244 ->willReturn(['redirect:1']);
250 * Gets post response event.
252 * @param array $headers
253 * Headers to be set into the response.
255 * @return \Symfony\Component\HttpKernel\Event\PostResponseEvent
256 * The post response event object.
258 protected function getPostResponseEvent($headers = array()) {
259 $http_kernel = $this->getMockBuilder('\Symfony\Component\HttpKernel\HttpKernelInterface')
261 $request = $this->getMockBuilder('Symfony\Component\HttpFoundation\Request')
262 ->disableOriginalConstructor()
265 $response = new Response('', 301, $headers);
267 return new PostResponseEvent($http_kernel, $request, $response);
271 * Gets response event object.
274 * @param $query_string
276 * @return GetResponseEvent
278 protected function getGetResponseEventStub($path_info, $query_string) {
279 $request = Request::create($path_info . '?' . $query_string, 'GET', [], [], [], ['SCRIPT_NAME' => 'index.php']);
281 $http_kernel = $this->getMockBuilder('\Symfony\Component\HttpKernel\HttpKernelInterface')
283 return new GetResponseEvent($http_kernel, $request, 'test');
287 * Gets the language manager mock object.
289 * @return \Drupal\language\ConfigurableLanguageManagerInterface|PHPUnit_Framework_MockObject_MockObject
291 protected function getLanguageManagerStub() {
292 $language_manager = $this->getMockBuilder('Drupal\language\ConfigurableLanguageManagerInterface')
294 $language_manager->expects($this->any())
295 ->method('getCurrentLanguage')
296 ->will($this->returnValue(new Language(array('id' => 'en'))));
298 return $language_manager;