359 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			PHP
		
	
	
		
			Executable File
		
	
	
			
		
		
	
	
			359 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			PHP
		
	
	
		
			Executable File
		
	
	
<?php
 | 
						|
/**
 | 
						|
 * Test services for name collisions.
 | 
						|
 *
 | 
						|
 * Let we have two service interfaces called Foo\Bar\Service\SomeBazV1Interface and Foo\Bar\Service\Some\BazV1Interface.
 | 
						|
 * Given current name generation logic both are going to be translated to BarSomeBazV1. This test checks such things
 | 
						|
 * are not going to happen.
 | 
						|
 *
 | 
						|
 * Copyright © Magento, Inc. All rights reserved.
 | 
						|
 * See COPYING.txt for license details.
 | 
						|
 */
 | 
						|
namespace Magento\AsynchronousOperations\Model;
 | 
						|
 | 
						|
use Magento\AsynchronousOperations\Model\ResourceModel\Bulk\Collection as BulkCollection;
 | 
						|
use Magento\Framework\MessageQueue\BulkPublisherInterface;
 | 
						|
use Magento\Framework\Exception\BulkException;
 | 
						|
use Magento\Framework\Phrase;
 | 
						|
use Magento\Framework\Registry;
 | 
						|
use Magento\Framework\Webapi\Exception;
 | 
						|
use Magento\TestFramework\Helper\Bootstrap;
 | 
						|
use Magento\Catalog\Api\Data\ProductInterface;
 | 
						|
use Magento\TestFramework\MessageQueue\PublisherConsumerController;
 | 
						|
use Magento\TestFramework\MessageQueue\EnvironmentPreconditionException;
 | 
						|
use Magento\TestFramework\MessageQueue\PreconditionFailedException;
 | 
						|
use Magento\Catalog\Model\ResourceModel\Product\Collection;
 | 
						|
use Magento\Catalog\Api\ProductRepositoryInterface;
 | 
						|
use Magento\Framework\ObjectManagerInterface;
 | 
						|
 | 
						|
/**
 | 
						|
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
 | 
						|
 *
 | 
						|
 * @magentoDbIsolation disabled
 | 
						|
 */
 | 
						|
class MassScheduleTest extends \PHPUnit\Framework\TestCase
 | 
						|
{
 | 
						|
    /**
 | 
						|
     * @var string[]
 | 
						|
     */
 | 
						|
    protected $consumers = ['async.operations.all'];
 | 
						|
 | 
						|
    /**
 | 
						|
     * @var ObjectManagerInterface
 | 
						|
     */
 | 
						|
    protected $objectManager;
 | 
						|
 | 
						|
    /**
 | 
						|
     * @var MassSchedule
 | 
						|
     */
 | 
						|
    private $massSchedule;
 | 
						|
 | 
						|
    /**
 | 
						|
     * @var Collection
 | 
						|
     */
 | 
						|
    private $collection;
 | 
						|
 | 
						|
    /**
 | 
						|
     * @var ProductRepositoryInterface
 | 
						|
     */
 | 
						|
    private $productRepository;
 | 
						|
 | 
						|
    /**
 | 
						|
     * @var PublisherConsumerController
 | 
						|
     */
 | 
						|
    private $publisherConsumerController;
 | 
						|
 | 
						|
    /**
 | 
						|
     * @var array
 | 
						|
     */
 | 
						|
    private $skus = [];
 | 
						|
 | 
						|
    /** @var string */
 | 
						|
    private $logFilePath;
 | 
						|
 | 
						|
    /**
 | 
						|
     * @var Registry
 | 
						|
     */
 | 
						|
    private $registry;
 | 
						|
 | 
						|
    protected function setUp(): void
 | 
						|
    {
 | 
						|
        $this->objectManager = Bootstrap::getObjectManager();
 | 
						|
        $this->registry = $this->objectManager->get(Registry::class);
 | 
						|
        $this->massSchedule = $this->objectManager->create(MassSchedule::class);
 | 
						|
        $this->logFilePath = TESTS_TEMP_DIR . "/MessageQueueTestLog.txt";
 | 
						|
        $this->collection = $this->objectManager->create(Collection::class);
 | 
						|
        $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class);
 | 
						|
        /** @var PublisherConsumerController publisherConsumerController */
 | 
						|
        $this->publisherConsumerController = $this->objectManager->create(PublisherConsumerController::class, [
 | 
						|
            'consumers' => $this->consumers,
 | 
						|
            'logFilePath' => $this->logFilePath,
 | 
						|
            'appInitParams' => \Magento\TestFramework\Helper\Bootstrap::getInstance()->getAppInitParams()
 | 
						|
        ]);
 | 
						|
 | 
						|
        try {
 | 
						|
            $this->publisherConsumerController->initialize();
 | 
						|
        } catch (EnvironmentPreconditionException $e) {
 | 
						|
            $this->markTestSkipped($e->getMessage());
 | 
						|
        } catch (PreconditionFailedException $e) {
 | 
						|
            $this->fail($e->getMessage());
 | 
						|
        }
 | 
						|
 | 
						|
        parent::setUp();
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * @dataProvider productDataProvider
 | 
						|
     * @param ProductInterface[] $products
 | 
						|
     */
 | 
						|
    public function testScheduleMass($products)
 | 
						|
    {
 | 
						|
        try {
 | 
						|
            $this->sendBulk($products);
 | 
						|
        } catch (BulkException $bulkException) {
 | 
						|
            $this->fail('Bulk was not accepted in full');
 | 
						|
        }
 | 
						|
 | 
						|
        //assert all products are created
 | 
						|
        try {
 | 
						|
            $this->publisherConsumerController->waitForAsynchronousResult(
 | 
						|
                [$this, 'assertProductExists'],
 | 
						|
                [$this->skus, count($this->skus)]
 | 
						|
            );
 | 
						|
        } catch (PreconditionFailedException $e) {
 | 
						|
            $this->fail("Not all products were created");
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * @dataProvider publisherExceptionDataProvider
 | 
						|
     * @param \Exception $exception
 | 
						|
     */
 | 
						|
    public function testScheduleMassWithExceptionDuringPublishing(\Exception $exception)
 | 
						|
    {
 | 
						|
        $products = [
 | 
						|
            ['product' => $this->getProduct()],
 | 
						|
        ];
 | 
						|
 | 
						|
        $publisher = $this->createMock(BulkPublisherInterface::class);
 | 
						|
        $publisher->expects($this->atLeastOnce())
 | 
						|
            ->method('publish')
 | 
						|
            ->willThrowException($exception);
 | 
						|
        $bulkManagement = $this->objectManager->create(
 | 
						|
            BulkManagement::class,
 | 
						|
            ['publisher' => $publisher]
 | 
						|
        );
 | 
						|
        $this->massSchedule = $this->objectManager->create(
 | 
						|
            MassSchedule::class,
 | 
						|
            ['bulkManagement' => $bulkManagement]
 | 
						|
        );
 | 
						|
 | 
						|
        $bulkCollection = $this->objectManager->create(BulkCollection::class);
 | 
						|
        $bulksCount = $bulkCollection->getSize();
 | 
						|
        try {
 | 
						|
            $this->massSchedule->publishMass(
 | 
						|
                'async.magento.catalog.api.productrepositoryinterface.save.post',
 | 
						|
                $products
 | 
						|
            );
 | 
						|
            $this->fail('Publish is not failed when operations are not published.');
 | 
						|
        } catch (\Exception $e) {
 | 
						|
            $bulkCollection = $this->objectManager->create(BulkCollection::class);
 | 
						|
            $this->assertEquals($bulksCount, $bulkCollection->getSize());
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * @return array
 | 
						|
     */
 | 
						|
    public function publisherExceptionDataProvider(): array
 | 
						|
    {
 | 
						|
        return [
 | 
						|
            [new \InvalidArgumentException('Unknown publisher type async')],
 | 
						|
            [new \LogicException('Could not find an implementation type for type "async" and connection "amqp".')],
 | 
						|
        ];
 | 
						|
    }
 | 
						|
 | 
						|
    public function sendBulk($products)
 | 
						|
    {
 | 
						|
        $this->skus = [];
 | 
						|
        foreach ($products as $data) {
 | 
						|
            if (isset($data['product'])) {
 | 
						|
                $this->skus[] = $data['product']->getSku();
 | 
						|
            }
 | 
						|
        }
 | 
						|
        $this->clearProducts();
 | 
						|
 | 
						|
        $result = $this->massSchedule->publishMass(
 | 
						|
            'async.magento.catalog.api.productrepositoryinterface.save.post',
 | 
						|
            $products
 | 
						|
        );
 | 
						|
 | 
						|
        //assert bulk accepted with no errors
 | 
						|
        $this->assertFalse($result->isErrors());
 | 
						|
 | 
						|
        //assert number of products sent to queue
 | 
						|
        $this->assertCount(count($this->skus), $result->getRequestItems());
 | 
						|
    }
 | 
						|
 | 
						|
    protected function tearDown(): void
 | 
						|
    {
 | 
						|
        $this->publisherConsumerController->stopConsumers();
 | 
						|
        $this->clearProducts();
 | 
						|
 | 
						|
        parent::tearDown();
 | 
						|
    }
 | 
						|
 | 
						|
    private function clearProducts()
 | 
						|
    {
 | 
						|
        $size = $this->objectManager->create(Collection::class)
 | 
						|
            ->addAttributeToFilter('sku', ['in' => $this->skus])
 | 
						|
            ->load()
 | 
						|
            ->getSize();
 | 
						|
 | 
						|
        if ($size == 0) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
 | 
						|
        $this->registry->unregister('isSecureArea');
 | 
						|
        $this->registry->register('isSecureArea', true);
 | 
						|
        try {
 | 
						|
            foreach ($this->skus as $sku) {
 | 
						|
                $this->productRepository->deleteById($sku);
 | 
						|
            }
 | 
						|
        } catch (\Exception $e) {
 | 
						|
            //nothing to delete
 | 
						|
        }
 | 
						|
        $this->registry->unregister('isSecureArea');
 | 
						|
 | 
						|
        $size = $this->objectManager->create(Collection::class)
 | 
						|
            ->addAttributeToFilter('sku', ['in' => $this->skus])
 | 
						|
            ->load()
 | 
						|
            ->getSize();
 | 
						|
 | 
						|
        if ($size > 0) {
 | 
						|
            throw new Exception(new Phrase("Collection size after clearing the products: %size", ['size' => $size]));
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    public function assertProductExists($productsSkus, $count)
 | 
						|
    {
 | 
						|
        $collection = $this->objectManager->create(Collection::class)
 | 
						|
            ->addAttributeToFilter('sku', ['in' => $productsSkus])
 | 
						|
            ->load();
 | 
						|
        $size = $collection->getSize();
 | 
						|
        return $size == $count;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * @dataProvider productExceptionDataProvider
 | 
						|
     * @param ProductInterface[] $products
 | 
						|
     */
 | 
						|
    public function testScheduleMassOneEntityFailure($products)
 | 
						|
    {
 | 
						|
        try {
 | 
						|
            $this->sendBulk($products);
 | 
						|
        } catch (BulkException $e) {
 | 
						|
            $this->assertCount(1, $e->getErrors());
 | 
						|
 | 
						|
            $errors = $e->getErrors();
 | 
						|
            $this->assertInstanceOf(\Magento\Framework\Exception\LocalizedException::class, $errors[0]);
 | 
						|
 | 
						|
            $this->assertEquals("Error processing 1 element of input data", $errors[0]->getMessage());
 | 
						|
 | 
						|
            $reasonException = $errors[0]->getPrevious();
 | 
						|
 | 
						|
            $expectedErrorMessage = "Data item corresponding to \"product\" " .
 | 
						|
                "must be specified in the message with topic " .
 | 
						|
                "\"async.magento.catalog.api.productrepositoryinterface.save.post\".";
 | 
						|
            $this->assertEquals(
 | 
						|
                $expectedErrorMessage,
 | 
						|
                $reasonException->getMessage()
 | 
						|
            );
 | 
						|
 | 
						|
            /** @var \Magento\WebapiAsync\Model\AsyncResponse $bulkStatus */
 | 
						|
            $bulkStatus = $e->getData();
 | 
						|
            $this->assertTrue($bulkStatus->isErrors());
 | 
						|
 | 
						|
            /** @var ItemStatus[] $items */
 | 
						|
            $items = $bulkStatus->getRequestItems();
 | 
						|
            $this->assertCount(2, $items);
 | 
						|
 | 
						|
            $this->assertEquals(ItemStatus::STATUS_ACCEPTED, $items[0]->getStatus());
 | 
						|
            $this->assertEquals(0, $items[0]->getId());
 | 
						|
 | 
						|
            $this->assertEquals(ItemStatus::STATUS_REJECTED, $items[1]->getStatus());
 | 
						|
            $this->assertEquals(1, $items[1]->getId());
 | 
						|
            $this->assertEquals($expectedErrorMessage, $items[1]->getErrorMessage());
 | 
						|
        }
 | 
						|
 | 
						|
        //assert one products is created
 | 
						|
        try {
 | 
						|
            $this->publisherConsumerController->waitForAsynchronousResult(
 | 
						|
                [$this, 'assertProductExists'],
 | 
						|
                [$this->skus, count($this->skus)]
 | 
						|
            );
 | 
						|
        } catch (PreconditionFailedException $e) {
 | 
						|
            $this->fail("Not all products were created");
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    private function getProduct()
 | 
						|
    {
 | 
						|
        /** @var $product \Magento\Catalog\Model\Product */
 | 
						|
        $product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
 | 
						|
            ->create(ProductInterface::class);
 | 
						|
        $product
 | 
						|
            ->setTypeId('simple')
 | 
						|
            ->setAttributeSetId(4)
 | 
						|
            ->setWebsiteIds([1])
 | 
						|
            ->setName('Simple Product 1')
 | 
						|
            ->setSku('unique-simple-product1')
 | 
						|
            ->setPrice(10)
 | 
						|
            ->setMetaTitle('meta title')
 | 
						|
            ->setMetaKeyword('meta keyword')
 | 
						|
            ->setMetaDescription('meta description')
 | 
						|
            ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH)
 | 
						|
            ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED)
 | 
						|
            ->setStockData(['use_config_manage_stock' => 0]);
 | 
						|
        return $product;
 | 
						|
    }
 | 
						|
 | 
						|
    public function productDataProvider()
 | 
						|
    {
 | 
						|
        return [
 | 
						|
            'single_product' => [
 | 
						|
                [['product' => $this->getProduct()]],
 | 
						|
            ],
 | 
						|
            'multiple_products' => [
 | 
						|
                [
 | 
						|
                    ['product' => $this->getProduct()
 | 
						|
                        ->setName('Simple Product 3')
 | 
						|
                        ->setSku('unique-simple-product3')
 | 
						|
                        ->setMetaTitle('meta title 3')
 | 
						|
                    ],
 | 
						|
                    ['product' => $this->getProduct()
 | 
						|
                        ->setName('Simple Product 2')
 | 
						|
                        ->setSku('unique-simple-product2')
 | 
						|
                        ->setMetaTitle('meta title 2')
 | 
						|
                    ]
 | 
						|
                ]
 | 
						|
            ],
 | 
						|
        ];
 | 
						|
    }
 | 
						|
 | 
						|
    public function productExceptionDataProvider()
 | 
						|
    {
 | 
						|
        return [
 | 
						|
            'single_product' => [
 | 
						|
                [['product' => $this->getProduct()]],
 | 
						|
            ],
 | 
						|
            'multiple_products' => [
 | 
						|
                [
 | 
						|
                    ['product' => $this->getProduct()],
 | 
						|
                    ['customer' => $this->getProduct()]
 | 
						|
                ]
 | 
						|
            ],
 | 
						|
        ];
 | 
						|
    }
 | 
						|
}
 |