Version 1
[yaffs-website] / web / core / tests / Drupal / Tests / Core / Path / AliasManagerTest.php
1 <?php
2
3 namespace Drupal\Tests\Core\Path;
4
5 use Drupal\Core\Language\Language;
6 use Drupal\Core\Language\LanguageInterface;
7 use Drupal\Core\Path\AliasManager;
8 use Drupal\Tests\UnitTestCase;
9
10 /**
11  * @coversDefaultClass \Drupal\Core\Path\AliasManager
12  * @group Path
13  */
14 class AliasManagerTest extends UnitTestCase {
15
16   /**
17    * The alias manager.
18    *
19    * @var \Drupal\Core\Path\AliasManager
20    */
21   protected $aliasManager;
22
23   /**
24    * Alias storage.
25    *
26    * @var \Drupal\Core\Path\AliasStorageInterface|\PHPUnit_Framework_MockObject_MockObject
27    */
28   protected $aliasStorage;
29
30   /**
31    * Alias whitelist.
32    *
33    * @var \Drupal\Core\Path\AliasWhitelistInterface|\PHPUnit_Framework_MockObject_MockObject
34    */
35   protected $aliasWhitelist;
36
37   /**
38    * Language manager.
39    *
40    * @var \Drupal\Core\Language\LanguageManagerInterface|\PHPUnit_Framework_MockObject_MockObject
41    */
42   protected $languageManager;
43
44   /**
45    * Cache backend.
46    *
47    * @var \Drupal\Core\Cache\CacheBackendInterface|\PHPUnit_Framework_MockObject_MockObject
48    */
49   protected $cache;
50
51   /**
52    * The internal cache key used by the alias manager.
53    *
54    * @var string
55    */
56   protected $cacheKey = 'preload-paths:key';
57
58   /**
59    * The cache key passed to the alias manager.
60    *
61    * @var string
62    */
63   protected $path = 'key';
64
65   /**
66    * {@inheritdoc}
67    */
68   protected function setUp() {
69     parent::setUp();
70
71     $this->aliasStorage = $this->getMock('Drupal\Core\Path\AliasStorageInterface');
72     $this->aliasWhitelist = $this->getMock('Drupal\Core\Path\AliasWhitelistInterface');
73     $this->languageManager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface');
74     $this->cache = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
75
76     $this->aliasManager = new AliasManager($this->aliasStorage, $this->aliasWhitelist, $this->languageManager, $this->cache);
77
78   }
79
80   /**
81    * Tests the getPathByAlias method for an alias that have no matching path.
82    *
83    * @covers ::getPathByAlias
84    */
85   public function testGetPathByAliasNoMatch() {
86     $alias = '/' . $this->randomMachineName();
87
88     $language = new Language(['id' => 'en']);
89
90     $this->languageManager->expects($this->any())
91       ->method('getCurrentLanguage')
92       ->with(LanguageInterface::TYPE_URL)
93       ->will($this->returnValue($language));
94
95     $this->aliasStorage->expects($this->once())
96       ->method('lookupPathSource')
97       ->with($alias, $language->getId())
98       ->will($this->returnValue(NULL));
99
100     $this->assertEquals($alias, $this->aliasManager->getPathByAlias($alias));
101     // Call it twice to test the static cache.
102     $this->assertEquals($alias, $this->aliasManager->getPathByAlias($alias));
103   }
104
105   /**
106    * Tests the getPathByAlias method for an alias that have a matching path.
107    *
108    * @covers ::getPathByAlias
109    */
110   public function testGetPathByAliasNatch() {
111     $alias = $this->randomMachineName();
112     $path = $this->randomMachineName();
113
114     $language = $this->setUpCurrentLanguage();
115
116     $this->aliasStorage->expects($this->once())
117       ->method('lookupPathSource')
118       ->with($alias, $language->getId())
119       ->will($this->returnValue($path));
120
121     $this->assertEquals($path, $this->aliasManager->getPathByAlias($alias));
122     // Call it twice to test the static cache.
123     $this->assertEquals($path, $this->aliasManager->getPathByAlias($alias));
124   }
125
126   /**
127    * Tests the getPathByAlias method when a langcode is passed explicitly.
128    *
129    * @covers ::getPathByAlias
130    */
131   public function testGetPathByAliasLangcode() {
132     $alias = $this->randomMachineName();
133     $path = $this->randomMachineName();
134
135     $this->languageManager->expects($this->never())
136       ->method('getCurrentLanguage');
137
138     $this->aliasStorage->expects($this->once())
139       ->method('lookupPathSource')
140       ->with($alias, 'de')
141       ->will($this->returnValue($path));
142
143     $this->assertEquals($path, $this->aliasManager->getPathByAlias($alias, 'de'));
144     // Call it twice to test the static cache.
145     $this->assertEquals($path, $this->aliasManager->getPathByAlias($alias, 'de'));
146   }
147
148
149   /**
150    * Tests the getAliasByPath method for a path that is not in the whitelist.
151    *
152    * @covers ::getAliasByPath
153    */
154   public function testGetAliasByPathWhitelist() {
155     $path_part1 = $this->randomMachineName();
156     $path_part2 = $this->randomMachineName();
157     $path = '/' . $path_part1 . '/' . $path_part2;
158
159     $this->setUpCurrentLanguage();
160
161     $this->aliasWhitelist->expects($this->any())
162       ->method('get')
163       ->with($path_part1)
164       ->will($this->returnValue(FALSE));
165
166     // The whitelist returns FALSE for that path part, so the storage should
167     // never be called.
168     $this->aliasStorage->expects($this->never())
169       ->method('lookupPathAlias');
170
171     $this->assertEquals($path, $this->aliasManager->getAliasByPath($path));
172   }
173
174   /**
175    * Tests the getAliasByPath method for a path that has no matching alias.
176    *
177    * @covers ::getAliasByPath
178    */
179   public function testGetAliasByPathNoMatch() {
180     $path_part1 = $this->randomMachineName();
181     $path_part2 = $this->randomMachineName();
182     $path = '/' . $path_part1 . '/' . $path_part2;
183
184     $language = $this->setUpCurrentLanguage();
185
186     $this->aliasManager->setCacheKey($this->path);
187
188     $this->aliasWhitelist->expects($this->any())
189       ->method('get')
190       ->with($path_part1)
191       ->will($this->returnValue(TRUE));
192
193     $this->aliasStorage->expects($this->once())
194       ->method('lookupPathAlias')
195       ->with($path, $language->getId())
196       ->will($this->returnValue(NULL));
197
198     $this->assertEquals($path, $this->aliasManager->getAliasByPath($path));
199     // Call it twice to test the static cache.
200     $this->assertEquals($path, $this->aliasManager->getAliasByPath($path));
201
202     // This needs to write out the cache.
203     $this->cache->expects($this->once())
204       ->method('set')
205       ->with($this->cacheKey, [$language->getId() => [$path]], (int) $_SERVER['REQUEST_TIME'] + (60 * 60 * 24));
206
207     $this->aliasManager->writeCache();
208   }
209
210   /**
211    * Tests the getAliasByPath method for a path that has a matching alias.
212    *
213    * @covers ::getAliasByPath
214    * @covers ::writeCache
215    */
216   public function testGetAliasByPathMatch() {
217     $path_part1 = $this->randomMachineName();
218     $path_part2 = $this->randomMachineName();
219     $path = '/' . $path_part1 . '/' . $path_part2;
220     $alias = $this->randomMachineName();
221
222     $language = $this->setUpCurrentLanguage();
223
224     $this->aliasManager->setCacheKey($this->path);
225
226     $this->aliasWhitelist->expects($this->any())
227       ->method('get')
228       ->with($path_part1)
229       ->will($this->returnValue(TRUE));
230
231     $this->aliasStorage->expects($this->once())
232       ->method('lookupPathAlias')
233       ->with($path, $language->getId())
234       ->will($this->returnValue($alias));
235
236     $this->assertEquals($alias, $this->aliasManager->getAliasByPath($path));
237     // Call it twice to test the static cache.
238     $this->assertEquals($alias, $this->aliasManager->getAliasByPath($path));
239
240     // This needs to write out the cache.
241     $this->cache->expects($this->once())
242       ->method('set')
243       ->with($this->cacheKey, [$language->getId() => [$path]], (int) $_SERVER['REQUEST_TIME'] + (60 * 60 * 24));
244
245     $this->aliasManager->writeCache();
246   }
247
248   /**
249    * Tests the getAliasByPath method for a path that is preloaded.
250    *
251    * @covers ::getAliasByPath
252    * @covers ::writeCache
253    */
254   public function testGetAliasByPathCachedMatch() {
255     $path_part1 = $this->randomMachineName();
256     $path_part2 = $this->randomMachineName();
257     $path = '/' . $path_part1 . '/' . $path_part2;
258     $alias = $this->randomMachineName();
259
260     $language = $this->setUpCurrentLanguage();
261
262     $cached_paths = [$language->getId() => [$path]];
263     $this->cache->expects($this->once())
264       ->method('get')
265       ->with($this->cacheKey)
266       ->will($this->returnValue((object) ['data' => $cached_paths]));
267
268     // Simulate a request so that the preloaded paths are fetched.
269     $this->aliasManager->setCacheKey($this->path);
270
271     $this->aliasWhitelist->expects($this->any())
272       ->method('get')
273       ->with($path_part1)
274       ->will($this->returnValue(TRUE));
275
276     $this->aliasStorage->expects($this->once())
277       ->method('preloadPathAlias')
278       ->with($cached_paths[$language->getId()], $language->getId())
279       ->will($this->returnValue([$path => $alias]));
280
281     // LookupPathAlias should not be called.
282     $this->aliasStorage->expects($this->never())
283       ->method('lookupPathAlias');
284
285     $this->assertEquals($alias, $this->aliasManager->getAliasByPath($path));
286     // Call it twice to test the static cache.
287     $this->assertEquals($alias, $this->aliasManager->getAliasByPath($path));
288
289     // This must not write to the cache again.
290     $this->cache->expects($this->never())
291       ->method('set');
292     $this->aliasManager->writeCache();
293   }
294
295   /**
296    * Tests the getAliasByPath cache when a different language is requested.
297    *
298    * @covers ::getAliasByPath
299    * @covers ::writeCache
300    */
301   public function testGetAliasByPathCachedMissLanguage() {
302     $path_part1 = $this->randomMachineName();
303     $path_part2 = $this->randomMachineName();
304     $path = '/' . $path_part1 . '/' . $path_part2;
305     $alias = $this->randomMachineName();
306
307     $language = $this->setUpCurrentLanguage();
308     $cached_language = new Language(['id' => 'de']);
309
310     $cached_paths = [$cached_language->getId() => [$path]];
311     $this->cache->expects($this->once())
312       ->method('get')
313       ->with($this->cacheKey)
314       ->will($this->returnValue((object) ['data' => $cached_paths]));
315
316     // Simulate a request so that the preloaded paths are fetched.
317     $this->aliasManager->setCacheKey($this->path);
318
319     $this->aliasWhitelist->expects($this->any())
320       ->method('get')
321       ->with($path_part1)
322       ->will($this->returnValue(TRUE));
323
324     // The requested language is different than the cached, so this will
325     // need to load.
326     $this->aliasStorage->expects($this->never())
327       ->method('preloadPathAlias');
328     $this->aliasStorage->expects($this->once())
329       ->method('lookupPathAlias')
330       ->with($path, $language->getId())
331       ->will($this->returnValue($alias));
332
333     $this->assertEquals($alias, $this->aliasManager->getAliasByPath($path));
334     // Call it twice to test the static cache.
335     $this->assertEquals($alias, $this->aliasManager->getAliasByPath($path));
336
337     // There is already a cache entry, so this should not write out to the
338     // cache.
339     $this->cache->expects($this->never())
340       ->method('set');
341     $this->aliasManager->writeCache();
342   }
343
344   /**
345    * Tests the getAliasByPath cache with a preloaded path without alias.
346    *
347    * @covers ::getAliasByPath
348    * @covers ::writeCache
349    */
350   public function testGetAliasByPathCachedMissNoAlias() {
351     $path_part1 = $this->randomMachineName();
352     $path_part2 = $this->randomMachineName();
353     $path = '/' . $path_part1 . '/' . $path_part2;
354     $cached_path = $this->randomMachineName();
355     $cached_alias = $this->randomMachineName();
356
357     $language = $this->setUpCurrentLanguage();
358
359     $cached_paths = [$language->getId() => [$cached_path, $path]];
360     $this->cache->expects($this->once())
361       ->method('get')
362       ->with($this->cacheKey)
363       ->will($this->returnValue((object) ['data' => $cached_paths]));
364
365     // Simulate a request so that the preloaded paths are fetched.
366     $this->aliasManager->setCacheKey($this->path);
367
368     $this->aliasWhitelist->expects($this->any())
369       ->method('get')
370       ->with($path_part1)
371       ->will($this->returnValue(TRUE));
372
373     $this->aliasStorage->expects($this->once())
374       ->method('preloadPathAlias')
375       ->with($cached_paths[$language->getId()], $language->getId())
376       ->will($this->returnValue([$cached_path => $cached_alias]));
377
378     // LookupPathAlias() should not be called.
379     $this->aliasStorage->expects($this->never())
380       ->method('lookupPathAlias');
381
382     $this->assertEquals($path, $this->aliasManager->getAliasByPath($path));
383     // Call it twice to test the static cache.
384     $this->assertEquals($path, $this->aliasManager->getAliasByPath($path));
385
386     // This must not write to the cache again.
387     $this->cache->expects($this->never())
388       ->method('set');
389     $this->aliasManager->writeCache();
390   }
391
392   /**
393    * Tests the getAliasByPath cache with an unpreloaded path without alias.
394    *
395    * @covers ::getAliasByPath
396    * @covers ::writeCache
397    */
398   public function testGetAliasByPathUncachedMissNoAlias() {
399     $path_part1 = $this->randomMachineName();
400     $path_part2 = $this->randomMachineName();
401     $path = '/' . $path_part1 . '/' . $path_part2;
402     $cached_path = $this->randomMachineName();
403     $cached_alias = $this->randomMachineName();
404
405     $language = $this->setUpCurrentLanguage();
406
407     $cached_paths = [$language->getId() => [$cached_path]];
408     $this->cache->expects($this->once())
409       ->method('get')
410       ->with($this->cacheKey)
411       ->will($this->returnValue((object) ['data' => $cached_paths]));
412
413     // Simulate a request so that the preloaded paths are fetched.
414     $this->aliasManager->setCacheKey($this->path);
415
416     $this->aliasWhitelist->expects($this->any())
417       ->method('get')
418       ->with($path_part1)
419       ->will($this->returnValue(TRUE));
420
421     $this->aliasStorage->expects($this->once())
422       ->method('preloadPathAlias')
423       ->with($cached_paths[$language->getId()], $language->getId())
424       ->will($this->returnValue([$cached_path => $cached_alias]));
425
426     $this->aliasStorage->expects($this->once())
427       ->method('lookupPathAlias')
428       ->with($path, $language->getId())
429       ->will($this->returnValue(NULL));
430
431     $this->assertEquals($path, $this->aliasManager->getAliasByPath($path));
432     // Call it twice to test the static cache.
433     $this->assertEquals($path, $this->aliasManager->getAliasByPath($path));
434
435     // There is already a cache entry, so this should not write out to the
436     // cache.
437     $this->cache->expects($this->never())
438       ->method('set');
439     $this->aliasManager->writeCache();
440   }
441
442   /**
443    * @covers ::cacheClear
444    */
445   public function testCacheClear() {
446     $path = '/path';
447     $alias = '/alias';
448     $language = $this->setUpCurrentLanguage();
449     $this->aliasStorage->expects($this->exactly(2))
450       ->method('lookupPathAlias')
451       ->with($path, $language->getId())
452       ->willReturn($alias);
453     $this->aliasWhitelist->expects($this->any())
454       ->method('get')
455       ->willReturn(TRUE);
456
457     // Populate the lookup map.
458     $this->assertEquals($alias, $this->aliasManager->getAliasByPath($path, $language->getId()));
459
460     // Check that the cache is populated.
461     $original_storage = clone $this->aliasStorage;
462     $this->aliasStorage->expects($this->never())
463       ->method('lookupPathSource');
464     $this->assertEquals($path, $this->aliasManager->getPathByAlias($alias, $language->getId()));
465
466     // Clear specific source.
467     $this->cache->expects($this->exactly(2))
468       ->method('delete');
469     $this->aliasManager->cacheClear($path);
470
471     // Ensure cache has been cleared (this will be the 2nd call to
472     // `lookupPathAlias` if cache is cleared).
473     $this->assertEquals($alias, $this->aliasManager->getAliasByPath($path, $language->getId()));
474
475     // Clear non-existent source.
476     $this->aliasManager->cacheClear('non-existent');
477   }
478
479   /**
480    * Tests the getAliasByPath cache with an unpreloaded path with alias.
481    *
482    * @covers ::getAliasByPath
483    * @covers ::writeCache
484    */
485   public function testGetAliasByPathUncachedMissWithAlias() {
486     $path_part1 = $this->randomMachineName();
487     $path_part2 = $this->randomMachineName();
488     $path = '/' . $path_part1 . '/' . $path_part2;
489     $cached_path = $this->randomMachineName();
490     $cached_no_alias_path = $this->randomMachineName();
491     $cached_alias = $this->randomMachineName();
492     $new_alias = $this->randomMachineName();
493
494     $language = $this->setUpCurrentLanguage();
495
496     $cached_paths = [$language->getId() => [$cached_path, $cached_no_alias_path]];
497     $this->cache->expects($this->once())
498       ->method('get')
499       ->with($this->cacheKey)
500       ->will($this->returnValue((object) ['data' => $cached_paths]));
501
502     // Simulate a request so that the preloaded paths are fetched.
503     $this->aliasManager->setCacheKey($this->path);
504
505     $this->aliasWhitelist->expects($this->any())
506       ->method('get')
507       ->with($path_part1)
508       ->will($this->returnValue(TRUE));
509
510     $this->aliasStorage->expects($this->once())
511       ->method('preloadPathAlias')
512       ->with($cached_paths[$language->getId()], $language->getId())
513       ->will($this->returnValue([$cached_path => $cached_alias]));
514
515     $this->aliasStorage->expects($this->once())
516       ->method('lookupPathAlias')
517       ->with($path, $language->getId())
518       ->will($this->returnValue($new_alias));
519
520     $this->assertEquals($new_alias, $this->aliasManager->getAliasByPath($path));
521     // Call it twice to test the static cache.
522     $this->assertEquals($new_alias, $this->aliasManager->getAliasByPath($path));
523
524     // There is already a cache entry, so this should not write out to the
525     // cache.
526     $this->cache->expects($this->never())
527       ->method('set');
528     $this->aliasManager->writeCache();
529   }
530
531   /**
532    * Sets up the current language.
533    *
534    * @return \Drupal\Core\Language\LanguageInterface
535    *   The current language object.
536    */
537   protected function setUpCurrentLanguage() {
538     $language = new Language(['id' => 'en']);
539
540     $this->languageManager->expects($this->any())
541       ->method('getCurrentLanguage')
542       ->with(LanguageInterface::TYPE_URL)
543       ->will($this->returnValue($language));
544
545     return $language;
546   }
547
548 }