AliasManagerTest.php

Same filename and directory in other branches
  1. 9 core/modules/path_alias/tests/src/Unit/AliasManagerTest.php
  2. 8.9.x core/modules/path_alias/tests/src/Unit/AliasManagerTest.php
  3. 10 core/modules/path_alias/tests/src/Unit/AliasManagerTest.php

Namespace

Drupal\Tests\path_alias\Unit

File

core/modules/path_alias/tests/src/Unit/AliasManagerTest.php

View source
<?php

declare (strict_types=1);
namespace Drupal\Tests\path_alias\Unit;

use Drupal\Component\Datetime\Time;
use Drupal\Core\Language\Language;
use Drupal\Core\Language\LanguageInterface;
use Drupal\path_alias\AliasRepositoryInterface;
use Drupal\path_alias\AliasManager;
use Drupal\Tests\UnitTestCase;

/**
 * @coversDefaultClass \Drupal\path_alias\AliasManager
 * @group path_alias
 */
class AliasManagerTest extends UnitTestCase {
    
    /**
     * The alias manager.
     *
     * @var \Drupal\path_alias\AliasManager
     */
    protected $aliasManager;
    
    /**
     * Alias repository.
     *
     * @var \Drupal\path_alias\AliasRepositoryInterface|\PHPUnit\Framework\MockObject\MockObject
     */
    protected $aliasRepository;
    
    /**
     * Alias whitelist.
     *
     * @var \Drupal\path_alias\AliasWhitelistInterface|\PHPUnit\Framework\MockObject\MockObject
     */
    protected $aliasWhitelist;
    
    /**
     * Language manager.
     *
     * @var \Drupal\Core\Language\LanguageManagerInterface|\PHPUnit\Framework\MockObject\MockObject
     */
    protected $languageManager;
    
    /**
     * Cache backend.
     *
     * @var \Drupal\Core\Cache\CacheBackendInterface|\PHPUnit\Framework\MockObject\MockObject
     */
    protected $cache;
    
    /**
     * The internal cache key used by the alias manager.
     *
     * @var string
     */
    protected $cacheKey = 'preload-paths:key';
    
    /**
     * The cache key passed to the alias manager.
     *
     * @var string
     */
    protected $path = 'key';
    
    /**
     * {@inheritdoc}
     */
    protected function setUp() : void {
        parent::setUp();
        $this->aliasRepository = $this->createMock(AliasRepositoryInterface::class);
        $this->aliasWhitelist = $this->createMock('Drupal\\path_alias\\AliasWhitelistInterface');
        $this->languageManager = $this->createMock('Drupal\\Core\\Language\\LanguageManagerInterface');
        $this->cache = $this->createMock('Drupal\\Core\\Cache\\CacheBackendInterface');
        $this->aliasManager = new AliasManager($this->aliasRepository, $this->aliasWhitelist, $this->languageManager, $this->cache, new Time());
    }
    
    /**
     * Tests the getPathByAlias method for an alias that have no matching path.
     *
     * @covers ::getPathByAlias
     */
    public function testGetPathByAliasNoMatch() : void {
        $alias = '/' . $this->randomMachineName();
        $language = new Language([
            'id' => 'en',
        ]);
        $this->languageManager
            ->expects($this->any())
            ->method('getCurrentLanguage')
            ->with(LanguageInterface::TYPE_URL)
            ->willReturn($language);
        $this->aliasRepository
            ->expects($this->once())
            ->method('lookupByAlias')
            ->with($alias, $language->getId())
            ->willReturn(NULL);
        $this->assertEquals($alias, $this->aliasManager
            ->getPathByAlias($alias));
        // Call it twice to test the static cache.
        $this->assertEquals($alias, $this->aliasManager
            ->getPathByAlias($alias));
    }
    
    /**
     * Tests the getPathByAlias method for an alias that have a matching path.
     *
     * @covers ::getPathByAlias
     */
    public function testGetPathByAliasMatch() : void {
        $alias = $this->randomMachineName();
        $path = $this->randomMachineName();
        $language = $this->setUpCurrentLanguage();
        $this->aliasRepository
            ->expects($this->once())
            ->method('lookupByAlias')
            ->with($alias, $language->getId())
            ->willReturn([
            'path' => $path,
        ]);
        $this->assertEquals($path, $this->aliasManager
            ->getPathByAlias($alias));
        // Call it twice to test the static cache.
        $this->assertEquals($path, $this->aliasManager
            ->getPathByAlias($alias));
    }
    
    /**
     * Tests the getPathByAlias method when a langcode is passed explicitly.
     *
     * @covers ::getPathByAlias
     */
    public function testGetPathByAliasLangcode() : void {
        $alias = $this->randomMachineName();
        $path = $this->randomMachineName();
        $this->languageManager
            ->expects($this->never())
            ->method('getCurrentLanguage');
        $this->aliasRepository
            ->expects($this->once())
            ->method('lookupByAlias')
            ->with($alias, 'de')
            ->willReturn([
            'path' => $path,
        ]);
        $this->assertEquals($path, $this->aliasManager
            ->getPathByAlias($alias, 'de'));
        // Call it twice to test the static cache.
        $this->assertEquals($path, $this->aliasManager
            ->getPathByAlias($alias, 'de'));
    }
    
    /**
     * Tests the getAliasByPath method for a path that is not in the whitelist.
     *
     * @covers ::getAliasByPath
     */
    public function testGetAliasByPathWhitelist() : void {
        $path_part1 = $this->randomMachineName();
        $path_part2 = $this->randomMachineName();
        $path = '/' . $path_part1 . '/' . $path_part2;
        $this->setUpCurrentLanguage();
        $this->aliasWhitelist
            ->expects($this->any())
            ->method('get')
            ->with($path_part1)
            ->willReturn(FALSE);
        // The whitelist returns FALSE for that path part, so the storage should
        // never be called.
        $this->aliasRepository
            ->expects($this->never())
            ->method('lookupBySystemPath');
        $this->assertEquals($path, $this->aliasManager
            ->getAliasByPath($path));
    }
    
    /**
     * Tests the getAliasByPath method for a path that has no matching alias.
     *
     * @covers ::getAliasByPath
     */
    public function testGetAliasByPathNoMatch() : void {
        $path_part1 = $this->randomMachineName();
        $path_part2 = $this->randomMachineName();
        $path = '/' . $path_part1 . '/' . $path_part2;
        $language = $this->setUpCurrentLanguage();
        $this->aliasManager
            ->setCacheKey($this->path);
        $this->aliasWhitelist
            ->expects($this->any())
            ->method('get')
            ->with($path_part1)
            ->willReturn(TRUE);
        $this->aliasRepository
            ->expects($this->once())
            ->method('lookupBySystemPath')
            ->with($path, $language->getId())
            ->willReturn(NULL);
        $this->assertEquals($path, $this->aliasManager
            ->getAliasByPath($path));
        // Call it twice to test the static cache.
        $this->assertEquals($path, $this->aliasManager
            ->getAliasByPath($path));
        // This needs to write out the cache.
        $this->cache
            ->expects($this->once())
            ->method('set')
            ->with($this->cacheKey, [
            $language->getId() => [
                $path,
            ],
        ], (int) $_SERVER['REQUEST_TIME'] + 60 * 60 * 24);
        $this->aliasManager
            ->writeCache();
    }
    
    /**
     * Tests the getAliasByPath method exception.
     *
     * @covers ::getAliasByPath
     */
    public function testGetAliasByPathException() : void {
        $this->expectException(\InvalidArgumentException::class);
        $this->aliasManager
            ->getAliasByPath('no-leading-slash-here');
    }
    
    /**
     * Tests the getAliasByPath method for a path that has a matching alias.
     *
     * @covers ::getAliasByPath
     * @covers ::writeCache
     */
    public function testGetAliasByPathMatch() : void {
        $path_part1 = $this->randomMachineName();
        $path_part2 = $this->randomMachineName();
        $path = '/' . $path_part1 . '/' . $path_part2;
        $alias = $this->randomMachineName();
        $language = $this->setUpCurrentLanguage();
        $this->aliasManager
            ->setCacheKey($this->path);
        $this->aliasWhitelist
            ->expects($this->any())
            ->method('get')
            ->with($path_part1)
            ->willReturn(TRUE);
        $this->aliasRepository
            ->expects($this->once())
            ->method('lookupBySystemPath')
            ->with($path, $language->getId())
            ->willReturn([
            'alias' => $alias,
        ]);
        $this->assertEquals($alias, $this->aliasManager
            ->getAliasByPath($path));
        // Call it twice to test the static cache.
        $this->assertEquals($alias, $this->aliasManager
            ->getAliasByPath($path));
        // This needs to write out the cache.
        $this->cache
            ->expects($this->once())
            ->method('set')
            ->with($this->cacheKey, [
            $language->getId() => [
                $path,
            ],
        ], (int) $_SERVER['REQUEST_TIME'] + 60 * 60 * 24);
        $this->aliasManager
            ->writeCache();
    }
    
    /**
     * Tests the getAliasByPath method for a path that is preloaded.
     *
     * @covers ::getAliasByPath
     * @covers ::writeCache
     */
    public function testGetAliasByPathCachedMatch() : void {
        $path_part1 = $this->randomMachineName();
        $path_part2 = $this->randomMachineName();
        $path = '/' . $path_part1 . '/' . $path_part2;
        $alias = $this->randomMachineName();
        $language = $this->setUpCurrentLanguage();
        // Use a set of cached paths where the tested path is in any position, not
        // only in the first one.
        $cached_paths = [
            $language->getId() => [
                '/another/path',
                $path,
            ],
        ];
        $this->cache
            ->expects($this->once())
            ->method('get')
            ->with($this->cacheKey)
            ->willReturn((object) [
            'data' => $cached_paths,
        ]);
        // Simulate a request so that the preloaded paths are fetched.
        $this->aliasManager
            ->setCacheKey($this->path);
        $this->aliasWhitelist
            ->expects($this->any())
            ->method('get')
            ->with($path_part1)
            ->willReturn(TRUE);
        $this->aliasRepository
            ->expects($this->once())
            ->method('preloadPathAlias')
            ->with($cached_paths[$language->getId()], $language->getId())
            ->willReturn([
            $path => $alias,
        ]);
        // LookupPathAlias should not be called.
        $this->aliasRepository
            ->expects($this->never())
            ->method('lookupBySystemPath');
        $this->assertEquals($alias, $this->aliasManager
            ->getAliasByPath($path));
        // Call it twice to test the static cache.
        $this->assertEquals($alias, $this->aliasManager
            ->getAliasByPath($path));
        // This must not write to the cache again.
        $this->cache
            ->expects($this->never())
            ->method('set');
        $this->aliasManager
            ->writeCache();
    }
    
    /**
     * Tests the getAliasByPath cache when a different language is requested.
     *
     * @covers ::getAliasByPath
     * @covers ::writeCache
     */
    public function testGetAliasByPathCachedMissLanguage() : void {
        $path_part1 = $this->randomMachineName();
        $path_part2 = $this->randomMachineName();
        $path = '/' . $path_part1 . '/' . $path_part2;
        $alias = $this->randomMachineName();
        $language = $this->setUpCurrentLanguage();
        $cached_language = new Language([
            'id' => 'de',
        ]);
        $cached_paths = [
            $cached_language->getId() => [
                $path,
            ],
        ];
        $this->cache
            ->expects($this->once())
            ->method('get')
            ->with($this->cacheKey)
            ->willReturn((object) [
            'data' => $cached_paths,
        ]);
        // Simulate a request so that the preloaded paths are fetched.
        $this->aliasManager
            ->setCacheKey($this->path);
        $this->aliasWhitelist
            ->expects($this->any())
            ->method('get')
            ->with($path_part1)
            ->willReturn(TRUE);
        // The requested language is different than the cached, so this will
        // need to load.
        $this->aliasRepository
            ->expects($this->never())
            ->method('preloadPathAlias');
        $this->aliasRepository
            ->expects($this->once())
            ->method('lookupBySystemPath')
            ->with($path, $language->getId())
            ->willReturn([
            'alias' => $alias,
        ]);
        $this->assertEquals($alias, $this->aliasManager
            ->getAliasByPath($path));
        // Call it twice to test the static cache.
        $this->assertEquals($alias, $this->aliasManager
            ->getAliasByPath($path));
        // There is already a cache entry, so this should not write out to the
        // cache.
        $this->cache
            ->expects($this->never())
            ->method('set');
        $this->aliasManager
            ->writeCache();
    }
    
    /**
     * Tests the getAliasByPath cache with a preloaded path without alias.
     *
     * @covers ::getAliasByPath
     * @covers ::writeCache
     */
    public function testGetAliasByPathCachedMissNoAlias() : void {
        $path_part1 = $this->randomMachineName();
        $path_part2 = $this->randomMachineName();
        $path = '/' . $path_part1 . '/' . $path_part2;
        $cached_path = $this->randomMachineName();
        $cached_alias = $this->randomMachineName();
        $language = $this->setUpCurrentLanguage();
        $cached_paths = [
            $language->getId() => [
                $cached_path,
                $path,
            ],
        ];
        $this->cache
            ->expects($this->once())
            ->method('get')
            ->with($this->cacheKey)
            ->willReturn((object) [
            'data' => $cached_paths,
        ]);
        // Simulate a request so that the preloaded paths are fetched.
        $this->aliasManager
            ->setCacheKey($this->path);
        $this->aliasWhitelist
            ->expects($this->any())
            ->method('get')
            ->with($path_part1)
            ->willReturn(TRUE);
        $this->aliasRepository
            ->expects($this->once())
            ->method('preloadPathAlias')
            ->with($cached_paths[$language->getId()], $language->getId())
            ->willReturn([
            $cached_path => $cached_alias,
        ]);
        // LookupPathAlias() should not be called.
        $this->aliasRepository
            ->expects($this->never())
            ->method('lookupBySystemPath');
        $this->assertEquals($path, $this->aliasManager
            ->getAliasByPath($path));
        // Call it twice to test the static cache.
        $this->assertEquals($path, $this->aliasManager
            ->getAliasByPath($path));
        // This must not write to the cache again.
        $this->cache
            ->expects($this->never())
            ->method('set');
        $this->aliasManager
            ->writeCache();
    }
    
    /**
     * Tests the getAliasByPath cache with an un-preloaded path without alias.
     *
     * @covers ::getAliasByPath
     * @covers ::writeCache
     */
    public function testGetAliasByPathUncachedMissNoAlias() : void {
        $path_part1 = $this->randomMachineName();
        $path_part2 = $this->randomMachineName();
        $path = '/' . $path_part1 . '/' . $path_part2;
        $cached_path = $this->randomMachineName();
        $cached_alias = $this->randomMachineName();
        $language = $this->setUpCurrentLanguage();
        $cached_paths = [
            $language->getId() => [
                $cached_path,
            ],
        ];
        $this->cache
            ->expects($this->once())
            ->method('get')
            ->with($this->cacheKey)
            ->willReturn((object) [
            'data' => $cached_paths,
        ]);
        // Simulate a request so that the preloaded paths are fetched.
        $this->aliasManager
            ->setCacheKey($this->path);
        $this->aliasWhitelist
            ->expects($this->any())
            ->method('get')
            ->with($path_part1)
            ->willReturn(TRUE);
        $this->aliasRepository
            ->expects($this->once())
            ->method('preloadPathAlias')
            ->with($cached_paths[$language->getId()], $language->getId())
            ->willReturn([
            $cached_path => $cached_alias,
        ]);
        $this->aliasRepository
            ->expects($this->once())
            ->method('lookupBySystemPath')
            ->with($path, $language->getId())
            ->willReturn(NULL);
        $this->assertEquals($path, $this->aliasManager
            ->getAliasByPath($path));
        // Call it twice to test the static cache.
        $this->assertEquals($path, $this->aliasManager
            ->getAliasByPath($path));
        // There is already a cache entry, so this should not write out to the
        // cache.
        $this->cache
            ->expects($this->never())
            ->method('set');
        $this->aliasManager
            ->writeCache();
    }
    
    /**
     * @covers ::cacheClear
     */
    public function testCacheClear() : void {
        $path = '/path';
        $alias = '/alias';
        $language = $this->setUpCurrentLanguage();
        $this->aliasRepository
            ->expects($this->exactly(2))
            ->method('lookupBySystemPath')
            ->with($path, $language->getId())
            ->willReturn([
            'alias' => $alias,
        ]);
        $this->aliasWhitelist
            ->expects($this->any())
            ->method('get')
            ->willReturn(TRUE);
        // Populate the lookup map.
        $this->assertEquals($alias, $this->aliasManager
            ->getAliasByPath($path, $language->getId()));
        // Check that the cache is populated.
        $this->aliasRepository
            ->expects($this->never())
            ->method('lookupByAlias');
        $this->assertEquals($path, $this->aliasManager
            ->getPathByAlias($alias, $language->getId()));
        // Clear specific source.
        $this->aliasManager
            ->cacheClear($path);
        // Ensure cache has been cleared (this will be the 2nd call to
        // `lookupPathAlias` if cache is cleared).
        $this->assertEquals($alias, $this->aliasManager
            ->getAliasByPath($path, $language->getId()));
        // Clear non-existent source.
        $this->aliasManager
            ->cacheClear('non-existent');
    }
    
    /**
     * Tests the getAliasByPath cache with an un-preloaded path with alias.
     *
     * @covers ::getAliasByPath
     * @covers ::writeCache
     */
    public function testGetAliasByPathUncachedMissWithAlias() : void {
        $path_part1 = $this->randomMachineName();
        $path_part2 = $this->randomMachineName();
        $path = '/' . $path_part1 . '/' . $path_part2;
        $cached_path = $this->randomMachineName();
        $cached_no_alias_path = $this->randomMachineName();
        $cached_alias = $this->randomMachineName();
        $new_alias = $this->randomMachineName();
        $language = $this->setUpCurrentLanguage();
        $cached_paths = [
            $language->getId() => [
                $cached_path,
                $cached_no_alias_path,
            ],
        ];
        $this->cache
            ->expects($this->once())
            ->method('get')
            ->with($this->cacheKey)
            ->willReturn((object) [
            'data' => $cached_paths,
        ]);
        // Simulate a request so that the preloaded paths are fetched.
        $this->aliasManager
            ->setCacheKey($this->path);
        $this->aliasWhitelist
            ->expects($this->any())
            ->method('get')
            ->with($path_part1)
            ->willReturn(TRUE);
        $this->aliasRepository
            ->expects($this->once())
            ->method('preloadPathAlias')
            ->with($cached_paths[$language->getId()], $language->getId())
            ->willReturn([
            $cached_path => $cached_alias,
        ]);
        $this->aliasRepository
            ->expects($this->once())
            ->method('lookupBySystemPath')
            ->with($path, $language->getId())
            ->willReturn([
            'alias' => $new_alias,
        ]);
        $this->assertEquals($new_alias, $this->aliasManager
            ->getAliasByPath($path));
        // Call it twice to test the static cache.
        $this->assertEquals($new_alias, $this->aliasManager
            ->getAliasByPath($path));
        // There is already a cache entry, so this should not write out to the
        // cache.
        $this->cache
            ->expects($this->never())
            ->method('set');
        $this->aliasManager
            ->writeCache();
    }
    
    /**
     * Sets up the current language.
     *
     * @return \Drupal\Core\Language\LanguageInterface
     *   The current language object.
     */
    protected function setUpCurrentLanguage() {
        $language = new Language([
            'id' => 'en',
        ]);
        $this->languageManager
            ->expects($this->any())
            ->method('getCurrentLanguage')
            ->with(LanguageInterface::TYPE_URL)
            ->willReturn($language);
        return $language;
    }

}

Classes

Title Deprecated Summary
AliasManagerTest @coversDefaultClass \Drupal\path_alias\AliasManager @group path_alias

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.