ValidatorTest.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\SalesRule\Test\Unit\Model;
  7. /**
  8. * Class ValidatorTest
  9. * @@SuppressWarnings(PHPMD.CouplingBetweenObjects)
  10. */
  11. class ValidatorTest extends \PHPUnit\Framework\TestCase
  12. {
  13. /**
  14. * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager
  15. */
  16. protected $helper;
  17. /**
  18. * @var \Magento\SalesRule\Model\Validator
  19. */
  20. protected $model;
  21. /**
  22. * @var \Magento\Quote\Model\Quote\Item|\PHPUnit_Framework_MockObject_MockObject
  23. */
  24. protected $item;
  25. /**
  26. * @var \Magento\Quote\Model\Quote\Address|\PHPUnit_Framework_MockObject_MockObject
  27. */
  28. protected $addressMock;
  29. /**
  30. * @var \Magento\SalesRule\Model\RulesApplier|\PHPUnit_Framework_MockObject_MockObject
  31. */
  32. protected $rulesApplier;
  33. /**
  34. * @var \Magento\SalesRule\Model\Validator\Pool|\PHPUnit_Framework_MockObject_MockObject
  35. */
  36. protected $validators;
  37. /**
  38. * @var \Magento\SalesRule\Model\Utility|\PHPUnit_Framework_MockObject_MockObject
  39. */
  40. protected $utility;
  41. /**
  42. * @var \Magento\SalesRule\Model\ResourceModel\Rule\Collection|\PHPUnit_Framework_MockObject_MockObject
  43. */
  44. protected $ruleCollection;
  45. /**
  46. * @var \Magento\Catalog\Helper\Data|\PHPUnit_Framework_MockObject_MockObject
  47. */
  48. protected $catalogData;
  49. /**
  50. * @var \Magento\Framework\Message\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject
  51. */
  52. protected $messageManager;
  53. protected function setUp()
  54. {
  55. $this->helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
  56. $this->rulesApplier = $this->createPartialMock(
  57. \Magento\SalesRule\Model\RulesApplier::class,
  58. ['setAppliedRuleIds', 'applyRules', 'addDiscountDescription', '__wakeup']
  59. );
  60. $this->addressMock = $this->getMockBuilder(\Magento\Quote\Model\Quote\Address::class)
  61. ->disableOriginalConstructor()
  62. ->setMethods(
  63. [
  64. 'getShippingAmountForDiscount',
  65. 'getQuote',
  66. 'getCustomAttributesCodes',
  67. 'setCartFixedRules'
  68. ]
  69. )
  70. ->getMock();
  71. /** @var \Magento\Quote\Model\Quote\Item\AbstractItem|\PHPUnit_Framework_MockObject_MockObject $item */
  72. $this->item = $this->createPartialMock(
  73. \Magento\Quote\Model\Quote\Item::class,
  74. ['__wakeup', 'getAddress', 'getParentItemId']
  75. );
  76. $this->item->expects($this->any())
  77. ->method('getAddress')
  78. ->willReturn($this->addressMock);
  79. $context = $this->createMock(\Magento\Framework\Model\Context::class);
  80. $registry = $this->createMock(\Magento\Framework\Registry::class);
  81. $this->catalogData = $this->createMock(\Magento\Catalog\Helper\Data::class);
  82. $this->utility = $this->createMock(\Magento\SalesRule\Model\Utility::class);
  83. $this->validators = $this->createPartialMock(\Magento\SalesRule\Model\Validator\Pool::class, ['getValidators']);
  84. $this->messageManager = $this->createMock(\Magento\Framework\Message\Manager::class);
  85. $this->ruleCollection = $this->getMockBuilder(\Magento\SalesRule\Model\ResourceModel\Rule\Collection::class)
  86. ->disableOriginalConstructor()
  87. ->getMock();
  88. $ruleCollectionFactoryMock = $this->prepareRuleCollectionMock($this->ruleCollection);
  89. /** @var \Magento\SalesRule\Model\Validator|\PHPUnit_Framework_MockObject_MockObject $validator */
  90. $this->model = $this->helper->getObject(
  91. \Magento\SalesRule\Model\Validator::class,
  92. [
  93. 'context' => $context,
  94. 'registry' => $registry,
  95. 'collectionFactory' => $ruleCollectionFactoryMock,
  96. 'catalogData' => $this->catalogData,
  97. 'utility' => $this->utility,
  98. 'rulesApplier' => $this->rulesApplier,
  99. 'validators' => $this->validators,
  100. 'messageManager' => $this->messageManager
  101. ]
  102. );
  103. $this->model->setWebsiteId(1);
  104. $this->model->setCustomerGroupId(2);
  105. $this->model->setCouponCode('code');
  106. $this->ruleCollection->expects($this->any())
  107. ->method('setValidationFilter')
  108. ->with(
  109. $this->model->getWebsiteId(),
  110. $this->model->getCustomerGroupId(),
  111. $this->model->getCouponCode(),
  112. null,
  113. $this->addressMock
  114. )
  115. ->willReturnSelf();
  116. }
  117. /**
  118. * @return \Magento\Quote\Model\Quote\Item|\PHPUnit_Framework_MockObject_MockObject
  119. */
  120. protected function getQuoteItemMock()
  121. {
  122. $fixturePath = __DIR__ . '/_files/';
  123. $itemDownloadable = $this->createPartialMock(
  124. \Magento\Quote\Model\Quote\Item::class,
  125. ['getAddress', '__wakeup']
  126. );
  127. $itemDownloadable->expects($this->any())->method('getAddress')->will($this->returnValue($this->addressMock));
  128. $itemSimple = $this->createPartialMock(\Magento\Quote\Model\Quote\Item::class, ['getAddress', '__wakeup']);
  129. $itemSimple->expects($this->any())->method('getAddress')->will($this->returnValue($this->addressMock));
  130. /** @var $quote \Magento\Quote\Model\Quote */
  131. $quote = $this->createPartialMock(\Magento\Quote\Model\Quote::class, ['getStoreId', '__wakeup']);
  132. $quote->expects($this->any())->method('getStoreId')->will($this->returnValue(1));
  133. $itemData = include $fixturePath . 'quote_item_downloadable.php';
  134. $itemDownloadable->addData($itemData);
  135. $quote->addItem($itemDownloadable);
  136. $itemData = include $fixturePath . 'quote_item_simple.php';
  137. $itemSimple->addData($itemData);
  138. $quote->addItem($itemSimple);
  139. return $itemDownloadable;
  140. }
  141. public function testCanApplyRules()
  142. {
  143. $this->model->init(
  144. $this->model->getWebsiteId(),
  145. $this->model->getCustomerGroupId(),
  146. $this->model->getCouponCode()
  147. );
  148. $item = $this->getQuoteItemMock();
  149. $rule = $this->createMock(\Magento\SalesRule\Model\Rule::class);
  150. $actionsCollection = $this->createPartialMock(\Magento\Rule\Model\Action\Collection::class, ['validate']);
  151. $actionsCollection->expects($this->any())
  152. ->method('validate')
  153. ->with($item)
  154. ->willReturn(true);
  155. $rule->expects($this->any())
  156. ->method('getActions')
  157. ->willReturn($actionsCollection);
  158. $iterator = new \ArrayIterator([$rule]);
  159. $this->ruleCollection->expects($this->any())
  160. ->method('getIterator')
  161. ->willReturn($iterator);
  162. $this->utility->expects($this->any())
  163. ->method('canProcessRule')
  164. ->with($rule, $this->anything())
  165. ->willReturn(true);
  166. $quote = $item->getQuote();
  167. $quote->setItemsQty(2);
  168. $quote->setVirtualItemsQty(1);
  169. $this->assertTrue($this->model->canApplyRules($item));
  170. $quote->setItemsQty(2);
  171. $quote->setVirtualItemsQty(2);
  172. $this->assertTrue($this->model->canApplyRules($item));
  173. }
  174. public function testProcess()
  175. {
  176. $negativePrice = -1;
  177. $this->item->setDiscountCalculationPrice($negativePrice);
  178. $this->item->setData('calculation_price', $negativePrice);
  179. $this->rulesApplier->expects($this->never())->method('applyRules');
  180. $this->model->init(
  181. $this->model->getWebsiteId(),
  182. $this->model->getCustomerGroupId(),
  183. $this->model->getCouponCode()
  184. );
  185. $this->model->process($this->item);
  186. }
  187. public function testProcessWhenItemPriceIsNegativeDiscountsAreZeroed()
  188. {
  189. $negativePrice = -1;
  190. $nonZeroDiscount = 123;
  191. $this->model->init(
  192. $this->model->getWebsiteId(),
  193. $this->model->getCustomerGroupId(),
  194. $this->model->getCouponCode()
  195. );
  196. $this->item->setDiscountCalculationPrice($negativePrice);
  197. $this->item->setData('calculation_price', $negativePrice);
  198. $this->item->setDiscountAmount($nonZeroDiscount);
  199. $this->item->setBaseDiscountAmount($nonZeroDiscount);
  200. $this->item->setDiscountPercent($nonZeroDiscount);
  201. $this->model->process($this->item);
  202. $this->assertEquals(0, $this->item->getDiscountAmount());
  203. $this->assertEquals(0, $this->item->getBaseDiscountAmount());
  204. $this->assertEquals(0, $this->item->getDiscountPercent());
  205. }
  206. public function testApplyRulesThatAppliedRuleIdsAreCollected()
  207. {
  208. $positivePrice = 1;
  209. $ruleId1 = 123;
  210. $ruleId2 = 234;
  211. $expectedRuleIds = [$ruleId1 => $ruleId1, $ruleId2 => $ruleId2];
  212. $this->model->init(
  213. $this->model->getWebsiteId(),
  214. $this->model->getCustomerGroupId(),
  215. $this->model->getCouponCode()
  216. );
  217. $this->item->setDiscountCalculationPrice($positivePrice);
  218. $this->item->setData('calculation_price', $positivePrice);
  219. $this->model->setSkipActionsValidation(true);
  220. $this->rulesApplier->expects($this->once())
  221. ->method('applyRules')
  222. ->with(
  223. $this->equalTo($this->item),
  224. $this->equalTo($this->ruleCollection),
  225. $this->anything(),
  226. $this->anything()
  227. )
  228. ->will($this->returnValue($expectedRuleIds));
  229. $this->rulesApplier->expects($this->once())
  230. ->method('setAppliedRuleIds')
  231. ->with(
  232. $this->anything(),
  233. $expectedRuleIds
  234. );
  235. $this->model->process($this->item);
  236. }
  237. public function testInit()
  238. {
  239. $this->assertInstanceOf(
  240. \Magento\SalesRule\Model\Validator::class,
  241. $this->model->init(
  242. $this->model->getWebsiteId(),
  243. $this->model->getCustomerGroupId(),
  244. $this->model->getCouponCode()
  245. )
  246. );
  247. }
  248. public function testCanApplyDiscount()
  249. {
  250. $validator = $this->getMockBuilder(\Magento\Framework\Validator\AbstractValidator::class)
  251. ->setMethods(['isValid'])
  252. ->disableOriginalConstructor()
  253. ->getMockForAbstractClass();
  254. $this->validators->expects($this->any())
  255. ->method('getValidators')
  256. ->with('discount')
  257. ->willReturn([$validator]);
  258. $validator->expects($this->any())
  259. ->method('isValid')
  260. ->with($this->item)
  261. ->willReturn(false);
  262. $this->model->init(
  263. $this->model->getWebsiteId(),
  264. $this->model->getCustomerGroupId(),
  265. $this->model->getCouponCode()
  266. );
  267. $this->assertFalse($this->model->canApplyDiscount($this->item));
  268. }
  269. public function testInitTotalsCanApplyDiscount()
  270. {
  271. $rule = $this->createPartialMock(
  272. \Magento\SalesRule\Model\Rule::class,
  273. ['getSimpleAction', 'getActions', 'getId']
  274. );
  275. $item1 = $this->getMockForAbstractClass(
  276. \Magento\Quote\Model\Quote\Item\AbstractItem::class,
  277. [],
  278. '',
  279. false,
  280. true,
  281. true,
  282. [
  283. '__clone',
  284. 'getDiscountCalculationPrice',
  285. 'getBaseDiscountCalculationPrice',
  286. 'getCalculationPrice',
  287. 'getParentItemId'
  288. ]
  289. );
  290. $item2 = clone $item1;
  291. $items = [$item1, $item2];
  292. $rule->expects($this->any())
  293. ->method('getSimpleAction')
  294. ->willReturn(\Magento\SalesRule\Model\Rule::CART_FIXED_ACTION);
  295. $iterator = new \ArrayIterator([$rule]);
  296. $this->ruleCollection->expects($this->once())->method('getIterator')->willReturn($iterator);
  297. $validator = $this->getMockBuilder(\Magento\Framework\Validator\AbstractValidator::class)
  298. ->setMethods(['isValid'])
  299. ->disableOriginalConstructor()
  300. ->getMockForAbstractClass();
  301. $this->validators->expects($this->atLeastOnce())->method('getValidators')->with('discount')
  302. ->willReturn([$validator]);
  303. $validator->expects($this->at(0))->method('isValid')->with($item1)->willReturn(false);
  304. $validator->expects($this->at(1))->method('isValid')->with($item2)->willReturn(true);
  305. $item1->expects($this->any())->method('getParentItemId')->willReturn(false);
  306. $item1->expects($this->never())->method('getDiscountCalculationPrice');
  307. $item1->expects($this->never())->method('getBaseDiscountCalculationPrice');
  308. $item2->expects($this->any())->method('getParentItemId')->willReturn(false);
  309. $item2->expects($this->any())->method('getDiscountCalculationPrice')->willReturn(50);
  310. $item2->expects($this->once())->method('getBaseDiscountCalculationPrice')->willReturn(50);
  311. $this->utility->expects($this->once())->method('getItemQty')->willReturn(1);
  312. $this->utility->expects($this->any())->method('canProcessRule')->willReturn(true);
  313. $actionsCollection = $this->createPartialMock(\Magento\Rule\Model\Action\Collection::class, ['validate']);
  314. $actionsCollection->expects($this->at(0))->method('validate')->with($item1)->willReturn(true);
  315. $actionsCollection->expects($this->at(1))->method('validate')->with($item2)->willReturn(true);
  316. $rule->expects($this->any())->method('getActions')->willReturn($actionsCollection);
  317. $rule->expects($this->any())->method('getId')->willReturn(1);
  318. $this->model->init(
  319. $this->model->getWebsiteId(),
  320. $this->model->getCustomerGroupId(),
  321. $this->model->getCouponCode()
  322. );
  323. $this->model->initTotals($items, $this->addressMock);
  324. $this->assertArrayHasKey('items_price', $this->model->getRuleItemTotalsInfo($rule->getId()));
  325. $this->assertArrayHasKey('base_items_price', $this->model->getRuleItemTotalsInfo($rule->getId()));
  326. $this->assertArrayHasKey('items_count', $this->model->getRuleItemTotalsInfo($rule->getId()));
  327. $this->assertEquals(1, $this->model->getRuleItemTotalsInfo($rule->getId())['items_count']);
  328. }
  329. public function testInitTotalsNoItems()
  330. {
  331. $address = $this->createMock(\Magento\Quote\Model\Quote\Address::class);
  332. $this->item->expects($this->never())
  333. ->method('getParentItemId');
  334. $this->model->init(
  335. $this->model->getWebsiteId(),
  336. $this->model->getCustomerGroupId(),
  337. $this->model->getCouponCode()
  338. );
  339. $this->model->initTotals([], $address);
  340. }
  341. /**
  342. * @param $ruleCollection
  343. * @return \PHPUnit_Framework_MockObject_MockObject
  344. */
  345. protected function prepareRuleCollectionMock($ruleCollection)
  346. {
  347. $this->ruleCollection->expects($this->any())
  348. ->method('addFieldToFilter')
  349. ->with('is_active', 1)
  350. ->will($this->returnSelf());
  351. $this->ruleCollection->expects($this->any())
  352. ->method('load')
  353. ->will($this->returnSelf());
  354. $ruleCollectionFactoryMock =
  355. $this->getMockBuilder(\Magento\SalesRule\Model\ResourceModel\Rule\CollectionFactory::class)
  356. ->disableOriginalConstructor()
  357. ->setMethods(['create'])
  358. ->getMock();
  359. $ruleCollectionFactoryMock->expects($this->any())
  360. ->method('create')
  361. ->will($this->returnValue($ruleCollection));
  362. return $ruleCollectionFactoryMock;
  363. }
  364. public function testProcessShippingAmountNoRules()
  365. {
  366. $iterator = new \ArrayIterator([]);
  367. $this->ruleCollection->expects($this->any())
  368. ->method('getIterator')
  369. ->willReturn($iterator);
  370. $this->model->init(
  371. $this->model->getWebsiteId(),
  372. $this->model->getCustomerGroupId(),
  373. $this->model->getCouponCode()
  374. );
  375. $this->assertInstanceOf(
  376. \Magento\SalesRule\Model\Validator::class,
  377. $this->model->processShippingAmount($this->setupAddressMock())
  378. );
  379. }
  380. public function testProcessShippingAmountProcessDisabled()
  381. {
  382. $ruleMock = $this->getMockBuilder(\Magento\SalesRule\Model\Rule::class)
  383. ->disableOriginalConstructor()
  384. ->setMethods([])
  385. ->getMock();
  386. $iterator = new \ArrayIterator([$ruleMock]);
  387. $this->ruleCollection->expects($this->any())
  388. ->method('getIterator')
  389. ->willReturn($iterator);
  390. $this->model->init(
  391. $this->model->getWebsiteId(),
  392. $this->model->getCustomerGroupId(),
  393. $this->model->getCouponCode()
  394. );
  395. $this->assertInstanceOf(
  396. \Magento\SalesRule\Model\Validator::class,
  397. $this->model->processShippingAmount($this->setupAddressMock())
  398. );
  399. }
  400. /**
  401. * @param string $action
  402. * @dataProvider dataProviderActions
  403. */
  404. public function testProcessShippingAmountActions($action)
  405. {
  406. $discountAmount = 50;
  407. $ruleMock = $this->getMockBuilder(\Magento\SalesRule\Model\Rule::class)
  408. ->disableOriginalConstructor()
  409. ->setMethods(['getApplyToShipping', 'getSimpleAction', 'getDiscountAmount'])
  410. ->getMock();
  411. $ruleMock->expects($this->any())
  412. ->method('getApplyToShipping')
  413. ->willReturn(true);
  414. $ruleMock->expects($this->any())
  415. ->method('getDiscountAmount')
  416. ->willReturn($discountAmount);
  417. $ruleMock->expects($this->any())
  418. ->method('getSimpleAction')
  419. ->willReturn($action);
  420. $iterator = new \ArrayIterator([$ruleMock]);
  421. $this->ruleCollection->expects($this->any())
  422. ->method('getIterator')
  423. ->willReturn($iterator);
  424. $this->utility->expects($this->any())
  425. ->method('canProcessRule')
  426. ->willReturn(true);
  427. $this->model->init(
  428. $this->model->getWebsiteId(),
  429. $this->model->getCustomerGroupId(),
  430. $this->model->getCouponCode()
  431. );
  432. $this->assertInstanceOf(
  433. \Magento\SalesRule\Model\Validator::class,
  434. $this->model->processShippingAmount($this->setupAddressMock(5))
  435. );
  436. }
  437. /**
  438. * @return array
  439. */
  440. public static function dataProviderActions()
  441. {
  442. return [
  443. [\Magento\SalesRule\Model\Rule::TO_PERCENT_ACTION],
  444. [\Magento\SalesRule\Model\Rule::BY_PERCENT_ACTION],
  445. [\Magento\SalesRule\Model\Rule::TO_FIXED_ACTION],
  446. [\Magento\SalesRule\Model\Rule::BY_FIXED_ACTION],
  447. [\Magento\SalesRule\Model\Rule::CART_FIXED_ACTION],
  448. ];
  449. }
  450. /**
  451. * @param null|int $shippingAmount
  452. * @return \PHPUnit_Framework_MockObject_MockObject
  453. */
  454. protected function setupAddressMock($shippingAmount = null)
  455. {
  456. $storeMock = $this->getMockBuilder(\Magento\Store\Model\Store::class)
  457. ->disableOriginalConstructor()
  458. ->setMethods([])
  459. ->getMock();
  460. $quoteMock = $this->getMockBuilder(\Magento\Quote\Model\Quote::class)
  461. ->disableOriginalConstructor()
  462. ->setMethods(['setAppliedRuleIds', 'getStore'])
  463. ->getMock();
  464. $quoteMock->expects($this->any())
  465. ->method('getStore')
  466. ->willReturn($storeMock);
  467. $quoteMock->expects($this->any())
  468. ->method('setAppliedRuleIds')
  469. ->willReturnSelf();
  470. $this->addressMock->expects($this->any())
  471. ->method('getShippingAmountForDiscount')
  472. ->willReturn($shippingAmount);
  473. $this->addressMock->expects($this->any())
  474. ->method('getQuote')
  475. ->willReturn($quoteMock);
  476. $this->addressMock->expects($this->any())
  477. ->method('getCustomAttributesCodes')
  478. ->willReturn([]);
  479. return $this->addressMock;
  480. }
  481. public function testReset()
  482. {
  483. $this->utility->expects($this->once())
  484. ->method('resetRoundingDeltas');
  485. $quoteMock = $this->getMockBuilder(\Magento\Quote\Model\Quote::class)
  486. ->disableOriginalConstructor()
  487. ->getMock();
  488. $addressMock = $this->getMockBuilder(\Magento\Quote\Model\Quote\Address::class)
  489. ->disableOriginalConstructor()
  490. ->getMock();
  491. $addressMock->expects($this->once())
  492. ->method('getQuote')
  493. ->willReturn($quoteMock);
  494. $this->model->init(
  495. $this->model->getWebsiteId(),
  496. $this->model->getCustomerGroupId(),
  497. $this->model->getCouponCode()
  498. );
  499. $this->assertInstanceOf(\Magento\SalesRule\Model\Validator::class, $this->model->reset($addressMock));
  500. }
  501. }