| Current Path : /home/rtorresani/www/vendor/magento/module-catalog/Test/Unit/Model/ |
| Current File : //home/rtorresani/www/vendor/magento/module-catalog/Test/Unit/Model/ProductRepositoryTest.php |
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Magento\Catalog\Test\Unit\Model;
use Magento\Catalog\Api\Data\ProductExtensionInterface;
use Magento\Catalog\Api\Data\ProductSearchResultsInterface;
use Magento\Catalog\Api\Data\ProductSearchResultsInterfaceFactory;
use Magento\Catalog\Api\ProductAttributeRepositoryInterface;
use Magento\Catalog\Controller\Adminhtml\Product\Initialization\Helper;
use Magento\Catalog\Model\Product;
use Magento\Catalog\Model\Product\Gallery\MimeTypeExtensionMap;
use Magento\Catalog\Model\Product\Gallery\Processor;
use Magento\Catalog\Model\Product\LinkTypeProvider;
use Magento\Catalog\Model\Product\Media\Config;
use Magento\Catalog\Model\Product\Option;
use Magento\Catalog\Model\Product\Option\Value;
use Magento\Catalog\Model\ProductFactory;
use Magento\Catalog\Model\ProductLink\Link;
use Magento\Catalog\Model\ProductRepository;
use Magento\Catalog\Model\ProductRepository\MediaGalleryProcessor;
use Magento\Catalog\Model\ResourceModel\Product\Collection;
use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory;
use Magento\Eav\Model\Entity\Attribute\Exception as AttributeException;
use Magento\Framework\Api\Data\ImageContentInterface;
use Magento\Framework\Api\Data\ImageContentInterfaceFactory;
use Magento\Framework\Api\ExtensibleDataObjectConverter;
use Magento\Framework\Api\FilterBuilder;
use Magento\Framework\Api\ImageContent;
use Magento\Framework\Api\ImageContentValidator;
use Magento\Framework\Api\ImageContentValidatorInterface;
use Magento\Framework\Api\ImageProcessorInterface;
use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface;
use Magento\Framework\Api\SearchCriteriaBuilder;
use Magento\Framework\Api\SearchCriteriaInterface;
use Magento\Framework\DB\Adapter\ConnectionException;
use Magento\Framework\Filesystem;
use Magento\Framework\Serialize\Serializer\Json;
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
use Magento\Store\Api\Data\StoreInterface;
use Magento\Store\Model\Store;
use Magento\Store\Model\StoreManagerInterface;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
/**
* Test for \Magento\Catalog\Model\ProductRepository.
*
* @SuppressWarnings(PHPMD.TooManyFields)
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
class ProductRepositoryTest extends TestCase
{
private const STUB_STORE_ID = 1;
private const STUB_STORE_ID_GLOBAL = 0;
private const STUB_PRODUCT_ID = 100;
private const STUB_PRODUCT_NAME = 'name';
private const STUB_PRODUCT_SKU = 'sku';
/**
* @var Product|MockObject
*/
private $product;
/**
* @var Product|MockObject
*/
private $initializedProduct;
/**
* @var ProductRepository
*/
private $model;
/**
* @var Helper|MockObject
*/
private $initializationHelper;
/**
* @var Product|MockObject
*/
private $resourceModel;
/**
* @var ProductFactory|MockObject
*/
private $productFactory;
/**
* @var CollectionFactory|MockObject
*/
private $collectionFactory;
/**
* @var SearchCriteriaBuilder|MockObject
*/
private $searchCriteriaBuilder;
/**
* @var FilterBuilder|MockObject
*/
private $filterBuilder;
/**
* @var ProductAttributeRepositoryInterface|MockObject
*/
private $metadataService;
/**
* @var ProductSearchResultsInterfaceFactory|MockObject
*/
private $searchResultsFactory;
/**
* @var ExtensibleDataObjectConverter|MockObject
*/
private $extensibleDataObjectConverter;
/**
* @var array data to create product
*/
private $productData = [
'sku' => 'exisiting',
'name' => 'existing product'
];
/**
* @var Filesystem|MockObject
*/
private $fileSystem;
/**
* @var MimeTypeExtensionMap|MockObject
*/
private $mimeTypeExtensionMap;
/**
* @var ImageContentInterfaceFactory|MockObject
*/
private $contentFactory;
/**
* @var ImageContentValidator|MockObject
*/
private $contentValidator;
/**
* @var LinkTypeProvider|MockObject
*/
private $linkTypeProvider;
/**
* @var ImageProcessorInterface|MockObject
*/
private $imageProcessor;
/**
* @var ObjectManager
*/
private $objectManager;
/**
* @var StoreManagerInterface|MockObject
*/
private $storeManager;
/**
* @var Processor|MockObject
*/
private $processor;
/**
* @var CollectionProcessorInterface|MockObject
*/
private $collectionProcessor;
/**
* @var ProductExtensionInterface|MockObject
*/
private $productExtension;
/**
* @var Json|MockObject
*/
private $serializerMock;
/**
* Product repository cache limit.
*
* @var int
*/
private $cacheLimit = 2;
/**
* @inheritDoc
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
protected function setUp(): void
{
$this->productFactory = $this->getMockBuilder(ProductFactory::class)
->addMethods(['setData'])
->onlyMethods(['create'])
->disableOriginalConstructor()
->getMock();
$this->product = $this->getMockBuilder(Product::class)
->addMethods(['setWebsiteIds'])
->onlyMethods(
[
'getId',
'getSku',
'getWebsiteIds',
'load',
'setData',
'getStoreId',
'getMediaGalleryEntries',
'getExtensionAttributes',
'getCategoryIds',
'getAttributes'
]
)
->disableOriginalConstructor()
->getMock();
$this->initializedProduct = $this->getMockBuilder(Product::class)
->addMethods(['setProductOptions'])
->onlyMethods(
[
'getWebsiteIds',
'load',
'getOptions',
'getSku',
'hasGalleryAttribute',
'getMediaConfig',
'getMediaAttributes',
'getProductLinks',
'setProductLinks',
'validate',
'save',
'getMediaGalleryEntries',
'getExtensionAttributes',
'getCategoryIds',
'getAttributes'
]
)
->disableOriginalConstructor()
->getMock();
$this->initializedProduct->expects($this->any())
->method('hasGalleryAttribute')
->willReturn(true);
$this->filterBuilder = $this->createMock(FilterBuilder::class);
$this->initializationHelper = $this->createMock(Helper::class);
$this->collectionFactory = $this->createPartialMock(CollectionFactory::class, ['create']);
$this->searchCriteriaBuilder = $this->createMock(SearchCriteriaBuilder::class);
$this->metadataService = $this->getMockForAbstractClass(ProductAttributeRepositoryInterface::class);
$this->searchResultsFactory = $this->createPartialMock(
ProductSearchResultsInterfaceFactory::class,
['create']
);
$this->resourceModel = $this->createMock(\Magento\Catalog\Model\ResourceModel\Product::class);
$this->resourceModel->method('getLinkField')->willReturn('some_value');
$this->objectManager = new ObjectManager($this);
$this->extensibleDataObjectConverter = $this->getMockBuilder(ExtensibleDataObjectConverter::class)
->onlyMethods(['toNestedArray'])
->disableOriginalConstructor()
->getMock();
$this->fileSystem = $this->getMockBuilder(Filesystem::class)
->disableOriginalConstructor()
->getMock();
$this->mimeTypeExtensionMap = $this->getMockBuilder(MimeTypeExtensionMap::class)
->getMock();
$this->contentFactory = $this->createPartialMock(ImageContentInterfaceFactory::class, ['create']);
$this->contentValidator = $this->getMockBuilder(ImageContentValidatorInterface::class)
->disableOriginalConstructor()
->getMockForAbstractClass();
$this->linkTypeProvider = $this->createPartialMock(LinkTypeProvider::class, ['getLinkTypes']);
$this->imageProcessor = $this->getMockForAbstractClass(ImageProcessorInterface::class);
$this->storeManager = $this->getMockBuilder(StoreManagerInterface::class)
->disableOriginalConstructor()
->onlyMethods(['getStore', 'getWebsites'])
->getMockForAbstractClass();
$this->productExtension = $this->getMockBuilder(ProductExtensionInterface::class)
->addMethods(['__toArray'])
->disableOriginalConstructor()
->getMockForAbstractClass();
$this->productExtension
->method('__toArray')
->willReturn([]);
$this->product
->method('getExtensionAttributes')
->willReturn($this->productExtension);
$this->initializedProduct
->method('getExtensionAttributes')
->willReturn($this->productExtension);
$this->product
->method('getCategoryIds')
->willReturn([1, 2, 3, 4]);
$this->initializedProduct
->method('getCategoryIds')
->willReturn([1, 2, 3, 4]);
$storeMock = $this->getMockBuilder(StoreInterface::class)
->disableOriginalConstructor()
->onlyMethods(['getId', 'getWebsiteId', 'getCode'])
->getMockForAbstractClass();
$storeMock->method('getId')->willReturn(self::STUB_STORE_ID);
$storeMock->expects($this->any())->method('getWebsiteId')->willReturn('1');
$storeMock->expects($this->any())->method('getCode')->willReturn(Store::ADMIN_CODE);
$this->storeManager->expects($this->any())->method('getStore')->willReturn($storeMock);
$this->processor = $this->createMock(Processor::class);
$this->collectionProcessor = $this->getMockBuilder(CollectionProcessorInterface::class)
->getMock();
$this->serializerMock = $this->getMockBuilder(Json::class)
->getMock();
$this->serializerMock->expects($this->any())
->method('unserialize')
->willReturnCallback(
function ($value) {
return json_decode($value, true);
}
);
$this->serializerMock->method('serialize')->willReturn(''); // PHP 8.1. Compatibility
$mediaProcessor = $this->objectManager->getObject(
MediaGalleryProcessor::class,
[
'processor' => $this->processor,
'contentFactory' => $this->contentFactory,
'imageProcessor' => $this->imageProcessor
]
);
$this->model = $this->objectManager->getObject(
ProductRepository::class,
[
'productFactory' => $this->productFactory,
'initializationHelper' => $this->initializationHelper,
'resourceModel' => $this->resourceModel,
'filterBuilder' => $this->filterBuilder,
'collectionFactory' => $this->collectionFactory,
'searchCriteriaBuilder' => $this->searchCriteriaBuilder,
'metadataServiceInterface' => $this->metadataService,
'searchResultsFactory' => $this->searchResultsFactory,
'extensibleDataObjectConverter' => $this->extensibleDataObjectConverter,
'contentValidator' => $this->contentValidator,
'fileSystem' => $this->fileSystem,
'mimeTypeExtensionMap' => $this->mimeTypeExtensionMap,
'linkTypeProvider' => $this->linkTypeProvider,
'storeManager' => $this->storeManager,
'mediaGalleryProcessor' => $this->processor,
'collectionProcessor' => $this->collectionProcessor,
'serializer' => $this->serializerMock,
'cacheLimit' => $this->cacheLimit
]
);
$this->objectManager->setBackwardCompatibleProperty($this->model, 'mediaProcessor', $mediaProcessor);
}
/**
* Test save product with global store id.
*
* @param array $productData
*
* @return void
* @dataProvider getProductData
*/
public function testSaveForAllStoreViewScope(array $productData): void
{
$this->productFactory->method('create')->willReturn($this->product);
$this->product->method('getSku')->willReturn($productData['sku']);
$this->extensibleDataObjectConverter
->expects($this->once())
->method('toNestedArray')
->willReturn($productData);
$this->resourceModel->method('getIdBySku')->willReturn(self::STUB_PRODUCT_ID);
$this->resourceModel->expects($this->once())->method('validate')->willReturn(true);
$storeIdData = ['store_id', $productData['store_id']];
$this->product
->method('setData')
->withConsecutive([], [], [], [], [], [], [], [], [], [], [], [], [], [], [], $storeIdData);
$this->model->save($this->product);
}
/**
* Product data provider.
*
* @return array
*/
public function getProductData(): array
{
return [
[
[
'sku' => self::STUB_PRODUCT_SKU,
'name' => self::STUB_PRODUCT_NAME,
'store_id' => self::STUB_STORE_ID_GLOBAL
]
]
];
}
/**
* Test save product without store.
*
* @return void
*/
public function testSaveWithoutStoreId(): void
{
$this->productFactory->method('create')->willReturn($this->product);
$this->product->method('getSku')->willReturn($this->productData['sku']);
$this->extensibleDataObjectConverter
->expects($this->once())
->method('toNestedArray')
->willReturn($this->productData);
$this->resourceModel->method('getIdBySku')->willReturn(self::STUB_PRODUCT_ID);
$this->resourceModel->expects($this->once())->method('validate')->willReturn(true);
$storeIdData = ['store_id', self::STUB_STORE_ID];
$this->product
->method('setData')
->withConsecutive([], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], $storeIdData);
$this->model->save($this->product);
}
/**
* @return void
*/
public function testGetAbsentProduct(): void
{
$this->expectException('Magento\Framework\Exception\NoSuchEntityException');
$this->expectExceptionMessage(
'The product that was requested doesn\'t exist. Verify the product and try again.'
);
$this->productFactory->expects($this->once())->method('create')
->willReturn($this->product);
$this->resourceModel->expects($this->once())->method('getIdBySku')->with('test_sku')
->willReturn(null);
$this->productFactory->expects($this->never())->method('setData');
$this->model->get('test_sku');
}
/**
* @return void
*/
public function testCreateCreatesProduct(): void
{
$sku = 'test_sku';
$this->productFactory->expects($this->once())->method('create')
->willReturn($this->product);
$this->resourceModel->expects($this->once())->method('getIdBySku')->with($sku)
->willReturn('test_id');
$this->product->expects($this->once())->method('load')->with('test_id');
$this->product->expects($this->once())->method('getSku')->willReturn($sku);
$this->assertEquals($this->product, $this->model->get($sku));
}
/**
* @return void
*/
public function testGetProductInEditMode(): void
{
$sku = 'test_sku';
$this->productFactory->expects($this->once())->method('create')
->willReturn($this->product);
$this->resourceModel->expects($this->once())->method('getIdBySku')->with($sku)
->willReturn('test_id');
$this->product->expects($this->once())->method('setData')->with('_edit_mode', true);
$this->product->expects($this->once())->method('load')->with('test_id');
$this->product->expects($this->once())->method('getSku')->willReturn($sku);
$this->assertEquals($this->product, $this->model->get($sku, true));
}
/**
* @return void
*/
public function testGetBySkuWithSpace(): void
{
$trimmedSku = 'test_sku';
$sku = 'test_sku ';
$this->productFactory->expects($this->once())->method('create')
->willReturn($this->product);
$this->resourceModel->expects($this->once())->method('getIdBySku')->with($sku)
->willReturn('test_id');
$this->product->expects($this->once())->method('load')->with('test_id');
$this->product->expects($this->once())->method('getSku')->willReturn($trimmedSku);
$this->assertEquals($this->product, $this->model->get($sku));
}
/**
* @return void
*/
public function testGetWithSetStoreId(): void
{
$productId = 123;
$sku = 'test-sku';
$storeId = 7;
$this->productFactory->expects($this->once())->method('create')->willReturn($this->product);
$this->resourceModel->expects($this->once())->method('getIdBySku')->with($sku)->willReturn($productId);
$this->product->expects($this->once())->method('setData')->with('store_id', $storeId);
$this->product->expects($this->once())->method('load')->with($productId);
$this->product->expects($this->once())->method('getId')->willReturn($productId);
$this->product->expects($this->once())->method('getSku')->willReturn($sku);
$this->assertSame($this->product, $this->model->get($sku, false, $storeId));
}
/**
* @return void
*/
public function testGetByIdAbsentProduct(): void
{
$this->expectException('Magento\Framework\Exception\NoSuchEntityException');
$this->expectExceptionMessage(
'The product that was requested doesn\'t exist. Verify the product and try again.'
);
$this->productFactory->expects($this->once())->method('create')
->willReturn($this->product);
$this->product->expects($this->once())->method('load')->with('product_id');
$this->product->expects($this->once())->method('getId')->willReturn(null);
$this->model->getById('product_id');
}
/**
* @return void
*/
public function testGetByIdProductInEditMode(): void
{
$productId = 123;
$this->productFactory->method('create')->willReturn($this->product);
$this->product->method('setData')->with('_edit_mode', true);
$this->product->method('load')->with($productId);
$this->product->expects($this->atLeastOnce())->method('getId')->willReturn($productId);
$this->product->method('getSku')->willReturn('simple');
$this->assertEquals($this->product, $this->model->getById($productId, true));
}
/**
* @param mixed $identifier
* @param bool $editMode
* @param mixed $storeId
*
* @return void
* @dataProvider cacheKeyDataProvider
*/
public function testGetByIdForCacheKeyGenerate($identifier, $editMode, $storeId): void
{
$this->productFactory
->expects($this->once())
->method('create')
->willReturn($this->product);
$withArgs = [];
if ($editMode) {
$withArgs[] = ['_edit_mode', $editMode];
}
if ($storeId !== null) {
$withArgs[] = ['store_id', $storeId];
}
$this->product
->method('setData')->withConsecutive(...$withArgs);
$this->product->expects($this->once())->method('load')->with($identifier);
$this->product->expects($this->atLeastOnce())->method('getId')->willReturn($identifier);
$this->product->method('getSku')->willReturn('simple');
$this->assertEquals($this->product, $this->model->getById($identifier, $editMode, $storeId));
//Second invocation should just return from cache
$this->assertEquals($this->product, $this->model->getById($identifier, $editMode, $storeId));
}
/**
* Test the forceReload parameter.
*
* @return void
*/
public function testGetByIdForcedReload(): void
{
$identifier = "23";
$editMode = false;
$storeId = 0;
$this->productFactory->expects($this->exactly(2))->method('create')
->willReturn($this->product);
$this->product->expects($this->exactly(2))->method('load');
$this->serializerMock->expects($this->exactly(3))->method('serialize');
$this->product->expects($this->exactly(4))->method('getId')->willReturn($identifier);
$this->product->method('getSku')->willReturn('simple');
$this->assertEquals($this->product, $this->model->getById($identifier, $editMode, $storeId));
//second invocation should just return from cache
$this->assertEquals($this->product, $this->model->getById($identifier, $editMode, $storeId));
//force reload
$this->assertEquals($this->product, $this->model->getById($identifier, $editMode, $storeId, true));
}
/**
* Test for getById() method if we try to get products when cache is already filled and is reduced.
*
* @return void
*/
public function testGetByIdWhenCacheReduced(): void
{
$result = [];
$expectedResult = [];
$productsCount = $this->cacheLimit * 2;
$productMocks = $this->getProductMocksForReducedCache($productsCount);
$productFactoryInvMock = $this->productFactory->expects($this->exactly($productsCount))
->method('create');
call_user_func_array([$productFactoryInvMock, 'willReturnOnConsecutiveCalls'], $productMocks);
$this->serializerMock->expects($this->atLeastOnce())->method('serialize');
for ($i = 1; $i <= $productsCount; $i++) {
$product = $this->model->getById($i, false, 0);
$result[] = $product->getId();
$expectedResult[] = $i;
}
$this->assertEquals($expectedResult, $result);
}
/**
* Get product mocks for testGetByIdWhenCacheReduced() method.
*
* @param int $productsCount
*
* @return array
*/
private function getProductMocksForReducedCache(int $productsCount): array
{
$productMocks = [];
for ($i = 1; $i <= $productsCount; $i++) {
$productMock = $this->getMockBuilder(Product::class)->disableOriginalConstructor()
->onlyMethods(['getId', 'getSku', 'load', 'setData'])
->getMock();
$productMock->expects($this->once())->method('load');
$productMock->expects($this->atLeastOnce())->method('getId')->willReturn($i);
$productMock->expects($this->atLeastOnce())->method('getSku')->willReturn($i . uniqid());
$productMocks[] = $productMock;
}
return $productMocks;
}
/**
* Test forceReload parameter.
*
* @return void
*/
public function testGetForcedReload(): void
{
$sku = "sku";
$id = "23";
$editMode = false;
$storeId = 0;
$this->productFactory->expects($this->exactly(2))->method('create')
->willReturn($this->product);
$this->product->expects($this->exactly(2))->method('load');
$this->product->expects($this->exactly(2))->method('getId')->willReturn($sku);
$this->resourceModel->expects($this->exactly(2))->method('getIdBySku')
->with($sku)->willReturn($id);
$this->product->expects($this->exactly(2))->method('getSku')->willReturn($sku);
$this->serializerMock->expects($this->exactly(3))->method('serialize');
$this->assertEquals($this->product, $this->model->get($sku, $editMode, $storeId));
//second invocation should just return from cache
$this->assertEquals($this->product, $this->model->get($sku, $editMode, $storeId));
//force reload
$this->assertEquals($this->product, $this->model->get($sku, $editMode, $storeId, true));
}
/**
* @return void
*/
public function testGetByIdWithSetStoreId(): void
{
$productId = 123;
$storeId = 1;
$this->productFactory->expects($this->atLeastOnce())->method('create')
->willReturn($this->product);
$this->product->expects($this->once())->method('setData')->with('store_id', $storeId);
$this->product->expects($this->once())->method('load')->with($productId);
$this->product->expects($this->atLeastOnce())->method('getId')->willReturn($productId);
$this->product->method('getSku')->willReturn('simple');
$this->assertEquals($this->product, $this->model->getById($productId, false, $storeId));
}
/**
* @return void
*/
public function testGetBySkuFromCacheInitializedInGetById(): void
{
$productId = 123;
$productSku = 'product_123';
$this->productFactory->expects($this->once())->method('create')
->willReturn($this->product);
$this->product->expects($this->once())->method('load')->with($productId);
$this->product->expects($this->atLeastOnce())->method('getId')->willReturn($productId);
$this->product->expects($this->once())->method('getSku')->willReturn($productSku);
$this->assertEquals($this->product, $this->model->getById($productId));
$this->assertEquals($this->product, $this->model->get($productSku));
}
/**
* @return void
*/
public function testSaveExisting(): void
{
$this->resourceModel->expects($this->any())->method('getIdBySku')->willReturn(100);
$this->productFactory->expects($this->any())
->method('create')
->willReturn($this->product);
$this->initializationHelper->expects($this->never())->method('initialize');
$this->resourceModel->expects($this->once())->method('validate')->with($this->product)
->willReturn(true);
$this->resourceModel->expects($this->once())->method('save')->with($this->product)->willReturn(true);
$this->extensibleDataObjectConverter
->expects($this->once())
->method('toNestedArray')
->willReturn($this->productData);
$this->product->expects($this->atLeastOnce())->method('getSku')->willReturn($this->productData['sku']);
$this->assertEquals($this->product, $this->model->save($this->product));
}
/**
* @return void
*/
public function testSaveNew(): void
{
$this->storeManager->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']);
$this->resourceModel
->method('getIdBySku')
->willReturnOnConsecutiveCalls(null, 100);
$this->productFactory->expects($this->any())
->method('create')
->willReturn($this->product);
$this->initializationHelper->expects($this->never())->method('initialize');
$this->resourceModel->expects($this->once())->method('validate')->with($this->product)
->willReturn(true);
$this->resourceModel->expects($this->once())->method('save')->with($this->product)->willReturn(true);
$this->extensibleDataObjectConverter
->expects($this->once())
->method('toNestedArray')
->willReturn($this->productData);
$this->product->method('getSku')->willReturn('simple');
$this->assertEquals($this->product, $this->model->save($this->product));
}
/**
* @return void
*/
public function testSaveUnableToSaveException(): void
{
$this->expectException('Magento\Framework\Exception\CouldNotSaveException');
$this->expectExceptionMessage('The product was unable to be saved. Please try again.');
$this->storeManager->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']);
$this->resourceModel->expects($this->exactly(1))
->method('getIdBySku')->willReturn(null);
$this->productFactory->expects($this->exactly(2))
->method('create')
->willReturn($this->product);
$this->initializationHelper->expects($this->never())->method('initialize');
$this->resourceModel->expects($this->once())->method('validate')->with($this->product)
->willReturn(true);
$this->resourceModel->expects($this->once())->method('save')->with($this->product)
->willThrowException(new \Exception());
$this->extensibleDataObjectConverter
->expects($this->once())
->method('toNestedArray')
->willReturn($this->productData);
$this->product->method('getSku')->willReturn('simple');
$this->model->save($this->product);
}
/**
* @return void
*/
public function testSaveException(): void
{
$this->expectException('Magento\Framework\Exception\InputException');
$this->expectExceptionMessage('Invalid value of "" provided for the attribute_code field.');
$this->storeManager->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']);
$this->resourceModel->expects($this->exactly(1))->method('getIdBySku')->willReturn(null);
$this->productFactory->expects($this->exactly(2))
->method('create')
->willReturn($this->product);
$this->initializationHelper->expects($this->never())->method('initialize');
$this->resourceModel->expects($this->once())->method('validate')->with($this->product)
->willReturn(true);
$attributeException = new AttributeException(__('123'));
$attributeException->setAttributeCode('attribute_code');
$this->resourceModel->expects($this->once())->method('save')->with($this->product)
->willThrowException($attributeException);
$this->product->expects($this->exactly(2))->method('getId')->willReturn(null);
$this->extensibleDataObjectConverter
->expects($this->once())
->method('toNestedArray')
->willReturn($this->productData);
$this->product->method('getSku')->willReturn('simple');
$this->model->save($this->product);
}
/**
* @return void
*/
public function testSaveInvalidProductException(): void
{
$this->expectException('Magento\Framework\Exception\CouldNotSaveException');
$this->expectExceptionMessage('Invalid product data: error1,error2');
$this->storeManager->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']);
$this->resourceModel->expects($this->exactly(1))->method('getIdBySku')->willReturn(null);
$this->productFactory->expects($this->exactly(2))
->method('create')
->willReturn($this->product);
$this->initializationHelper->expects($this->never())->method('initialize');
$this->resourceModel->expects($this->once())->method('validate')->with($this->product)
->willReturn(['error1', 'error2']);
$this->product->expects($this->once())->method('getId')->willReturn(null);
$this->extensibleDataObjectConverter
->expects($this->once())
->method('toNestedArray')
->willReturn($this->productData);
$this->product->method('getSku')->willReturn('simple');
$this->model->save($this->product);
}
/**
* @return void
*/
public function testSaveThrowsTemporaryStateExceptionIfDatabaseConnectionErrorOccurred(): void
{
$this->expectException('Magento\Framework\Exception\TemporaryState\CouldNotSaveException');
$this->expectExceptionMessage('Database connection error');
$this->storeManager->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']);
$this->productFactory->expects($this->any())
->method('create')
->willReturn($this->product);
$this->initializationHelper->expects($this->never())
->method('initialize');
$this->resourceModel->expects($this->once())
->method('validate')
->with($this->product)
->willReturn(true);
$this->resourceModel->expects($this->once())
->method('save')
->with($this->product)
->willThrowException(new ConnectionException('Connection lost'));
$this->extensibleDataObjectConverter
->expects($this->once())
->method('toNestedArray')
->willReturn($this->productData);
$this->product->method('getSku')->willReturn('simple');
$this->model->save($this->product);
}
/**
* @return void
*/
public function testDelete(): void
{
$this->product->expects($this->exactly(2))->method('getSku')->willReturn('product-42');
$this->product->expects($this->exactly(2))->method('getId')->willReturn(42);
$this->resourceModel->expects($this->once())->method('delete')->with($this->product)
->willReturn(true);
$this->assertTrue($this->model->delete($this->product));
}
/**
* @return void
*/
public function testDeleteException(): void
{
$this->expectException('Magento\Framework\Exception\StateException');
$this->expectExceptionMessage('The "product-42" product couldn\'t be removed.');
$this->product->expects($this->exactly(2))->method('getSku')->willReturn('product-42');
$this->product->expects($this->exactly(2))->method('getId')->willReturn(42);
$this->resourceModel->expects($this->once())->method('delete')->with($this->product)
->willThrowException(new \Exception());
$this->model->delete($this->product);
}
/**
* @return void
*/
public function testDeleteById(): void
{
$sku = 'product-42';
$this->productFactory->expects($this->once())->method('create')
->willReturn($this->product);
$this->resourceModel->expects($this->once())->method('getIdBySku')->with($sku)
->willReturn('42');
$this->product->expects($this->once())->method('load')->with('42');
$this->product->expects($this->atLeastOnce())->method('getSku')->willReturn($sku);
$this->assertTrue($this->model->deleteById($sku));
}
/**
* @return void
*/
public function testGetList(): void
{
$searchCriteriaMock = $this->getMockForAbstractClass(SearchCriteriaInterface::class);
$searchCriteriaMock->expects($this->once())
->method('getFilterGroups')
->willReturn([]);
$collectionMock = $this->createMock(Collection::class);
$this->collectionFactory->expects($this->once())->method('create')->willReturn($collectionMock);
$this->product->method('getSku')->willReturn('simple');
$collectionMock->expects($this->once())->method('addAttributeToSelect')->with('*');
$collectionMock->expects($this->exactly(2))->method('joinAttribute')->withConsecutive(
['status', 'catalog_product/status', 'entity_id', null, 'inner'],
['visibility', 'catalog_product/visibility', 'entity_id', null, 'inner']
);
$this->collectionProcessor->expects($this->once())
->method('process')
->with($searchCriteriaMock, $collectionMock);
$collectionMock->expects($this->once())->method('load');
$collectionMock->expects($this->once())->method('addCategoryIds');
$collectionMock->expects($this->atLeastOnce())->method('getItems')->willReturn([$this->product]);
$collectionMock->expects($this->once())->method('getSize')->willReturn(128);
$searchResultsMock = $this->getMockForAbstractClass(ProductSearchResultsInterface::class);
$searchResultsMock->expects($this->once())->method('setSearchCriteria')->with($searchCriteriaMock);
$searchResultsMock->expects($this->once())->method('setItems')->with([$this->product]);
$this->searchResultsFactory->expects($this->once())->method('create')->willReturn($searchResultsMock);
$this->assertEquals($searchResultsMock, $this->model->getList($searchCriteriaMock));
}
/**
* Data provider for the key cache generator.
*
* @return array
*/
public function cacheKeyDataProvider(): array
{
$anyObject = $this->getMockBuilder(\stdClass::class)->addMethods(['getId'])
->disableOriginalConstructor()
->getMock();
$anyObject->expects($this->any())
->method('getId')
->willReturn(123);
return [
[
'identifier' => 'test-sku',
'editMode' => false,
'storeId' => null
],
[
'identifier' => 25,
'editMode' => false,
'storeId' => null
],
[
'identifier' => 25,
'editMode' => true,
'storeId' => null
],
[
'identifier' => 'test-sku',
'editMode' => true,
'storeId' => null
],
[
'identifier' => 25,
'editMode' => true,
'storeId' => $anyObject
],
[
'identifier' => 'test-sku',
'editMode' => true,
'storeId' => $anyObject
],
[
'identifier' => 25,
'editMode' => false,
'storeId' => $anyObject
],
[
'identifier' => 'test-sku',
'editMode' => false,
'storeId' => $anyObject
]
];
}
/**
* @param array $newOptions
*
* @return void
* @dataProvider saveExistingWithOptionsDataProvider
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function testSaveExistingWithOptions(array $newOptions): void
{
$this->storeManager->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']);
$this->resourceModel->expects($this->any())->method('getIdBySku')->willReturn(100);
$this->productFactory->expects($this->any())
->method('create')
->willReturn($this->initializedProduct);
$this->initializationHelper->expects($this->never())->method('initialize');
$this->resourceModel->expects($this->once())->method('validate')->with($this->initializedProduct)
->willReturn(true);
$this->resourceModel->expects($this->once())->method('save')
->with($this->initializedProduct)->willReturn(true);
//option data
$this->productData['options'] = $newOptions;
$this->extensibleDataObjectConverter
->expects($this->once())
->method('toNestedArray')
->willReturn($this->productData);
$this->initializedProduct->expects($this->atLeastOnce())
->method('getSku')->willReturn($this->productData['sku']);
$this->product->expects($this->atLeastOnce())->method('getSku')->willReturn($this->productData['sku']);
$this->assertEquals($this->initializedProduct, $this->model->save($this->product));
}
/**
* @return array
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
public function saveExistingWithOptionsDataProvider(): array
{
$data = [];
//Scenario 1: new options contains one existing option and one new option
//there are two existing options, one will be updated and one will be deleted
$newOptionsData = [
[
'option_id' => 10,
'type' => 'drop_down',
'values' => [
[
'title' => 'DropdownOptions_1',
"option_type_id" => 8, //existing
'price' => 3
],
[ //new option value
'title' => 'DropdownOptions_3',
'price' => 4
]
]
],
[//new option
'type' => 'checkbox',
'values' => [
[
'title' => 'CheckBoxValue2',
'price' => 5
]
]
]
];
/** @var Option|MockObject $existingOption1 */
$existingOption1 = $this->getMockBuilder(Option::class)
->disableOriginalConstructor()
->getMock();
$existingOption1->setData(
[
'option_id' => 10,
'type' => 'drop_down'
]
);
/** @var Value $existingOptionValue1 */
$existingOptionValue1 = $this->getMockBuilder(Value::class)
->disableOriginalConstructor()
->getMock();
$existingOptionValue1->setData(
[
'option_type_id' => '8',
'title' => 'DropdownOptions_1',
'price' => 5
]
);
$existingOptionValue2 = $this->getMockBuilder(Value::class)
->disableOriginalConstructor()
->getMock();
$existingOptionValue2->setData(
[
'option_type_id' => '9',
'title' => 'DropdownOptions_2',
'price' => 6
]
);
$existingOption1->setValues(
[
'8' => $existingOptionValue1,
'9' => $existingOptionValue2
]
);
$existingOption2 = $this->getMockBuilder(Option::class)
->disableOriginalConstructor()
->getMock();
$existingOption2->setData(
[
'option_id' => 11,
'type' => 'drop_down'
]
);
$data['scenario_1'] = [
'new_options' => $newOptionsData,
'existing_options' => [
"10" => $existingOption1,
"11" => $existingOption2,
],
'expected_data' => [
[
'option_id' => 10,
'type' => 'drop_down',
'values' => [
[
'title' => 'DropdownOptions_1',
'option_type_id' => 8,
'price' => 3
],
[
'title' => 'DropdownOptions_3',
"price" => 4
],
[
'option_type_id' => 9,
'title' => 'DropdownOptions_2',
'price' => 6,
'is_delete' => 1
]
]
],
[
'type' => 'checkbox',
'values' => [
[
'title' => 'CheckBoxValue2',
'price' => 5
]
]
],
[
'option_id' => 11,
'type' => 'drop_down',
'values' => [],
'is_delete' => 1
]
]
];
return $data;
}
/**
* @param array $newLinks
* @param array $existingLinks
* @param array $expectedData
*
* @return void
* @dataProvider saveWithLinksDataProvider
*/
public function testSaveWithLinks(array $newLinks, array $existingLinks, array $expectedData): void
{
$this->storeManager->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']);
$this->resourceModel->expects($this->any())->method('getIdBySku')->willReturn(100);
$this->productFactory->expects($this->any())
->method('create')
->willReturn($this->initializedProduct);
$this->initializationHelper->expects($this->never())->method('initialize');
$this->resourceModel->expects($this->once())->method('validate')->with($this->initializedProduct)
->willReturn(true);
$this->resourceModel->expects($this->once())->method('save')
->with($this->initializedProduct)->willReturn(true);
$this->initializedProduct->setData("product_links", $existingLinks);
if (!empty($newLinks)) {
$linkTypes = ['related' => 1, 'upsell' => 4, 'crosssell' => 5, 'associated' => 3];
$this->linkTypeProvider->expects($this->once())
->method('getLinkTypes')
->willReturn($linkTypes);
$this->initializedProduct->setData("ignore_links_flag", false);
$this->resourceModel
->expects($this->any())->method('getProductsIdsBySkus')
->willReturn([$newLinks['linked_product_sku'] => $newLinks['linked_product_sku']]);
$inputLink = $this->objectManager->getObject(Link::class);
$inputLink->setProductSku($newLinks['product_sku']);
$inputLink->setLinkType($newLinks['link_type']);
$inputLink->setLinkedProductSku($newLinks['linked_product_sku']);
$inputLink->setLinkedProductType($newLinks['linked_product_type']);
$inputLink->setPosition($newLinks['position']);
if (isset($newLinks['qty'])) {
$inputLink->setQty($newLinks['qty']);
}
$this->productData['product_links'] = [$inputLink];
$this->initializedProduct->expects($this->any())
->method('getProductLinks')
->willReturn([$inputLink]);
} else {
$this->resourceModel
->expects($this->any())->method('getProductsIdsBySkus')
->willReturn([]);
$this->productData['product_links'] = [];
$this->initializedProduct->setData('ignore_links_flag', true);
$this->initializedProduct->expects($this->never())
->method('getProductLinks')
->willReturn([]);
}
$willReturnArgs = [$this->productData];
if (!empty($newLinks)) {
$willReturnArgs[] = $newLinks;
}
$this->extensibleDataObjectConverter
->method('toNestedArray')
->willReturnOnConsecutiveCalls(...$willReturnArgs);
$outputLinks = [];
if (!empty($expectedData)) {
foreach ($expectedData as $link) {
$outputLink = $this->objectManager->getObject(Link::class);
$outputLink->setProductSku($link['product_sku']);
$outputLink->setLinkType($link['link_type']);
$outputLink->setLinkedProductSku($link['linked_product_sku']);
$outputLink->setLinkedProductType($link['linked_product_type']);
$outputLink->setPosition($link['position']);
if (isset($link['qty'])) {
$outputLink->setQty($link['qty']);
}
$outputLinks[] = $outputLink;
}
}
if (!empty($outputLinks)) {
$this->initializedProduct->expects($this->once())
->method('setProductLinks')
->with($outputLinks);
} else {
$this->initializedProduct->expects($this->never())
->method('setProductLinks');
}
$this->initializedProduct->expects($this->atLeastOnce())
->method('getSku')->willReturn($this->productData['sku']);
$results = $this->model->save($this->initializedProduct);
$this->assertEquals($this->initializedProduct, $results);
}
/**
* @return mixed
*/
public function saveWithLinksDataProvider(): array
{
// Scenario 1
// No existing, new links
$data['scenario_1'] = [
'newLinks' => [
"product_sku" => "Simple Product 1",
"link_type" => "associated",
"linked_product_sku" => "Simple Product 2",
"linked_product_type" => "simple",
"position" => 0,
"qty" => 1
],
'existingLinks' => [],
'expectedData' => [[
"product_sku" => "Simple Product 1",
"link_type" => "associated",
"linked_product_sku" => "Simple Product 2",
"linked_product_type" => "simple",
"position" => 0,
"qty" => 1
]]
];
// Scenario 2
// Existing, no new links
$data['scenario_2'] = [
'newLinks' => [],
'existingLinks' => [
"product_sku" => "Simple Product 1",
"link_type" => "related",
"linked_product_sku" => "Simple Product 2",
"linked_product_type" => "simple",
"position" => 0
],
'expectedData' => []
];
// Scenario 3
// Existing and new links
$data['scenario_3'] = [
'newLinks' => [
"product_sku" => "Simple Product 1",
"link_type" => "related",
"linked_product_sku" => "Simple Product 2",
"linked_product_type" => "simple",
"position" => 0
],
'existingLinks' => [
"product_sku" => "Simple Product 1",
"link_type" => "related",
"linked_product_sku" => "Simple Product 3",
"linked_product_type" => "simple",
"position" => 0
],
'expectedData' => [
[
"product_sku" => "Simple Product 1",
"link_type" => "related",
"linked_product_sku" => "Simple Product 2",
"linked_product_type" => "simple",
"position" => 0
]
]
];
return $data;
}
/**
* @return void
*/
protected function setupProductMocksForSave(): void
{
$this->resourceModel->expects($this->any())->method('getIdBySku')->willReturn(100);
$this->productFactory->expects($this->any())
->method('create')
->willReturn($this->initializedProduct);
$this->initializationHelper->expects($this->never())->method('initialize');
$this->resourceModel->expects($this->once())->method('validate')->with($this->initializedProduct)
->willReturn(true);
$this->resourceModel->expects($this->once())->method('save')
->with($this->initializedProduct)->willReturn(true);
}
/**
* @return void
*/
public function testSaveExistingWithNewMediaGalleryEntries(): void
{
$this->storeManager->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']);
$newEntriesData = [
'images' => [
[
'value_id' => null,
'label' => "label_text",
'position' => 10,
'disabled' => false,
'types' => ['image', 'small_image'],
'content' => [
'data' => [
ImageContentInterface::NAME => 'filename',
ImageContentInterface::TYPE => 'image/jpeg',
ImageContentInterface::BASE64_ENCODED_DATA => 'encoded_content'
]
],
'media_type' => 'media_type'
]
]
];
$this->setupProductMocksForSave();
//media gallery data
$this->productData['media_gallery'] = $newEntriesData;
$this->extensibleDataObjectConverter
->expects($this->once())
->method('toNestedArray')
->willReturn($this->productData);
$this->initializedProduct->setData('media_gallery', $newEntriesData);
$this->initializedProduct->expects($this->any())
->method('getMediaAttributes')
->willReturn(["image" => "imageAttribute", "small_image" => "small_image_attribute"]);
//setup media attribute backend
$mediaTmpPath = '/tmp';
$absolutePath = '/a/b/filename.jpg';
$this->processor->expects($this->once())->method('clearMediaAttribute')
->with($this->initializedProduct, ['image', 'small_image']);
$mediaConfigMock = $this->getMockBuilder(Config::class)
->disableOriginalConstructor()
->getMock();
$mediaConfigMock->expects($this->once())
->method('getTmpMediaShortUrl')
->with($absolutePath)
->willReturn($mediaTmpPath . $absolutePath);
$this->initializedProduct->expects($this->once())
->method('getMediaConfig')
->willReturn($mediaConfigMock);
//verify new entries
$contentDataObject = $this->getMockBuilder(ImageContent::class)
->disableOriginalConstructor()
->setMethods(null)
->getMock();
$this->contentFactory->expects($this->once())
->method('create')
->willReturn($contentDataObject);
$this->imageProcessor->expects($this->once())
->method('processImageContent')
->willReturn($absolutePath);
$imageFileUri = "imageFileUri";
$this->processor->expects($this->once())->method('addImage')
->with($this->initializedProduct, $mediaTmpPath . $absolutePath, ['image', 'small_image'], true, false)
->willReturn($imageFileUri);
$this->processor->expects($this->once())->method('updateImage')
->with(
$this->initializedProduct,
$imageFileUri,
[
'label' => 'label_text',
'position' => 10,
'disabled' => false,
'media_type' => 'media_type',
]
);
$this->initializedProduct->expects($this->atLeastOnce())
->method('getSku')->willReturn($this->productData['sku']);
$this->product->expects($this->atLeastOnce())->method('getSku')->willReturn($this->productData['sku']);
$this->model->save($this->product);
}
/**
* @return array
*/
public function websitesProvider(): array
{
return [
[[1,2,3]]
];
}
/**
* @return void
*/
public function testSaveWithDifferentWebsites(): void
{
$storeMock = $this->getMockForAbstractClass(StoreInterface::class);
$this->resourceModel
->method('getIdBySku')
->willReturnOnConsecutiveCalls(null, 100);
$this->productFactory->expects($this->any())
->method('create')
->willReturn($this->product);
$this->initializationHelper->expects($this->never())->method('initialize');
$this->resourceModel->expects($this->once())->method('validate')->with($this->product)
->willReturn(true);
$this->resourceModel->expects($this->once())->method('save')->with($this->product)->willReturn(true);
$this->extensibleDataObjectConverter
->expects($this->once())
->method('toNestedArray')
->willReturn($this->productData);
$this->storeManager->expects($this->any())
->method('getStore')
->willReturn($storeMock);
$this->storeManager->expects($this->once())
->method('getWebsites')
->willReturn(
[
1 => ['first'],
2 => ['second'],
3 => ['third']
]
);
$this->product->expects($this->once())->method('setWebsiteIds')->willReturn([2,3]);
$this->product->method('getSku')->willReturn('simple');
$this->assertEquals($this->product, $this->model->save($this->product));
}
/**
* @return void
*/
public function testSaveExistingWithMediaGalleryEntries(): void
{
$this->storeManager->expects($this->any())->method('getWebsites')->willReturn([1 => 'default']);
//update one entry, delete one entry
$newEntries = [
[
'value_id' => 5,
"label" => "new_label_text",
'file' => 'filename1',
'position' => 10,
'disabled' => false,
'types' => ['image', 'small_image']
],
];
$existingMediaGallery = [
'images' => [
[
'value_id' => 5,
"label" => "label_text",
'file' => 'filename1',
'position' => 10,
'disabled' => true
],
[
'value_id' => 6, //will be deleted
'file' => 'filename2'
]
]
];
$expectedResult = [
[
'value_id' => 5,
"label" => "new_label_text",
'file' => 'filename1',
'position' => 10,
'disabled' => false,
'types' => ['image', 'small_image']
],
[
'value_id' => 6, //will be deleted
'file' => 'filename2',
'removed' => true
]
];
$this->setupProductMocksForSave();
//media gallery data
$this->productData['media_gallery']['images'] = $newEntries;
$this->extensibleDataObjectConverter
->expects($this->once())
->method('toNestedArray')
->willReturn($this->productData);
$this->initializedProduct->setData('media_gallery', $existingMediaGallery);
$this->initializedProduct->expects($this->any())
->method('getMediaAttributes')
->willReturn(["image" => "filename1", "small_image" => "filename2"]);
$this->processor->expects($this->once())->method('clearMediaAttribute')
->with($this->initializedProduct, ['image', 'small_image']);
$this->processor->expects($this->once())
->method('setMediaAttribute')
->with($this->initializedProduct, ['image', 'small_image'], 'filename1');
$this->initializedProduct->expects($this->atLeastOnce())
->method('getSku')->willReturn($this->productData['sku']);
$this->product->expects($this->atLeastOnce())->method('getSku')->willReturn($this->productData['sku']);
$this->product->expects($this->any())->method('getMediaGalleryEntries')->willReturn(null);
$this->model->save($this->product);
$this->assertEquals($expectedResult, $this->initializedProduct->getMediaGallery('images'));
}
}