<?php /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\SalesRule\Test\Unit\Model; /** * Class UtilityTest * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class UtilityTest extends \PHPUnit\Framework\TestCase { /** * @var \Magento\SalesRule\Model\ResourceModel\Coupon\UsageFactory | \PHPUnit_Framework_MockObject_MockObject */ protected $usageFactory; /** * @var \Magento\SalesRule\Model\CouponFactory | \PHPUnit_Framework_MockObject_MockObject */ protected $couponFactory; /** * @var \Magento\SalesRule\Model\Coupon | \PHPUnit_Framework_MockObject_MockObject */ protected $coupon; /** * @var \Magento\Quote\Model\Quote | \PHPUnit_Framework_MockObject_MockObject */ protected $quote; /** * @var \Magento\SalesRule\Model\Rule\CustomerFactory | \PHPUnit_Framework_MockObject_MockObject */ protected $customerFactory; /** * @var \Magento\SalesRule\Model\Rule\Customer | \PHPUnit_Framework_MockObject_MockObject */ protected $customer; /** * @var \Magento\Quote\Model\Quote\Address | \PHPUnit_Framework_MockObject_MockObject */ protected $address; /** * @var \Magento\SalesRule\Model\Rule | \PHPUnit_Framework_MockObject_MockObject */ protected $rule; /** * @var \Magento\Framework\DataObjectFactory | \PHPUnit_Framework_MockObject_MockObject */ protected $objectFactory; /** * @var \Magento\Quote\Model\Quote\Item\AbstractItem | \PHPUnit_Framework_MockObject_MockObject */ protected $item; /** * @var \Magento\SalesRule\Model\Utility */ protected $utility; /** * @var \Magento\Framework\Pricing\PriceCurrencyInterface | \PHPUnit_Framework_MockObject_MockObject */ protected $priceCurrency; protected function setUp() { $this->usageFactory = $this->createPartialMock( \Magento\SalesRule\Model\ResourceModel\Coupon\UsageFactory::class, ['create'] ); $this->couponFactory = $this->createPartialMock(\Magento\SalesRule\Model\CouponFactory::class, ['create']); $this->objectFactory = $this->createPartialMock(\Magento\Framework\DataObjectFactory::class, ['create']); $this->customerFactory = $this->createPartialMock( \Magento\SalesRule\Model\Rule\CustomerFactory::class, ['create'] ); $this->coupon = $this->createPartialMock( \Magento\SalesRule\Model\Coupon::class, [ 'load', 'getId', 'getUsageLimit', 'getTimesUsed', 'getUsagePerCustomer', '__wakeup' ] ); $this->quote = $this->createPartialMock(\Magento\Quote\Model\Quote::class, ['__wakeup', 'getStore']); $this->customer = $this->createPartialMock( \Magento\SalesRule\Model\Rule\Customer::class, ['loadByCustomerRule', '__wakeup'] ); $this->rule = $this->createPartialMock(\Magento\SalesRule\Model\Rule::class, [ 'hasIsValidForAddress', 'getIsValidForAddress', 'setIsValidForAddress', '__wakeup', 'validate', 'afterLoad', 'getDiscountQty' ]); $this->address = $this->createPartialMock(\Magento\Quote\Model\Quote\Address::class, [ 'isObjectNew', 'getQuote', 'setIsValidForAddress', '__wakeup', 'validate', 'afterLoad' ]); $this->address->setQuote($this->quote); $this->item = $this->createPartialMock(\Magento\Quote\Model\Quote\Item\AbstractItem::class, [ 'getDiscountCalculationPrice', 'getCalculationPrice', 'getBaseDiscountCalculationPrice', 'getBaseCalculationPrice', 'getQuote', 'getAddress', 'getOptionByCode', 'getTotalQty', '__wakeup' ]); $this->priceCurrency = $this->getMockBuilder(\Magento\Framework\Pricing\PriceCurrencyInterface::class) ->getMock(); $this->utility = new \Magento\SalesRule\Model\Utility( $this->usageFactory, $this->couponFactory, $this->customerFactory, $this->objectFactory, $this->priceCurrency ); } /** * Check rule for specific address */ public function testCanProcessRuleValidAddress() { $this->rule->expects($this->once()) ->method('hasIsValidForAddress') ->with($this->address) ->will($this->returnValue(true)); $this->rule->expects($this->once()) ->method('getIsValidForAddress') ->with($this->address) ->will($this->returnValue(true)); $this->address->expects($this->once()) ->method('isObjectNew') ->will($this->returnValue(false)); $this->assertTrue($this->utility->canProcessRule($this->rule, $this->address)); } /** * Check coupon entire usage limit */ public function testCanProcessRuleCouponUsageLimitFail() { $couponCode = 111; $couponId = 4; $quoteId = 4; $usageLimit = 1; $timesUsed = 2; $this->rule->setCouponType(\Magento\SalesRule\Model\Rule::COUPON_TYPE_SPECIFIC); $this->quote->setCouponCode($couponCode); $this->quote->setId($quoteId); $this->address->expects($this->once()) ->method('getQuote') ->will($this->returnValue($this->quote)); $this->coupon->expects($this->atLeastOnce()) ->method('getUsageLimit') ->will($this->returnValue($usageLimit)); $this->coupon->expects($this->once()) ->method('getTimesUsed') ->will($this->returnValue($timesUsed)); $this->coupon->expects($this->once()) ->method('load') ->with($couponCode, 'code') ->will($this->returnSelf()); $this->couponFactory->expects($this->once()) ->method('create') ->will($this->returnValue($this->coupon)); $this->coupon->expects($this->once()) ->method('getId') ->will($this->returnValue($couponId)); $this->assertFalse($this->utility->canProcessRule($this->rule, $this->address)); } /** * Check coupon per customer usage limit */ public function testCanProcessRuleCouponUsagePerCustomerFail() { $couponCode = 111; $couponId = 4; $quoteId = 4; $customerId = 1; $usageLimit = 1; $timesUsed = 2; $this->rule->setCouponType(\Magento\SalesRule\Model\Rule::COUPON_TYPE_SPECIFIC); $this->quote->setCouponCode($couponCode); $this->quote->setId($quoteId); $this->quote->setCustomerId($customerId); $this->address->expects($this->atLeastOnce()) ->method('getQuote') ->will($this->returnValue($this->quote)); $this->coupon->expects($this->atLeastOnce()) ->method('getUsagePerCustomer') ->will($this->returnValue($usageLimit)); $this->coupon->expects($this->once()) ->method('load') ->with($couponCode, 'code') ->will($this->returnSelf()); $this->coupon->expects($this->atLeastOnce()) ->method('getId') ->will($this->returnValue($couponId)); $this->couponFactory->expects($this->once()) ->method('create') ->will($this->returnValue($this->coupon)); $couponUsage = new \Magento\Framework\DataObject(); $this->objectFactory->expects($this->once()) ->method('create') ->will($this->returnValue($couponUsage)); $couponUsageModel = $this->createMock(\Magento\SalesRule\Model\ResourceModel\Coupon\Usage::class); $couponUsage->setData(['coupon_id' => $couponId, 'times_used' => $timesUsed]); $this->usageFactory->expects($this->once()) ->method('create') ->will($this->returnValue($couponUsageModel)); $this->assertFalse($this->utility->canProcessRule($this->rule, $this->address)); } /** * Check rule per customer usage limit */ public function testCanProcessRuleUsagePerCustomer() { $customerId = 1; $usageLimit = 1; $timesUsed = 2; $ruleId = 4; $this->rule->setId($ruleId); $this->rule->setUsesPerCustomer($usageLimit); $this->quote->setCustomerId($customerId); $this->address->expects($this->atLeastOnce()) ->method('getQuote') ->will($this->returnValue($this->quote)); $this->customer->setId($customerId); $this->customer->setTimesUsed($timesUsed); $this->customerFactory->expects($this->once()) ->method('create') ->will($this->returnValue($this->customer)); $this->assertFalse($this->utility->canProcessRule($this->rule, $this->address)); } /** * Quote does not meet rule's conditions */ public function testCanProcessRuleInvalidConditions() { $this->rule->setCouponType(\Magento\SalesRule\Model\Rule::COUPON_TYPE_NO_COUPON); $this->assertFalse($this->utility->canProcessRule($this->rule, $this->address)); } /** * Quote does not meet rule's conditions */ public function testCanProcessRule() { $this->rule->setCouponType(\Magento\SalesRule\Model\Rule::COUPON_TYPE_NO_COUPON); $this->rule->expects($this->once()) ->method('validate') ->will($this->returnValue(true)); $this->assertTrue($this->utility->canProcessRule($this->rule, $this->address)); } public function testGetItemPrice() { $price = $this->getItemPrice(); $this->assertEquals($price, $this->utility->getItemPrice($this->item)); } public function testGetItemPriceNull() { $price = 4; $this->item->expects($this->once()) ->method('getDiscountCalculationPrice') ->will($this->returnValue($price)); $this->item->expects($this->once()) ->method('getCalculationPrice') ->will($this->returnValue(null)); $this->assertEquals($price, $this->utility->getItemPrice($this->item)); } public function testGetItemBasePrice() { $price = $this->getItemBasePrice(); $this->assertEquals($price, $this->utility->getItemBasePrice($this->item)); } public function testGetBaseItemPriceCalculation() { $calcPrice = 5; $this->item->expects($this->once()) ->method('getDiscountCalculationPrice') ->will($this->returnValue(null)); $this->item->expects($this->any()) ->method('getBaseCalculationPrice') ->will($this->returnValue($calcPrice)); $this->assertEquals($calcPrice, $this->utility->getItemBasePrice($this->item)); } public function testGetItemQtyMin() { $qty = 7; $discountQty = 4; $this->item->expects($this->once()) ->method('getTotalQty') ->will($this->returnValue($qty)); $this->rule->expects($this->once()) ->method('getDiscountQty') ->will($this->returnValue($discountQty)); $this->assertEquals(min($discountQty, $qty), $this->utility->getItemQty($this->item, $this->rule)); } public function testGetItemQty() { $qty = 7; $this->item->expects($this->once()) ->method('getTotalQty') ->will($this->returnValue($qty)); $this->rule->expects($this->once()) ->method('getDiscountQty') ->will($this->returnValue(null)); $this->assertEquals($qty, $this->utility->getItemQty($this->item, $this->rule)); } /** * @dataProvider mergeIdsDataProvider * * @param [] $a1 * @param [] $a2 * @param bool $isSting * @param [] $expected */ public function testMergeIds($a1, $a2, $isSting, $expected) { $this->assertEquals($expected, $this->utility->mergeIds($a1, $a2, $isSting)); } /** * @return array */ public function mergeIdsDataProvider() { return [ ['id1,id2', '', true, 'id1,id2'], ['id1,id2', '', false, ['id1', 'id2']], ['', 'id3,id4', false, ['id3', 'id4']], ['', 'id3,id4', true, 'id3,id4'], [['id1', 'id2'], ['id3', 'id4'], false, ['id1', 'id2', 'id3', 'id4']], [['id1', 'id2'], ['id3', 'id4'], true, 'id1,id2,id3,id4'] ]; } public function testMinFix() { $qty = 13; $amount = 10; $baseAmount = 12; $fixedAmount = 20; $fixedBaseAmount = 24; $this->getItemPrice(); $this->getItemBasePrice(); $this->item->setDiscountAmount($amount); $this->item->setBaseDiscountAmount($baseAmount); $discountData = $this->createMock(\Magento\SalesRule\Model\Rule\Action\Discount\Data::class); $discountData->expects($this->atLeastOnce()) ->method('getAmount') ->will($this->returnValue($amount)); $discountData->expects($this->atLeastOnce()) ->method('getBaseAmount') ->will($this->returnValue($baseAmount)); $discountData->expects($this->once()) ->method('setAmount') ->with($fixedAmount); $discountData->expects($this->once()) ->method('setBaseAmount') ->with($fixedBaseAmount); $this->assertNull($this->utility->minFix($discountData, $this->item, $qty)); } /** * @return int */ protected function getItemPrice() { $price = 4; $calcPrice = 5; $this->item->expects($this->atLeastOnce()) ->method('getDiscountCalculationPrice') ->will($this->returnValue($price)); $this->item->expects($this->once()) ->method('getCalculationPrice') ->will($this->returnValue($calcPrice)); return $price; } /** * @return int */ protected function getItemBasePrice() { $price = 4; $calcPrice = 5; $this->item->expects($this->atLeastOnce()) ->method('getDiscountCalculationPrice') ->will($this->returnValue($calcPrice)); $this->item->expects($this->any()) ->method('getBaseDiscountCalculationPrice') ->will($this->returnValue($price)); return $price; } public function testDeltaRoundignFix() { $discountAmount = 10.003; $baseDiscountAmount = 12.465; $percent = 15; $roundedDiscount = round($discountAmount, 2); $roundedBaseDiscount = round($baseDiscountAmount, 2); $delta = $discountAmount - $roundedDiscount; $baseDelta = $baseDiscountAmount - $roundedBaseDiscount; $secondRoundedDiscount = round($discountAmount + $delta); $secondRoundedBaseDiscount = round($baseDiscountAmount + $baseDelta); $this->item->expects($this->any()) ->method('getQuote') ->will($this->returnValue($this->quote)); $store = $this->createMock(\Magento\Store\Model\Store::class); $this->priceCurrency->expects($this->any()) ->method('round') ->will($this->returnValueMap([ [$discountAmount, $roundedDiscount], [$baseDiscountAmount, $roundedBaseDiscount], [$discountAmount + $delta, $secondRoundedDiscount], //? [$baseDiscountAmount + $baseDelta, $secondRoundedBaseDiscount], //? ])); $this->quote->expects($this->any()) ->method('getStore') ->will($this->returnValue($store)); $this->item->setDiscountPercent($percent); $discountData = $this->createMock(\Magento\SalesRule\Model\Rule\Action\Discount\Data::class); $discountData->expects($this->at(0)) ->method('getAmount') ->will($this->returnValue($discountAmount)); $discountData->expects($this->at(1)) ->method('getBaseAmount') ->will($this->returnValue($baseDiscountAmount)); $discountData->expects($this->at(2)) ->method('setAmount') ->with($roundedDiscount); $discountData->expects($this->at(3)) ->method('setBaseAmount') ->with($roundedBaseDiscount); $discountData->expects($this->at(4)) ->method('getAmount') ->will($this->returnValue($discountAmount)); $discountData->expects($this->at(5)) ->method('getBaseAmount') ->will($this->returnValue($baseDiscountAmount)); $discountData->expects($this->at(6)) ->method('setAmount') ->with($secondRoundedDiscount); $discountData->expects($this->at(7)) ->method('setBaseAmount') ->with($secondRoundedBaseDiscount); $this->assertEquals($this->utility, $this->utility->deltaRoundingFix($discountData, $this->item)); $this->assertEquals($this->utility, $this->utility->deltaRoundingFix($discountData, $this->item)); } public function testResetRoundingDeltas() { $this->assertNull($this->utility->resetRoundingDeltas()); } }