UtilityTest.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510
  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 UtilityTest
  9. *
  10. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  11. */
  12. class UtilityTest extends \PHPUnit\Framework\TestCase
  13. {
  14. /**
  15. * @var \Magento\SalesRule\Model\ResourceModel\Coupon\UsageFactory | \PHPUnit_Framework_MockObject_MockObject
  16. */
  17. protected $usageFactory;
  18. /**
  19. * @var \Magento\SalesRule\Model\CouponFactory | \PHPUnit_Framework_MockObject_MockObject
  20. */
  21. protected $couponFactory;
  22. /**
  23. * @var \Magento\SalesRule\Model\Coupon | \PHPUnit_Framework_MockObject_MockObject
  24. */
  25. protected $coupon;
  26. /**
  27. * @var \Magento\Quote\Model\Quote | \PHPUnit_Framework_MockObject_MockObject
  28. */
  29. protected $quote;
  30. /**
  31. * @var \Magento\SalesRule\Model\Rule\CustomerFactory | \PHPUnit_Framework_MockObject_MockObject
  32. */
  33. protected $customerFactory;
  34. /**
  35. * @var \Magento\SalesRule\Model\Rule\Customer | \PHPUnit_Framework_MockObject_MockObject
  36. */
  37. protected $customer;
  38. /**
  39. * @var \Magento\Quote\Model\Quote\Address | \PHPUnit_Framework_MockObject_MockObject
  40. */
  41. protected $address;
  42. /**
  43. * @var \Magento\SalesRule\Model\Rule | \PHPUnit_Framework_MockObject_MockObject
  44. */
  45. protected $rule;
  46. /**
  47. * @var \Magento\Framework\DataObjectFactory | \PHPUnit_Framework_MockObject_MockObject
  48. */
  49. protected $objectFactory;
  50. /**
  51. * @var \Magento\Quote\Model\Quote\Item\AbstractItem | \PHPUnit_Framework_MockObject_MockObject
  52. */
  53. protected $item;
  54. /**
  55. * @var \Magento\SalesRule\Model\Utility
  56. */
  57. protected $utility;
  58. /**
  59. * @var \Magento\Framework\Pricing\PriceCurrencyInterface | \PHPUnit_Framework_MockObject_MockObject
  60. */
  61. protected $priceCurrency;
  62. protected function setUp()
  63. {
  64. $this->usageFactory = $this->createPartialMock(
  65. \Magento\SalesRule\Model\ResourceModel\Coupon\UsageFactory::class,
  66. ['create']
  67. );
  68. $this->couponFactory = $this->createPartialMock(\Magento\SalesRule\Model\CouponFactory::class, ['create']);
  69. $this->objectFactory = $this->createPartialMock(\Magento\Framework\DataObjectFactory::class, ['create']);
  70. $this->customerFactory = $this->createPartialMock(
  71. \Magento\SalesRule\Model\Rule\CustomerFactory::class,
  72. ['create']
  73. );
  74. $this->coupon = $this->createPartialMock(
  75. \Magento\SalesRule\Model\Coupon::class,
  76. [
  77. 'load',
  78. 'getId',
  79. 'getUsageLimit',
  80. 'getTimesUsed',
  81. 'getUsagePerCustomer',
  82. '__wakeup'
  83. ]
  84. );
  85. $this->quote = $this->createPartialMock(\Magento\Quote\Model\Quote::class, ['__wakeup', 'getStore']);
  86. $this->customer = $this->createPartialMock(
  87. \Magento\SalesRule\Model\Rule\Customer::class,
  88. ['loadByCustomerRule', '__wakeup']
  89. );
  90. $this->rule = $this->createPartialMock(\Magento\SalesRule\Model\Rule::class, [
  91. 'hasIsValidForAddress',
  92. 'getIsValidForAddress',
  93. 'setIsValidForAddress',
  94. '__wakeup',
  95. 'validate',
  96. 'afterLoad',
  97. 'getDiscountQty'
  98. ]);
  99. $this->address = $this->createPartialMock(\Magento\Quote\Model\Quote\Address::class, [
  100. 'isObjectNew',
  101. 'getQuote',
  102. 'setIsValidForAddress',
  103. '__wakeup',
  104. 'validate',
  105. 'afterLoad'
  106. ]);
  107. $this->address->setQuote($this->quote);
  108. $this->item = $this->createPartialMock(\Magento\Quote\Model\Quote\Item\AbstractItem::class, [
  109. 'getDiscountCalculationPrice',
  110. 'getCalculationPrice',
  111. 'getBaseDiscountCalculationPrice',
  112. 'getBaseCalculationPrice',
  113. 'getQuote',
  114. 'getAddress',
  115. 'getOptionByCode',
  116. 'getTotalQty',
  117. '__wakeup'
  118. ]);
  119. $this->priceCurrency = $this->getMockBuilder(\Magento\Framework\Pricing\PriceCurrencyInterface::class)
  120. ->getMock();
  121. $this->utility = new \Magento\SalesRule\Model\Utility(
  122. $this->usageFactory,
  123. $this->couponFactory,
  124. $this->customerFactory,
  125. $this->objectFactory,
  126. $this->priceCurrency
  127. );
  128. }
  129. /**
  130. * Check rule for specific address
  131. */
  132. public function testCanProcessRuleValidAddress()
  133. {
  134. $this->rule->expects($this->once())
  135. ->method('hasIsValidForAddress')
  136. ->with($this->address)
  137. ->will($this->returnValue(true));
  138. $this->rule->expects($this->once())
  139. ->method('getIsValidForAddress')
  140. ->with($this->address)
  141. ->will($this->returnValue(true));
  142. $this->address->expects($this->once())
  143. ->method('isObjectNew')
  144. ->will($this->returnValue(false));
  145. $this->assertTrue($this->utility->canProcessRule($this->rule, $this->address));
  146. }
  147. /**
  148. * Check coupon entire usage limit
  149. */
  150. public function testCanProcessRuleCouponUsageLimitFail()
  151. {
  152. $couponCode = 111;
  153. $couponId = 4;
  154. $quoteId = 4;
  155. $usageLimit = 1;
  156. $timesUsed = 2;
  157. $this->rule->setCouponType(\Magento\SalesRule\Model\Rule::COUPON_TYPE_SPECIFIC);
  158. $this->quote->setCouponCode($couponCode);
  159. $this->quote->setId($quoteId);
  160. $this->address->expects($this->once())
  161. ->method('getQuote')
  162. ->will($this->returnValue($this->quote));
  163. $this->coupon->expects($this->atLeastOnce())
  164. ->method('getUsageLimit')
  165. ->will($this->returnValue($usageLimit));
  166. $this->coupon->expects($this->once())
  167. ->method('getTimesUsed')
  168. ->will($this->returnValue($timesUsed));
  169. $this->coupon->expects($this->once())
  170. ->method('load')
  171. ->with($couponCode, 'code')
  172. ->will($this->returnSelf());
  173. $this->couponFactory->expects($this->once())
  174. ->method('create')
  175. ->will($this->returnValue($this->coupon));
  176. $this->coupon->expects($this->once())
  177. ->method('getId')
  178. ->will($this->returnValue($couponId));
  179. $this->assertFalse($this->utility->canProcessRule($this->rule, $this->address));
  180. }
  181. /**
  182. * Check coupon per customer usage limit
  183. */
  184. public function testCanProcessRuleCouponUsagePerCustomerFail()
  185. {
  186. $couponCode = 111;
  187. $couponId = 4;
  188. $quoteId = 4;
  189. $customerId = 1;
  190. $usageLimit = 1;
  191. $timesUsed = 2;
  192. $this->rule->setCouponType(\Magento\SalesRule\Model\Rule::COUPON_TYPE_SPECIFIC);
  193. $this->quote->setCouponCode($couponCode);
  194. $this->quote->setId($quoteId);
  195. $this->quote->setCustomerId($customerId);
  196. $this->address->expects($this->atLeastOnce())
  197. ->method('getQuote')
  198. ->will($this->returnValue($this->quote));
  199. $this->coupon->expects($this->atLeastOnce())
  200. ->method('getUsagePerCustomer')
  201. ->will($this->returnValue($usageLimit));
  202. $this->coupon->expects($this->once())
  203. ->method('load')
  204. ->with($couponCode, 'code')
  205. ->will($this->returnSelf());
  206. $this->coupon->expects($this->atLeastOnce())
  207. ->method('getId')
  208. ->will($this->returnValue($couponId));
  209. $this->couponFactory->expects($this->once())
  210. ->method('create')
  211. ->will($this->returnValue($this->coupon));
  212. $couponUsage = new \Magento\Framework\DataObject();
  213. $this->objectFactory->expects($this->once())
  214. ->method('create')
  215. ->will($this->returnValue($couponUsage));
  216. $couponUsageModel = $this->createMock(\Magento\SalesRule\Model\ResourceModel\Coupon\Usage::class);
  217. $couponUsage->setData(['coupon_id' => $couponId, 'times_used' => $timesUsed]);
  218. $this->usageFactory->expects($this->once())
  219. ->method('create')
  220. ->will($this->returnValue($couponUsageModel));
  221. $this->assertFalse($this->utility->canProcessRule($this->rule, $this->address));
  222. }
  223. /**
  224. * Check rule per customer usage limit
  225. */
  226. public function testCanProcessRuleUsagePerCustomer()
  227. {
  228. $customerId = 1;
  229. $usageLimit = 1;
  230. $timesUsed = 2;
  231. $ruleId = 4;
  232. $this->rule->setId($ruleId);
  233. $this->rule->setUsesPerCustomer($usageLimit);
  234. $this->quote->setCustomerId($customerId);
  235. $this->address->expects($this->atLeastOnce())
  236. ->method('getQuote')
  237. ->will($this->returnValue($this->quote));
  238. $this->customer->setId($customerId);
  239. $this->customer->setTimesUsed($timesUsed);
  240. $this->customerFactory->expects($this->once())
  241. ->method('create')
  242. ->will($this->returnValue($this->customer));
  243. $this->assertFalse($this->utility->canProcessRule($this->rule, $this->address));
  244. }
  245. /**
  246. * Quote does not meet rule's conditions
  247. */
  248. public function testCanProcessRuleInvalidConditions()
  249. {
  250. $this->rule->setCouponType(\Magento\SalesRule\Model\Rule::COUPON_TYPE_NO_COUPON);
  251. $this->assertFalse($this->utility->canProcessRule($this->rule, $this->address));
  252. }
  253. /**
  254. * Quote does not meet rule's conditions
  255. */
  256. public function testCanProcessRule()
  257. {
  258. $this->rule->setCouponType(\Magento\SalesRule\Model\Rule::COUPON_TYPE_NO_COUPON);
  259. $this->rule->expects($this->once())
  260. ->method('validate')
  261. ->will($this->returnValue(true));
  262. $this->assertTrue($this->utility->canProcessRule($this->rule, $this->address));
  263. }
  264. public function testGetItemPrice()
  265. {
  266. $price = $this->getItemPrice();
  267. $this->assertEquals($price, $this->utility->getItemPrice($this->item));
  268. }
  269. public function testGetItemPriceNull()
  270. {
  271. $price = 4;
  272. $this->item->expects($this->once())
  273. ->method('getDiscountCalculationPrice')
  274. ->will($this->returnValue($price));
  275. $this->item->expects($this->once())
  276. ->method('getCalculationPrice')
  277. ->will($this->returnValue(null));
  278. $this->assertEquals($price, $this->utility->getItemPrice($this->item));
  279. }
  280. public function testGetItemBasePrice()
  281. {
  282. $price = $this->getItemBasePrice();
  283. $this->assertEquals($price, $this->utility->getItemBasePrice($this->item));
  284. }
  285. public function testGetBaseItemPriceCalculation()
  286. {
  287. $calcPrice = 5;
  288. $this->item->expects($this->once())
  289. ->method('getDiscountCalculationPrice')
  290. ->will($this->returnValue(null));
  291. $this->item->expects($this->any())
  292. ->method('getBaseCalculationPrice')
  293. ->will($this->returnValue($calcPrice));
  294. $this->assertEquals($calcPrice, $this->utility->getItemBasePrice($this->item));
  295. }
  296. public function testGetItemQtyMin()
  297. {
  298. $qty = 7;
  299. $discountQty = 4;
  300. $this->item->expects($this->once())
  301. ->method('getTotalQty')
  302. ->will($this->returnValue($qty));
  303. $this->rule->expects($this->once())
  304. ->method('getDiscountQty')
  305. ->will($this->returnValue($discountQty));
  306. $this->assertEquals(min($discountQty, $qty), $this->utility->getItemQty($this->item, $this->rule));
  307. }
  308. public function testGetItemQty()
  309. {
  310. $qty = 7;
  311. $this->item->expects($this->once())
  312. ->method('getTotalQty')
  313. ->will($this->returnValue($qty));
  314. $this->rule->expects($this->once())
  315. ->method('getDiscountQty')
  316. ->will($this->returnValue(null));
  317. $this->assertEquals($qty, $this->utility->getItemQty($this->item, $this->rule));
  318. }
  319. /**
  320. * @dataProvider mergeIdsDataProvider
  321. *
  322. * @param [] $a1
  323. * @param [] $a2
  324. * @param bool $isSting
  325. * @param [] $expected
  326. */
  327. public function testMergeIds($a1, $a2, $isSting, $expected)
  328. {
  329. $this->assertEquals($expected, $this->utility->mergeIds($a1, $a2, $isSting));
  330. }
  331. /**
  332. * @return array
  333. */
  334. public function mergeIdsDataProvider()
  335. {
  336. return [
  337. ['id1,id2', '', true, 'id1,id2'],
  338. ['id1,id2', '', false, ['id1', 'id2']],
  339. ['', 'id3,id4', false, ['id3', 'id4']],
  340. ['', 'id3,id4', true, 'id3,id4'],
  341. [['id1', 'id2'], ['id3', 'id4'], false, ['id1', 'id2', 'id3', 'id4']],
  342. [['id1', 'id2'], ['id3', 'id4'], true, 'id1,id2,id3,id4']
  343. ];
  344. }
  345. public function testMinFix()
  346. {
  347. $qty = 13;
  348. $amount = 10;
  349. $baseAmount = 12;
  350. $fixedAmount = 20;
  351. $fixedBaseAmount = 24;
  352. $this->getItemPrice();
  353. $this->getItemBasePrice();
  354. $this->item->setDiscountAmount($amount);
  355. $this->item->setBaseDiscountAmount($baseAmount);
  356. $discountData = $this->createMock(\Magento\SalesRule\Model\Rule\Action\Discount\Data::class);
  357. $discountData->expects($this->atLeastOnce())
  358. ->method('getAmount')
  359. ->will($this->returnValue($amount));
  360. $discountData->expects($this->atLeastOnce())
  361. ->method('getBaseAmount')
  362. ->will($this->returnValue($baseAmount));
  363. $discountData->expects($this->once())
  364. ->method('setAmount')
  365. ->with($fixedAmount);
  366. $discountData->expects($this->once())
  367. ->method('setBaseAmount')
  368. ->with($fixedBaseAmount);
  369. $this->assertNull($this->utility->minFix($discountData, $this->item, $qty));
  370. }
  371. /**
  372. * @return int
  373. */
  374. protected function getItemPrice()
  375. {
  376. $price = 4;
  377. $calcPrice = 5;
  378. $this->item->expects($this->atLeastOnce())
  379. ->method('getDiscountCalculationPrice')
  380. ->will($this->returnValue($price));
  381. $this->item->expects($this->once())
  382. ->method('getCalculationPrice')
  383. ->will($this->returnValue($calcPrice));
  384. return $price;
  385. }
  386. /**
  387. * @return int
  388. */
  389. protected function getItemBasePrice()
  390. {
  391. $price = 4;
  392. $calcPrice = 5;
  393. $this->item->expects($this->atLeastOnce())
  394. ->method('getDiscountCalculationPrice')
  395. ->will($this->returnValue($calcPrice));
  396. $this->item->expects($this->any())
  397. ->method('getBaseDiscountCalculationPrice')
  398. ->will($this->returnValue($price));
  399. return $price;
  400. }
  401. public function testDeltaRoundignFix()
  402. {
  403. $discountAmount = 10.003;
  404. $baseDiscountAmount = 12.465;
  405. $percent = 15;
  406. $roundedDiscount = round($discountAmount, 2);
  407. $roundedBaseDiscount = round($baseDiscountAmount, 2);
  408. $delta = $discountAmount - $roundedDiscount;
  409. $baseDelta = $baseDiscountAmount - $roundedBaseDiscount;
  410. $secondRoundedDiscount = round($discountAmount + $delta);
  411. $secondRoundedBaseDiscount = round($baseDiscountAmount + $baseDelta);
  412. $this->item->expects($this->any())
  413. ->method('getQuote')
  414. ->will($this->returnValue($this->quote));
  415. $store = $this->createMock(\Magento\Store\Model\Store::class);
  416. $this->priceCurrency->expects($this->any())
  417. ->method('round')
  418. ->will($this->returnValueMap([
  419. [$discountAmount, $roundedDiscount],
  420. [$baseDiscountAmount, $roundedBaseDiscount],
  421. [$discountAmount + $delta, $secondRoundedDiscount], //?
  422. [$baseDiscountAmount + $baseDelta, $secondRoundedBaseDiscount], //?
  423. ]));
  424. $this->quote->expects($this->any())
  425. ->method('getStore')
  426. ->will($this->returnValue($store));
  427. $this->item->setDiscountPercent($percent);
  428. $discountData = $this->createMock(\Magento\SalesRule\Model\Rule\Action\Discount\Data::class);
  429. $discountData->expects($this->at(0))
  430. ->method('getAmount')
  431. ->will($this->returnValue($discountAmount));
  432. $discountData->expects($this->at(1))
  433. ->method('getBaseAmount')
  434. ->will($this->returnValue($baseDiscountAmount));
  435. $discountData->expects($this->at(2))
  436. ->method('setAmount')
  437. ->with($roundedDiscount);
  438. $discountData->expects($this->at(3))
  439. ->method('setBaseAmount')
  440. ->with($roundedBaseDiscount);
  441. $discountData->expects($this->at(4))
  442. ->method('getAmount')
  443. ->will($this->returnValue($discountAmount));
  444. $discountData->expects($this->at(5))
  445. ->method('getBaseAmount')
  446. ->will($this->returnValue($baseDiscountAmount));
  447. $discountData->expects($this->at(6))
  448. ->method('setAmount')
  449. ->with($secondRoundedDiscount);
  450. $discountData->expects($this->at(7))
  451. ->method('setBaseAmount')
  452. ->with($secondRoundedBaseDiscount);
  453. $this->assertEquals($this->utility, $this->utility->deltaRoundingFix($discountData, $this->item));
  454. $this->assertEquals($this->utility, $this->utility->deltaRoundingFix($discountData, $this->item));
  455. }
  456. public function testResetRoundingDeltas()
  457. {
  458. $this->assertNull($this->utility->resetRoundingDeltas());
  459. }
  460. }