diff --git a/Repository/Tests/BaseTest.php b/Repository/Tests/BaseTest.php index 67f674946..64f99b0a8 100644 --- a/Repository/Tests/BaseTest.php +++ b/Repository/Tests/BaseTest.php @@ -8,6 +8,7 @@ */ namespace eZ\Publish\API\Repository\Tests; +use Doctrine\DBAL\Connection; use eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException; use eZ\Publish\API\Repository\Tests\PHPUnitConstraint\ValidationErrorOccurs as PHPUnitConstraintValidationErrorOccurs; use eZ\Publish\API\Repository\Values\Content\Location; @@ -609,6 +610,60 @@ public function createUserWithPolicies($login, array $policiesData) } } + /** + * @return \Doctrine\DBAL\Connection + * + * @throws \ErrorException + */ + protected function getRawDatabaseConnection() + { + $connection = $this + ->getSetupFactory() + ->getServiceContainer()->get('ezpublish.api.storage_engine.legacy.connection'); + + if (!$connection instanceof Connection) { + throw new \RuntimeException( + sprintf('Expected %s got %s', Connection::class, get_class($connection)) + ); + } + + return $connection; + } + + /** + * Executes the given callback passing raw Database Connection (\Doctrine\DBAL\Connection). + * Returns the result returned by the given callback. + * + * **Note**: The method clears the entire persistence cache pool. + * + * @throws \Exception + * + * @param callable $callback + * + * @return mixed the return result of the given callback + */ + public function performRawDatabaseOperation(callable $callback) + { + $repository = $this->getRepository(false); + $repository->beginTransaction(); + try { + $callback( + $this->getRawDatabaseConnection() + ); + $repository->commit(); + } catch (Exception $e) { + $repository->rollback(); + throw $e; + } + + /** @var \Symfony\Component\Cache\Adapter\TagAwareAdapterInterface $cachePool */ + $cachePool = $this + ->getSetupFactory() + ->getServiceContainer()->get('ezpublish.cache_pool'); + + $cachePool->clear(); + } + /** * Traverse all errors for all fields in all Translations to find expected one. * diff --git a/Repository/Tests/URLAliasServiceTest.php b/Repository/Tests/URLAliasServiceTest.php index 1daec52f6..c340e2faa 100644 --- a/Repository/Tests/URLAliasServiceTest.php +++ b/Repository/Tests/URLAliasServiceTest.php @@ -22,7 +22,7 @@ /** * Test case for operations in the URLAliasService using in memory storage. * - * @see eZ\Publish\API\Repository\URLAliasService + * @see \eZ\Publish\API\Repository\URLAliasService * @group url-alias */ class URLAliasServiceTest extends BaseTest @@ -1215,6 +1215,103 @@ public function testRefreshSystemUrlAliasesForContentsWithUpdatedContentTypes() $this->assertUrlIsCurrent('/DE-Short-Name/DE-Nested-Short-Name', $nestedFolderLocation->id); } + /** + * Test restoring missing current URL which has existing history. + * + * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException + * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException + * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Exception + */ + public function testRefreshSystemUrlAliasesForMissingUrlWithHistory() + { + $repository = $this->getRepository(); + $urlAliasService = $repository->getURLAliasService(); + $locationService = $repository->getLocationService(); + + $folderNames = ['eng-GB' => 'My folder Name']; + $folder = $this->createFolder($folderNames, 2); + $folderLocation = $locationService->loadLocation($folder->contentInfo->mainLocationId); + $nestedFolder = $this->createFolder(['eng-GB' => 'Nested folder'], $folderLocation->id); + $nestedFolderLocation = $locationService->loadLocation($nestedFolder->contentInfo->mainLocationId); + + $folder = $this->updateContentField( + $folder->contentInfo, + 'name', + ['eng-GB' => 'Updated Name'] + ); + // create more historical entries + $this->updateContentField( + $folder->contentInfo, + 'name', + ['eng-GB' => 'Updated Again Name'] + ); + // create historical entry for nested folder + $this->updateContentField( + $nestedFolder->contentInfo, + 'name', + ['eng-GB' => 'Updated Nested folder'] + ); + + // perform sanity check + $this->assertUrlIsHistory('/My-folder-Name', $folderLocation->id); + $this->assertUrlIsHistory('/Updated-Name', $folderLocation->id); + $this->assertUrlIsHistory('/My-folder-Name/Nested-folder', $nestedFolderLocation->id); + $this->assertUrlIsHistory('/Updated-Name/Nested-folder', $nestedFolderLocation->id); + $this->assertUrlIsHistory('/Updated-Again-Name/Nested-folder', $nestedFolderLocation->id); + + $this->assertUrlIsCurrent('/Updated-Again-Name', $folderLocation->id); + $this->assertUrlIsCurrent('/Updated-Again-Name/Updated-Nested-folder', $nestedFolderLocation->id); + + self::assertNotEmpty($urlAliasService->listLocationAliases($folderLocation, false)); + + // corrupt database by removing original entry, keeping its history + $this->performRawDatabaseOperation( + function (Connection $connection) use ($folderLocation) { + $queryBuilder = $connection->createQueryBuilder(); + $expr = $queryBuilder->expr(); + $queryBuilder + ->delete('ezurlalias_ml') + ->where( + $expr->andX( + $expr->eq( + 'action', + $queryBuilder->createPositionalParameter( + "eznode:{$folderLocation->id}" + ) + ), + $expr->eq( + 'is_original', + $queryBuilder->createPositionalParameter(1) + ) + ) + ); + + return $queryBuilder->execute(); + } + ); + + // perform sanity check + self::assertEmpty($urlAliasService->listLocationAliases($folderLocation, false)); + + // Begin the actual test + $urlAliasService->refreshSystemUrlAliasesForLocation($folderLocation); + $urlAliasService->refreshSystemUrlAliasesForLocation($nestedFolderLocation); + + // make sure there is no corrupted data that could affect the test + $urlAliasService->deleteCorruptedUrlAliases(); + + // test if history was restored + $this->assertUrlIsHistory('/My-folder-Name', $folderLocation->id); + $this->assertUrlIsHistory('/Updated-Name', $folderLocation->id); + $this->assertUrlIsHistory('/My-folder-Name/Nested-folder', $nestedFolderLocation->id); + $this->assertUrlIsHistory('/Updated-Name/Nested-folder', $nestedFolderLocation->id); + $this->assertUrlIsHistory('/Updated-Again-Name/Nested-folder', $nestedFolderLocation->id); + + $this->assertUrlIsCurrent('/Updated-Again-Name', $folderLocation->id); + $this->assertUrlIsCurrent('/Updated-Again-Name/Updated-Nested-folder', $nestedFolderLocation->id); + } + /** * Lookup given URL and check if it is archived and points to the given Location Id. * @@ -1408,26 +1505,6 @@ private function assertUrlAliasPropertiesCorrect( ); } - /** - * @return \Doctrine\DBAL\Connection - * - * @throws \ErrorException - */ - private function getRawDatabaseConnection() - { - $connection = $this - ->getSetupFactory() - ->getServiceContainer()->get('ezpublish.api.storage_engine.legacy.connection'); - - if (!$connection instanceof Connection) { - throw new \RuntimeException( - sprintf('Expected %s got something else', Connection::class) - ); - } - - return $connection; - } - /** * Insert intentionally broken rows into ezurlalias_ml table to test cleanup API. *