ProductTest.php 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. /**
  7. * As far none class is present as separate bundle product,
  8. * this test is clone of \Magento\Catalog\Model\Product with product type "bundle"
  9. */
  10. namespace Magento\Bundle\Model;
  11. use Magento\Bundle\Model\Product\Price;
  12. use Magento\Bundle\Model\Product\Type as BundleType;
  13. use Magento\Catalog\Api\ProductRepositoryInterface;
  14. use Magento\Catalog\Model\Product;
  15. use Magento\Catalog\Model\Product\Attribute\Source\Status;
  16. use Magento\Catalog\Model\Product\Type;
  17. use Magento\Catalog\Model\Product\Visibility;
  18. use Magento\CatalogInventory\Model\Stock;
  19. use Magento\Framework\ObjectManagerInterface;
  20. use Magento\Store\Api\StoreRepositoryInterface;
  21. use Magento\Store\Model\StoreManagerInterface;
  22. use Magento\TestFramework\Entity;
  23. use Magento\TestFramework\Helper\Bootstrap;
  24. /**
  25. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  26. */
  27. class ProductTest extends \PHPUnit\Framework\TestCase
  28. {
  29. /**
  30. * @var Product
  31. */
  32. private $model;
  33. /**
  34. * @var ObjectManagerInterface
  35. */
  36. private $objectManager;
  37. /**
  38. * @return void
  39. */
  40. protected function setUp()
  41. {
  42. $this->objectManager = Bootstrap::getObjectManager();
  43. $this->model = $this->objectManager->create(Product::class);
  44. $this->model->setTypeId(Type::TYPE_BUNDLE);
  45. }
  46. /**
  47. * Tests Retrieve ans set type instance of the product
  48. *
  49. * @see \Magento\Catalog\Model\Product::getTypeInstance
  50. * @see \Magento\Catalog\Model\Product::setTypeInstance
  51. * @return void
  52. */
  53. public function testGetSetTypeInstance()
  54. {
  55. // model getter
  56. $typeInstance = $this->model->getTypeInstance();
  57. $this->assertInstanceOf(BundleType::class, $typeInstance);
  58. $this->assertSame($typeInstance, $this->model->getTypeInstance());
  59. // singleton getter
  60. $otherProduct = $this->objectManager->create(Product::class);
  61. $otherProduct->setTypeId(Type::TYPE_BUNDLE);
  62. $this->assertSame($typeInstance, $otherProduct->getTypeInstance());
  63. // model setter
  64. $customTypeInstance = $this->objectManager->create(BundleType::class);
  65. $this->model->setTypeInstance($customTypeInstance);
  66. $this->assertSame($customTypeInstance, $this->model->getTypeInstance());
  67. }
  68. /**
  69. * @magentoDbIsolation enabled
  70. * @magentoAppIsolation enabled
  71. * @magentoAppArea adminhtml
  72. */
  73. public function testCRUD()
  74. {
  75. $this->model->setTypeId(Type::TYPE_BUNDLE)
  76. ->setAttributeSetId(4)
  77. ->setName('Bundle Product')
  78. ->setSku(uniqid())
  79. ->setPrice(10)
  80. ->setMetaTitle('meta title')
  81. ->setMetaKeyword('meta keyword')
  82. ->setMetaDescription('meta description')
  83. ->setVisibility(Visibility::VISIBILITY_BOTH)
  84. ->setStatus(Status::STATUS_ENABLED);
  85. $crud = new Entity($this->model, ['sku' => uniqid()]);
  86. $crud->testCrud();
  87. }
  88. /**
  89. * Tests Get product price model
  90. *
  91. * @see \Magento\Catalog\Model\Product::getPriceModel
  92. * @return void
  93. */
  94. public function testGetPriceModel()
  95. {
  96. $this->model->setTypeId(Type::TYPE_BUNDLE);
  97. $type = $this->model->getPriceModel();
  98. $this->assertInstanceOf(Price::class, $type);
  99. $this->assertSame($type, $this->model->getPriceModel());
  100. }
  101. /**
  102. * Tests Check is product composite
  103. *
  104. * @see \Magento\Catalog\Model\Product::isComposite
  105. * @return void
  106. */
  107. public function testIsComposite()
  108. {
  109. $this->assertTrue($this->model->isComposite());
  110. }
  111. /**
  112. * Checks a case when bundle product is should be available per multiple stores.
  113. *
  114. * @magentoDataFixture Magento/Bundle/_files/product_with_multiple_options.php
  115. * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php
  116. * @magentoDbIsolation disabled
  117. */
  118. public function testMultipleStores()
  119. {
  120. /** @var ProductRepositoryInterface $productRepository */
  121. $productRepository = $this->objectManager->get(ProductRepositoryInterface::class);
  122. $bundle = $productRepository->get('bundle-product');
  123. /** @var StoreRepositoryInterface $storeRepository */
  124. $storeRepository = $this->objectManager->get(StoreRepositoryInterface::class);
  125. $store = $storeRepository->get('fixture_second_store');
  126. self::assertNotEquals($store->getId(), $bundle->getStoreId());
  127. /** @var StoreManagerInterface $storeManager */
  128. $storeManager = $this->objectManager->get(StoreManagerInterface::class);
  129. $storeManager->setCurrentStore($store->getId());
  130. $bundle->setStoreId($store->getId())
  131. ->setCopyFromView(true);
  132. $updatedBundle = $productRepository->save($bundle);
  133. self::assertEquals($store->getId(), $updatedBundle->getStoreId());
  134. }
  135. /**
  136. * @param float $selectionQty
  137. * @param float $qty
  138. * @param int $isInStock
  139. * @param bool $manageStock
  140. * @param int $backorders
  141. * @param bool $isSalable
  142. * @magentoAppIsolation enabled
  143. * @magentoDataFixture Magento/Bundle/_files/product.php
  144. * @dataProvider stockConfigDataProvider
  145. * @covers \Magento\Catalog\Model\Product::isSalable
  146. */
  147. public function testIsSalable(
  148. float $selectionQty,
  149. float $qty,
  150. int $isInStock,
  151. bool $manageStock,
  152. int $backorders,
  153. bool $isSalable
  154. ) {
  155. $productRepository = $this->objectManager->get(ProductRepositoryInterface::class);
  156. $child = $productRepository->get('simple');
  157. $childStockItem = $child->getExtensionAttributes()->getStockItem();
  158. $childStockItem->setQty($qty);
  159. $childStockItem->setIsInStock($isInStock);
  160. $childStockItem->setUseConfigManageStock(false);
  161. $childStockItem->setManageStock($manageStock);
  162. $childStockItem->setUseConfigBackorders(false);
  163. $childStockItem->setBackorders($backorders);
  164. $productRepository->save($child);
  165. /** @var \Magento\Catalog\Model\Product $bundle */
  166. $bundle = $productRepository->get('bundle-product');
  167. foreach ($bundle->getExtensionAttributes()->getBundleProductOptions() as $productOption) {
  168. foreach ($productOption->getProductLinks() as $productLink) {
  169. $productLink->setCanChangeQuantity(0);
  170. $productLink->setQty($selectionQty);
  171. }
  172. }
  173. $productRepository->save($bundle);
  174. $this->assertEquals($isSalable, $bundle->isSalable());
  175. }
  176. /**
  177. * @return array
  178. */
  179. public function stockConfigDataProvider(): array
  180. {
  181. $qtyVars = [0, 10];
  182. $isInStockVars = [
  183. Stock::STOCK_OUT_OF_STOCK,
  184. Stock::STOCK_IN_STOCK,
  185. ];
  186. $manageStockVars = [false, true];
  187. $backordersVars = [
  188. Stock::BACKORDERS_NO,
  189. Stock::BACKORDERS_YES_NONOTIFY,
  190. Stock::BACKORDERS_YES_NOTIFY,
  191. ];
  192. $selectionQtyVars = [5, 10, 15];
  193. $variations = [];
  194. foreach ($qtyVars as $qty) {
  195. foreach ($isInStockVars as $isInStock) {
  196. foreach ($manageStockVars as $manageStock) {
  197. foreach ($backordersVars as $backorders) {
  198. foreach ($selectionQtyVars as $selectionQty) {
  199. $variationName = "selectionQty: {$selectionQty}"
  200. . " qty: {$qty}"
  201. . " isInStock: {$isInStock}"
  202. . " manageStock: {$manageStock}"
  203. . " backorders: {$backorders}";
  204. $isSalable = $this->checkIsSalable(
  205. $selectionQty,
  206. $qty,
  207. $isInStock,
  208. $manageStock,
  209. $backorders
  210. );
  211. $variations[$variationName] = [
  212. $selectionQty,
  213. $qty,
  214. $isInStock,
  215. $manageStock,
  216. $backorders,
  217. $isSalable
  218. ];
  219. }
  220. }
  221. }
  222. }
  223. }
  224. return $variations;
  225. }
  226. /**
  227. * @param float $selectionQty
  228. * @param float $qty
  229. * @param int $isInStock
  230. * @param bool $manageStock
  231. * @param int $backorders
  232. * @return bool
  233. * @see \Magento\Bundle\Model\ResourceModel\Selection\Collection::addQuantityFilter
  234. */
  235. private function checkIsSalable(
  236. float $selectionQty,
  237. float $qty,
  238. int $isInStock,
  239. bool $manageStock,
  240. int $backorders
  241. ): bool {
  242. return !$manageStock || ($isInStock && ($backorders || $selectionQty <= $qty));
  243. }
  244. }