![]() Server : Apache System : Linux server2.corals.io 4.18.0-348.2.1.el8_5.x86_64 #1 SMP Mon Nov 15 09:17:08 EST 2021 x86_64 User : corals ( 1002) PHP Version : 7.4.33 Disable Function : exec,passthru,shell_exec,system Directory : /home/corals/cartforge.co/vendor/magento/module-sitemap/Test/Unit/Model/ |
<?php /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ declare(strict_types=1); namespace Magento\Sitemap\Test\Unit\Model; use Magento\Framework\App\Request\Http; use Magento\Framework\DataObject; use Magento\Framework\Escaper; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Filesystem; use Magento\Framework\Filesystem\Directory\Write as DirectoryWrite; use Magento\Framework\Filesystem\File\Write; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Framework\Translate\InlineInterface; use Magento\Framework\ZendEscaper; use Magento\Sitemap\Helper\Data; use Magento\Sitemap\Model\ItemProvider\ConfigReaderInterface; use Magento\Sitemap\Model\ItemProvider\ItemProviderInterface; use Magento\Sitemap\Model\ResourceModel\Catalog\Category; use Magento\Sitemap\Model\ResourceModel\Catalog\CategoryFactory; use Magento\Sitemap\Model\ResourceModel\Catalog\Product; use Magento\Sitemap\Model\ResourceModel\Catalog\ProductFactory; use Magento\Sitemap\Model\ResourceModel\Cms\Page; use Magento\Sitemap\Model\ResourceModel\Cms\PageFactory; use Magento\Sitemap\Model\ResourceModel\Sitemap as SitemapResource; use Magento\Sitemap\Model\Sitemap; use Magento\Sitemap\Model\SitemapConfigReaderInterface; use Magento\Sitemap\Model\SitemapItem; use Magento\Store\Model\Store; use Magento\Store\Model\StoreManagerInterface; use PHPUnit\Framework\Assert; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class SitemapTest extends TestCase { /** * @var Data */ private $helperMockSitemap; /** * @var SitemapResource */ private $resourceMock; /** * @var Category */ private $sitemapCategoryMock; /** * @var Product */ private $sitemapProductMock; /** * @var Page */ private $sitemapCmsPageMock; /** * @var Filesystem */ private $filesystemMock; /** * @var DirectoryWrite */ private $directoryMock; /** * @var Write */ private $fileMock; /** * @var StoreManagerInterface|MockObject */ private $storeManagerMock; /** * @var ItemProviderInterface|MockObject */ private $itemProviderMock; /** * @var ConfigReaderInterface|MockObject */ private $configReaderMock; /** * @var Http|MockObject */ private $request; /** * @var Store|MockObject */ private $store; /** * @inheritdoc */ protected function setUp(): void { $this->sitemapCategoryMock = $this->getMockBuilder(Category::class) ->disableOriginalConstructor() ->getMock(); $this->sitemapProductMock = $this->getMockBuilder(Product::class) ->disableOriginalConstructor() ->getMock(); $this->sitemapCmsPageMock = $this->getMockBuilder(Page::class) ->disableOriginalConstructor() ->getMock(); $this->helperMockSitemap = $this->getMockBuilder(Data::class) ->disableOriginalConstructor() ->getMock(); $resourceMethods = [ '_construct', 'beginTransaction', 'rollBack', 'save', 'addCommitCallback', 'commit', '__wakeup', ]; $this->resourceMock = $this->getMockBuilder(SitemapResource::class) ->setMethods($resourceMethods) ->disableOriginalConstructor() ->getMock(); $this->resourceMock->method('addCommitCallback') ->willReturnSelf(); $this->fileMock = $this->createMock(Write::class); $this->directoryMock = $this->createMock(DirectoryWrite::class); $this->directoryMock->method('openFile') ->willReturn($this->fileMock); $this->filesystemMock = $this->getMockBuilder(Filesystem::class) ->setMethods(['getDirectoryWrite']) ->disableOriginalConstructor() ->getMock(); $this->filesystemMock->method('getDirectoryWrite') ->willReturn($this->directoryMock); $this->configReaderMock = $this->getMockForAbstractClass(SitemapConfigReaderInterface::class); $this->itemProviderMock = $this->getMockForAbstractClass(ItemProviderInterface::class); $this->request = $this->createMock(Http::class); $this->store = $this->createPartialMock(Store::class, ['isFrontUrlSecure', 'getBaseUrl']); $this->storeManagerMock = $this->getMockForAbstractClass(StoreManagerInterface::class); $this->storeManagerMock->method('getStore') ->willReturn($this->store); } /** * Check not allowed sitemap path validation */ public function testNotAllowedPath() { $this->expectException('Magento\Framework\Exception\LocalizedException'); $this->expectExceptionMessage('Please define a correct path.'); $model = $this->getModelMock(); $model->setSitemapPath('../'); $model->beforeSave(); } /** * Check not exists sitemap path validation */ public function testPathNotExists() { $this->expectException('Magento\Framework\Exception\LocalizedException'); $this->expectExceptionMessage('Please create the specified folder "/" before saving the sitemap.'); $this->directoryMock->expects($this->once()) ->method('isExist') ->willReturn(false); $model = $this->getModelMock(); $model->beforeSave(); } /** * Check not writable sitemap path validation */ public function testPathNotWritable() { $this->expectException(LocalizedException::class); $this->expectExceptionMessage('Please make sure that "/" is writable by the web-server.'); $this->directoryMock->expects($this->once()) ->method('isExist') ->willReturn(true); $this->directoryMock->expects($this->once()) ->method('isWritable') ->willReturn(false); $model = $this->getModelMock(); $model->beforeSave(); } /** * Check invalid chars in sitemap filename validation * No spaces or other characters are allowed. */ public function testFilenameInvalidChars() { $this->expectException(LocalizedException::class); $this->expectExceptionMessage( 'Please use only letters (a-z or A-Z), numbers (0-9) or underscores (_) in the filename.' ); $this->directoryMock->expects($this->once()) ->method('isExist') ->willReturn(true); $this->directoryMock->expects($this->once()) ->method('isWritable') ->willReturn(true); $model = $this->getModelMock(); $model->setSitemapFilename('*sitemap?.xml'); $model->beforeSave(); } /** * Data provider for sitemaps * * 1) Limit set to 50000 urls and 10M per sitemap file (single file) * 2) Limit set to 1 url and 10M per sitemap file (multiple files, 1 record per file) * 3) Limit set to 50000 urls and 264 bytes per sitemap file (multiple files, 1 record per file) * * @static * @return array */ public static function sitemapDataProvider() { $expectedSingleFile = ['/sitemap-1-1.xml' => __DIR__ . '/_files/sitemap-single.xml']; $expectedMultiFile = [ '/sitemap-1-1.xml' => __DIR__ . '/_files/sitemap-1-1.xml', '/sitemap-1-2.xml' => __DIR__ . '/_files/sitemap-1-2.xml', '/sitemap-1-3.xml' => __DIR__ . '/_files/sitemap-1-3.xml', '/sitemap-1-4.xml' => __DIR__ . '/_files/sitemap-1-4.xml', '/sitemap.xml' => __DIR__ . '/_files/sitemap-index.xml', ]; return [ [50000, 10485760, $expectedSingleFile, 6], [1, 10485760, $expectedMultiFile, 18], [50000, 264, $expectedMultiFile, 18], ]; } /** * Check generation of sitemaps * * @param int $maxLines * @param int $maxFileSize * @param array $expectedFile * @param int $expectedWrites * @dataProvider sitemapDataProvider */ public function testGenerateXml($maxLines, $maxFileSize, $expectedFile, $expectedWrites) { $actualData = []; $model = $this->prepareSitemapModelMock( $actualData, $maxLines, $maxFileSize, $expectedFile, $expectedWrites, null ); $model->generateXml(); $this->assertCount(count($expectedFile), $actualData, 'Number of generated files is incorrect'); foreach ($expectedFile as $expectedFileName => $expectedFilePath) { $this->assertArrayHasKey( $expectedFileName, $actualData, sprintf('File %s was not generated', $expectedFileName) ); $this->assertXmlStringEqualsXmlFile($expectedFilePath, $actualData[$expectedFileName]); } } /** * Data provider for robots.txt * * @static * @return array */ public static function robotsDataProvider() { $expectedSingleFile = ['/sitemap-1-1.xml' => __DIR__ . '/_files/sitemap-single.xml']; $expectedMultiFile = [ '/sitemap-1-1.xml' => __DIR__ . '/_files/sitemap-1-1.xml', '/sitemap-1-2.xml' => __DIR__ . '/_files/sitemap-1-2.xml', '/sitemap-1-3.xml' => __DIR__ . '/_files/sitemap-1-3.xml', '/sitemap-1-4.xml' => __DIR__ . '/_files/sitemap-1-4.xml', '/sitemap.xml' => __DIR__ . '/_files/sitemap-index.xml', ]; return [ [ 50000, 10485760, $expectedSingleFile, 6, [ 'robotsStart' => '', 'robotsFinish' => 'Sitemap: http://store.com/sitemap.xml', 'pushToRobots' => 1 ], ], // empty robots file [ 50000, 10485760, $expectedSingleFile, 6, [ 'robotsStart' => "User-agent: *", 'robotsFinish' => "User-agent: *" . PHP_EOL . 'Sitemap: http://store.com/sitemap.xml', 'pushToRobots' => 1 ] ], // not empty robots file EOL [ 1, 10485760, $expectedMultiFile, 18, [ 'robotsStart' => "User-agent: *\r\n", 'robotsFinish' => "User-agent: *\r\n\r\nSitemap: http://store.com/sitemap.xml", 'pushToRobots' => 1 ] ], // not empty robots file WIN [ 50000, 264, $expectedMultiFile, 18, [ 'robotsStart' => "User-agent: *\n", 'robotsFinish' => "User-agent: *\n\nSitemap: http://store.com/sitemap.xml", 'pushToRobots' => 1 ] ], // not empty robots file UNIX [ 50000, 10485760, $expectedSingleFile, 6, ['robotsStart' => '', 'robotsFinish' => '', 'pushToRobots' => 0] ] // empty robots file ]; } /** * Check pushing of sitemaps to robots.txt * * @param int $maxLines * @param int $maxFileSize * @param array $expectedFile * @param int $expectedWrites * @param array $robotsInfo * @dataProvider robotsDataProvider */ public function testAddSitemapToRobotsTxt($maxLines, $maxFileSize, $expectedFile, $expectedWrites, $robotsInfo) { $actualData = []; $model = $this->prepareSitemapModelMock( $actualData, $maxLines, $maxFileSize, $expectedFile, $expectedWrites, $robotsInfo ); $model->generateXml(); } /** * Prepare mock of Sitemap model * * @param array $actualData * @param int $maxLines * @param int $maxFileSize * @param array $expectedFile * @param int $expectedWrites * @param array $robotsInfo * @return Sitemap|MockObject * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ protected function prepareSitemapModelMock( &$actualData, $maxLines, $maxFileSize, $expectedFile, $expectedWrites, $robotsInfo ) { // Check that all $expectedWrites lines were written $actualData = []; $currentFile = ''; $streamWriteCallback = function ($str) use (&$actualData, &$currentFile) { if (!array_key_exists($currentFile, $actualData)) { $actualData[$currentFile] = ''; } $actualData[$currentFile] .= $str; }; // Check that all expected lines were written $this->fileMock->expects( $this->exactly($expectedWrites) )->method( 'write' )->willReturnCallback( $streamWriteCallback ); $checkFileCallback = function ($file) use (&$currentFile) { $currentFile = $file; };// Check that all expected file descriptors were created $this->directoryMock->expects($this->exactly(count($expectedFile)))->method('openFile') ->willReturnCallback($checkFileCallback); // Check that all file descriptors were closed $this->fileMock->expects($this->exactly(count($expectedFile))) ->method('close'); if (count($expectedFile) == 1) { $this->directoryMock->expects($this->once()) ->method('renameFile') ->willReturnCallback( function ($from, $to) { Assert::assertEquals('/sitemap-1-1.xml', $from); Assert::assertEquals('/sitemap.xml', $to); } ); } // Check robots txt $robotsStart = ''; if (isset($robotsInfo['robotsStart'])) { $robotsStart = $robotsInfo['robotsStart']; } $robotsFinish = 'Sitemap: http://store.com/sitemap.xml'; if (isset($robotsInfo['robotsFinish'])) { $robotsFinish = $robotsInfo['robotsFinish']; } $this->directoryMock->method('readFile') ->willReturn($robotsStart); $this->directoryMock->method('writeFile') ->with( $this->equalTo('robots.txt'), $this->equalTo($robotsFinish) ); // Mock helper methods $pushToRobots = 0; if (isset($robotsInfo['pushToRobots'])) { $pushToRobots = (int)$robotsInfo['pushToRobots']; } $this->configReaderMock->method('getMaximumLinesNumber') ->willReturn($maxLines); $this->configReaderMock->method('getMaximumFileSize') ->willReturn($maxFileSize); $this->configReaderMock->method('getEnableSubmissionRobots') ->willReturn($pushToRobots); $model = $this->getModelMock(true); $this->store->expects($this->atLeastOnce()) ->method('isFrontUrlSecure') ->willReturn(false); $this->store->expects($this->atLeastOnce()) ->method('getBaseUrl') ->with($this->isType('string'), false) ->willReturn('http://store.com/'); return $model; } /** * Get model mock object * * @param bool $mockBeforeSave * @return Sitemap|MockObject */ protected function getModelMock($mockBeforeSave = false) { $methods = [ '_construct', '_getResource', '_getBaseDir', '_getFileObject', '_afterSave', '_getCurrentDateTime', '_getCategoryItemsCollection', '_getProductItemsCollection', '_getPageItemsCollection', '_getDocumentRoot', ]; if ($mockBeforeSave) { $methods[] = 'beforeSave'; } $storeBaseMediaUrl = 'http://store.com/media/catalog/product/cache/c9e0b0ef589f3508e5ba515cde53c5ff/'; $this->itemProviderMock->method('getItems') ->willReturn( [ new SitemapItem('category.html', '1.0', 'daily', '2012-12-21 00:00:00'), new SitemapItem('/category/sub-category.html', '1.0', 'daily', '2012-12-21 00:00:00'), new SitemapItem('product.html', '0.5', 'monthly', '2012-12-21 00:00:00'), new SitemapItem( 'product2.html', '0.5', 'monthly', '2012-12-21 00:00:00', new DataObject( [ 'collection' => [ new DataObject( [ 'url' => $storeBaseMediaUrl . 'i/m/image1.png', 'caption' => 'Copyright © caption ™ & > title < "' ] ), new DataObject( ['url' => $storeBaseMediaUrl . 'i/m/image_no_caption.png', 'caption' => null] ), ], 'thumbnail' => $storeBaseMediaUrl . 't/h/thumbnail.jpg', 'title' => 'Product & > title < "', ] ) ) ] ); /** @var Sitemap $model */ $model = $this->getMockBuilder(Sitemap::class) ->setMethods($methods) ->setConstructorArgs($this->getModelConstructorArgs()) ->getMock(); $model->method('_getResource') ->willReturn($this->resourceMock); $model->method('_getCurrentDateTime') ->willReturn('2012-12-21T00:00:00-08:00'); $model->method('_getBaseDir') ->willReturn(''); $model->method('_getDocumentRoot') ->willReturn('/project'); $model->setSitemapFilename('sitemap.xml'); $model->setStoreId(1); $model->setSitemapPath('/'); return $model; } /** * @return array */ private function getModelConstructorArgs() { $categoryFactory = $this->getMockBuilder(CategoryFactory::class) ->disableOriginalConstructor() ->getMock(); $productFactory = $this->getMockBuilder(ProductFactory::class) ->disableOriginalConstructor() ->getMock(); $cmsFactory = $this->getMockBuilder(PageFactory::class) ->disableOriginalConstructor() ->getMock(); $objectManager = new ObjectManager($this); $escaper = $objectManager->getObject(Escaper::class); $this->setPrivatePropertyValue($escaper, 'escaper', $objectManager->getObject(ZendEscaper::class)); $this->setPrivatePropertyValue($escaper, 'translateInline', $this->createMock(InlineInterface::class)); $constructArguments = $objectManager->getConstructArguments( Sitemap::class, [ 'categoryFactory' => $categoryFactory, 'productFactory' => $productFactory, 'cmsFactory' => $cmsFactory, 'storeManager' => $this->storeManagerMock, 'sitemapData' => $this->helperMockSitemap, 'filesystem' => $this->filesystemMock, 'itemProvider' => $this->itemProviderMock, 'configReader' => $this->configReaderMock, 'escaper' => $escaper, 'request' => $this->request, ] ); $constructArguments['resource'] = null; return $constructArguments; } /** * Check site URL getter * * @param string $storeBaseUrl * @param string $documentRoot * @param string $baseDir * @param string $sitemapPath * @param string $sitemapFileName * @param string $result * @dataProvider siteUrlDataProvider */ public function testGetSitemapUrl($storeBaseUrl, $documentRoot, $baseDir, $sitemapPath, $sitemapFileName, $result) { /** @var Sitemap $model */ $model = $this->getMockBuilder(Sitemap::class) ->setMethods( [ '_getStoreBaseUrl', '_getDocumentRoot', '_getBaseDir', '_construct', ] ) ->setConstructorArgs($this->getModelConstructorArgs()) ->getMock(); $model->method('_getStoreBaseUrl') ->willReturn($storeBaseUrl); $model->method('_getDocumentRoot') ->willReturn($documentRoot); $model->method('_getBaseDir') ->willReturn($baseDir); $this->assertEquals($result, $model->getSitemapUrl($sitemapPath, $sitemapFileName)); } /** * Data provider for Check site URL getter * * @static * @return array */ public static function siteUrlDataProvider() { return [ [ 'http://store.com', 'c:\\http\\mage2\\', 'c:\\http\\mage2\\', '/', 'sitemap.xml', 'http://store.com/sitemap.xml', ], [ 'http://store.com/store2', 'c:\\http\\mage2\\', 'c:\\http\\mage2\\', '/sitemaps/store2', 'sitemap.xml', 'http://store.com/sitemaps/store2/sitemap.xml' ], [ 'http://store.com/builds/regression/ee/', '/var/www/html', '/opt/builds/regression/ee', '/', 'sitemap.xml', 'http://store.com/builds/regression/ee/sitemap.xml' ], [ 'http://store.com/store2', 'c:\\http\\mage2\\', 'c:\\http\\mage2\\store2', '/sitemaps/store2', 'sitemap.xml', 'http://store.com/store2/sitemaps/store2/sitemap.xml' ], [ 'http://store2.store.com', 'c:\\http\\mage2\\', 'c:\\http\\mage2\\', '/sitemaps/store2', 'sitemap.xml', 'http://store2.store.com/sitemaps/store2/sitemap.xml' ], [ 'http://store.com', '/var/www/store/', '/var/www/store/', '/', 'sitemap.xml', 'http://store.com/sitemap.xml' ], [ 'http://store.com/store2', '/var/www/store/', '/var/www/store/store2/', '/sitemaps/store2', 'sitemap.xml', 'http://store.com/store2/sitemaps/store2/sitemap.xml' ] ]; } /** * Check site URL getter * * @param string $storeBaseUrl * @param string $baseDir * @param string $documentRoot * @dataProvider getDocumentRootFromBaseDirUrlDataProvider */ public function testGetDocumentRootFromBaseDir( string $storeBaseUrl, string $baseDir, ?string $documentRoot ) { $this->store->setCode('store'); $this->store->method('getBaseUrl')->willReturn($storeBaseUrl); $this->directoryMock->method('getAbsolutePath')->willReturn($baseDir); /** @var Sitemap $model */ $model = $this->getMockBuilder(Sitemap::class) ->setMethods(['_construct']) ->setConstructorArgs($this->getModelConstructorArgs()) ->getMock(); $method = new \ReflectionMethod($model, 'getDocumentRootFromBaseDir'); $method->setAccessible(true); $this->assertSame($documentRoot, $method->invoke($model)); } /** * Provides test cases for document root testing * * @return array */ public function getDocumentRootFromBaseDirUrlDataProvider(): array { return [ [ 'http://magento.com/', '/var/www', '/var/www', ], [ 'http://magento.com/usa', '/var/www/usa', '/var/www', ], [ 'http://magento.com/usa/tx', '/var/www/usa/tx', '/var/www', ], 'symlink <document root>/usa/txt -> /var/www/html' => [ 'http://magento.com/usa/tx', '/var/www/html', null, ], ]; } /** * @param mixed $object * @param string $attributeName * @param string $value */ private function setPrivatePropertyValue($object, $attributeName, $value): void { $attribute = new \ReflectionProperty($object, $attributeName); if ($attribute->isPublic()) { $object->$attributeName = $value; } else { $attribute->setAccessible(true); $attribute->setValue($object, $value); $attribute->setAccessible(false); } } }