CarrierTest.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Usps\Test\Unit\Model;
  7. use Magento\Catalog\Model\ResourceModel\Product\Collection;
  8. use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory;
  9. use Magento\Framework\App\Config\ScopeConfigInterface;
  10. use Magento\Framework\DataObject;
  11. use Magento\Framework\HTTP\ZendClient;
  12. use Magento\Framework\HTTP\ZendClientFactory;
  13. use Magento\Framework\Locale\ResolverInterface;
  14. use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
  15. use Magento\Framework\Xml\Security;
  16. use Magento\Quote\Model\Quote\Address\RateRequest;
  17. use Magento\Quote\Model\Quote\Address\RateResult\Error;
  18. use Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory;
  19. use Magento\Quote\Model\Quote\Address\RateResult\Method;
  20. use Magento\Quote\Model\Quote\Address\RateResult\MethodFactory;
  21. use Magento\Shipping\Helper\Carrier as CarrierHelper;
  22. use Magento\Shipping\Model\Rate\Result;
  23. use Magento\Shipping\Model\Rate\ResultFactory;
  24. use Magento\Shipping\Model\Shipment\ReturnShipment;
  25. use Magento\Shipping\Model\Simplexml\Element;
  26. use Magento\Shipping\Model\Simplexml\ElementFactory;
  27. use Magento\Usps\Helper\Data as DataHelper;
  28. use Magento\Usps\Model\Carrier;
  29. use PHPUnit_Framework_MockObject_MockObject as MockObject;
  30. /**
  31. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  32. */
  33. class CarrierTest extends \PHPUnit\Framework\TestCase
  34. {
  35. /**
  36. * @var \Zend_Http_Response|MockObject
  37. */
  38. private $httpResponse;
  39. /**
  40. * @var ObjectManager
  41. */
  42. private $objectManager;
  43. /**
  44. * @var Error|MockObject
  45. */
  46. private $error;
  47. /**
  48. * @var ErrorFactory|MockObject
  49. */
  50. private $errorFactory;
  51. /**
  52. * @var Carrier|MockObject
  53. */
  54. private $carrier;
  55. /**
  56. * @var ScopeConfigInterface|MockObject
  57. */
  58. private $scope;
  59. /**
  60. * @var DataHelper|MockObject
  61. */
  62. private $dataHelper;
  63. /**
  64. * @var ZendClient|MockObject
  65. */
  66. private $httpClient;
  67. /**
  68. * @inheritdoc
  69. */
  70. protected function setUp()
  71. {
  72. $this->objectManager = new ObjectManager($this);
  73. $this->scope = $this->getMockBuilder(ScopeConfigInterface::class)
  74. ->disableOriginalConstructor()
  75. ->getMock();
  76. $this->scope->method('getValue')
  77. ->willReturnCallback([$this, 'scopeConfiggetValue']);
  78. $xmlElFactory = $this->getXmlFactory();
  79. $rateFactory = $this->getRateFactory();
  80. $rateMethodFactory = $this->getRateMethodFactory();
  81. $httpClientFactory = $this->getHttpClientFactory();
  82. $data = ['id' => 'usps', 'store' => '1'];
  83. $this->error = $this->getMockBuilder(Error::class)
  84. ->setMethods(['setCarrier', 'setCarrierTitle', 'setErrorMessage'])
  85. ->getMock();
  86. $this->errorFactory = $this->getMockBuilder(ErrorFactory::class)
  87. ->disableOriginalConstructor()
  88. ->setMethods(['create'])
  89. ->getMock();
  90. $this->errorFactory->expects($this->any())->method('create')->willReturn($this->error);
  91. $carrierHelper = $this->getCarrierHelper();
  92. $productCollectionFactory = $this->getProductCollectionFactory();
  93. $arguments = [
  94. 'scopeConfig' => $this->scope,
  95. 'xmlSecurity' => new Security(),
  96. 'xmlElFactory' => $xmlElFactory,
  97. 'rateFactory' => $rateFactory,
  98. 'rateMethodFactory' => $rateMethodFactory,
  99. 'httpClientFactory' => $httpClientFactory,
  100. 'data' => $data,
  101. 'rateErrorFactory' => $this->errorFactory,
  102. 'carrierHelper' => $carrierHelper,
  103. 'productCollectionFactory' => $productCollectionFactory,
  104. 'dataHelper' => $this->dataHelper,
  105. ];
  106. $this->dataHelper = $this->getMockBuilder(DataHelper::class)
  107. ->disableOriginalConstructor()
  108. ->setMethods(['displayGirthValue'])
  109. ->getMock();
  110. $this->carrier = $this->objectManager->getObject(Carrier::class, $arguments);
  111. }
  112. /**
  113. * @dataProvider codeDataProvider
  114. */
  115. public function testGetCodeArray($code)
  116. {
  117. $this->assertNotEmpty($this->carrier->getCode($code));
  118. }
  119. public function testGetCodeBool()
  120. {
  121. $this->assertFalse($this->carrier->getCode('test_code'));
  122. }
  123. public function testCollectRates()
  124. {
  125. $expectedRequest = '<?xml version="1.0" encoding="UTF-8"?><RateV4Request USERID="213MAGEN6752">'
  126. . '<Revision>2</Revision><Package ID="0"><Service>ALL</Service><ZipOrigination/>'
  127. . '<ZipDestination>90032</ZipDestination><Pounds>4</Pounds><Ounces>4.2512000000</Ounces>'
  128. . '<Container>VARIABLE</Container><Size>REGULAR</Size><Machinable/></Package></RateV4Request>';
  129. $expectedXml = new \SimpleXMLElement($expectedRequest);
  130. $this->scope->method('isSetFlag')
  131. ->willReturn(true);
  132. $this->httpClient->expects(self::exactly(2))
  133. ->method('setParameterGet')
  134. ->withConsecutive(
  135. ['API', 'RateV4'],
  136. ['XML', self::equalTo($expectedXml->asXML())]
  137. );
  138. $this->httpResponse->method('getBody')
  139. ->willReturn(file_get_contents(__DIR__ . '/_files/success_usps_response_rates.xml'));
  140. $data = require __DIR__ . '/_files/rates_request_data.php';
  141. $request = $this->objectManager->getObject(RateRequest::class, ['data' => $data[0]]);
  142. self::assertNotEmpty($this->carrier->collectRates($request)->getAllRates());
  143. }
  144. public function testCollectRatesWithUnavailableService()
  145. {
  146. $expectedCount = 5;
  147. $this->scope->expects($this->once())
  148. ->method('isSetFlag')
  149. ->willReturn(true);
  150. $this->httpResponse->expects($this->once())
  151. ->method('getBody')
  152. ->willReturn(file_get_contents(__DIR__ . '/_files/response_rates.xml'));
  153. $data = require __DIR__ . '/_files/rates_request_data.php';
  154. $request = $this->objectManager->getObject(RateRequest::class, ['data' => $data[1]]);
  155. $rates = $this->carrier->collectRates($request)->getAllRates();
  156. $this->assertEquals($expectedCount, count($rates));
  157. }
  158. public function testReturnOfShipment()
  159. {
  160. $this->httpResponse->method('getBody')
  161. ->willReturn(file_get_contents(__DIR__ . '/_files/success_usps_response_return_shipment.xml'));
  162. $request = $this->objectManager->getObject(
  163. ReturnShipment::class,
  164. require __DIR__ . '/_files/return_shipment_request_data.php'
  165. );
  166. $this->httpClient->expects(self::exactly(2))
  167. ->method('setParameterGet')
  168. ->withConsecutive(
  169. ['API', 'SignatureConfirmationCertifyV3'],
  170. ['XML', $this->stringContains('<WeightInOunces>80</WeightInOunces>')]
  171. );
  172. $this->assertNotEmpty($this->carrier->returnOfShipment($request)->getInfo()[0]['tracking_number']);
  173. }
  174. public function testFormattingFloatValuesForIntlShipmentRequest()
  175. {
  176. $this->httpResponse->method('getBody')
  177. ->willReturn(
  178. file_get_contents(__DIR__ . '/_files/success_usps_response_return_shipment.xml')
  179. );
  180. $request = $this->objectManager->getObject(
  181. ReturnShipment::class,
  182. require __DIR__ . '/_files/return_shipment_request_data.php'
  183. );
  184. $request->setRecipientAddressCountryCode('UK');
  185. $formattedValuesRegex = '(<Value>5.00<\/Value>).*';
  186. $formattedValuesRegex .= '(<NetOunces>0.00<\/NetOunces>)';
  187. $this->httpClient->expects($this->exactly(2))
  188. ->method('setParameterGet')
  189. ->withConsecutive(
  190. ['API', 'ExpressMailIntl'],
  191. ['XML', $this->matchesRegularExpression('/' . $formattedValuesRegex . '/')]
  192. );
  193. $this->carrier->returnOfShipment($request);
  194. }
  195. /**
  196. * Emulates the config's `getValue` method.
  197. *
  198. * @param $path
  199. * @return string|string
  200. */
  201. public function scopeConfigGetValue($path)
  202. {
  203. $pathMap = [
  204. 'carriers/usps/allowed_methods' => '0_FCLE,0_FCL,0_FCP,1,2,3,4,6,7,13,16,17,22,23,25,27,28,33,' .
  205. '34,35,36,37,42,43,53,55,56,57,61,INT_1,INT_2,INT_4,INT_6,INT_7,INT_8,INT_9,INT_10,INT_11,' .
  206. 'INT_12,INT_13,INT_14,INT_15,INT_16,INT_20,INT_26',
  207. 'carriers/usps/showmethod' => 1,
  208. 'carriers/usps/debug' => 1,
  209. 'carriers/usps/userid' => 'test',
  210. 'carriers/usps/mode' => 0,
  211. ];
  212. return isset($pathMap[$path]) ? $pathMap[$path] : null;
  213. }
  214. /**
  215. * @return array
  216. */
  217. public function codeDataProvider()
  218. {
  219. return [['container'], ['machinable'], ['method'], ['size']];
  220. }
  221. public function testCollectRatesErrorMessage()
  222. {
  223. $this->scope->method('isSetFlag')
  224. ->willReturn(false);
  225. $this->error->method('setCarrier')
  226. ->with('usps');
  227. $this->error->expects($this->once())
  228. ->method('setCarrierTitle');
  229. $this->error->expects($this->once())
  230. ->method('setErrorMessage');
  231. $request = new RateRequest();
  232. $this->assertSame($this->error, $this->carrier->collectRates($request));
  233. }
  234. public function testCollectRatesFail()
  235. {
  236. $this->scope->method('isSetFlag')
  237. ->willReturn(true);
  238. $this->scope->expects($this->atLeastOnce())
  239. ->method('getValue')
  240. ->willReturnMap(
  241. [
  242. ['carriers/usps/userid' => 123],
  243. ['carriers/usps/container' => 11],
  244. ]
  245. );
  246. $request = new RateRequest();
  247. $request->setPackageWeight(1);
  248. $this->assertNotEmpty($this->carrier->collectRates($request));
  249. }
  250. /**
  251. * @param string $data
  252. * @param array $maskFields
  253. * @param string $expected
  254. * @dataProvider logDataProvider
  255. */
  256. public function testFilterDebugData($data, array $maskFields, $expected)
  257. {
  258. $refClass = new \ReflectionClass(Carrier::class);
  259. $property = $refClass->getProperty('_debugReplacePrivateDataKeys');
  260. $property->setAccessible(true);
  261. $property->setValue($this->carrier, $maskFields);
  262. $refMethod = $refClass->getMethod('filterDebugData');
  263. $refMethod->setAccessible(true);
  264. $result = $refMethod->invoke($this->carrier, $data);
  265. $expectedXml = new \SimpleXMLElement($expected);
  266. $resultXml = new \SimpleXMLElement($result);
  267. $this->assertEquals($expectedXml->asXML(), $resultXml->asXML());
  268. }
  269. /**
  270. * Get list of variations
  271. */
  272. public function logDataProvider()
  273. {
  274. return [
  275. [
  276. '<?xml version="1.0" encoding="UTF-8"?>
  277. <RateRequest USERID="12312">
  278. <Package ID="0">
  279. <Service>ALL</Service>
  280. </Package>
  281. </RateRequest>',
  282. ['USERID'],
  283. '<?xml version="1.0" encoding="UTF-8"?>
  284. <RateRequest USERID="****">
  285. <Package ID="0">
  286. <Service>ALL</Service>
  287. </Package>
  288. </RateRequest>',
  289. ],
  290. ];
  291. }
  292. /**
  293. * @param string $countyCode
  294. * @param string $carrierMethodCode
  295. * @param bool $displayGirthValueResult
  296. * @param bool $result
  297. * @dataProvider isGirthAllowedDataProvider
  298. */
  299. public function testIsGirthAllowed($countyCode, $carrierMethodCode, $displayGirthValueResult, $result)
  300. {
  301. $this->dataHelper->method('displayGirthValue')
  302. ->with($carrierMethodCode)
  303. ->willReturn($displayGirthValueResult);
  304. $this->assertEquals($result, $this->carrier->isGirthAllowed($countyCode, $carrierMethodCode));
  305. }
  306. /**
  307. * @return array
  308. */
  309. public function isGirthAllowedDataProvider()
  310. {
  311. return [
  312. ['US', 'usps_1', true, false],
  313. ['UK', 'usps_1', true, true],
  314. ['US', 'usps_0', false, true],
  315. ];
  316. }
  317. /**
  318. * @return MockObject
  319. */
  320. private function getXmlFactory(): MockObject
  321. {
  322. $xmlElFactory = $this->getMockBuilder(ElementFactory::class)
  323. ->disableOriginalConstructor()
  324. ->setMethods(['create'])
  325. ->getMock();
  326. $xmlElFactory->method('create')
  327. ->willReturnCallback(
  328. function ($data) {
  329. $helper = new ObjectManager($this);
  330. return $helper->getObject(
  331. Element::class,
  332. ['data' => $data['data']]
  333. );
  334. }
  335. );
  336. return $xmlElFactory;
  337. }
  338. /**
  339. * @return MockObject
  340. */
  341. private function getRateFactory(): MockObject
  342. {
  343. $rateFactory = $this->getMockBuilder(ResultFactory::class)
  344. ->disableOriginalConstructor()
  345. ->setMethods(['create'])
  346. ->getMock();
  347. $rateResult = $this->getMockBuilder(Result::class)
  348. ->disableOriginalConstructor()
  349. ->setMethods(null)
  350. ->getMock();
  351. $rateFactory->method('create')
  352. ->willReturn($rateResult);
  353. return $rateFactory;
  354. }
  355. /**
  356. * @return MockObject
  357. */
  358. private function getRateMethodFactory(): MockObject
  359. {
  360. $rateMethodFactory = $this->getMockBuilder(MethodFactory::class)
  361. ->disableOriginalConstructor()
  362. ->setMethods(['create'])
  363. ->getMock();
  364. $rateMethod = $this->getMockBuilder(Method::class)
  365. ->disableOriginalConstructor()
  366. ->setMethods(['setPrice'])
  367. ->getMock();
  368. $rateMethod->method('setPrice')
  369. ->willReturnSelf();
  370. $rateMethodFactory->method('create')
  371. ->willReturn($rateMethod);
  372. return $rateMethodFactory;
  373. }
  374. /**
  375. * @return MockObject
  376. */
  377. private function getHttpClientFactory(): MockObject
  378. {
  379. $this->httpResponse = $this->getMockBuilder(\Zend_Http_Response::class)
  380. ->disableOriginalConstructor()
  381. ->setMethods(['getBody'])
  382. ->getMock();
  383. $this->httpClient = $this->getMockBuilder(ZendClient::class)
  384. ->getMock();
  385. $this->httpClient->method('request')
  386. ->willReturn($this->httpResponse);
  387. $httpClientFactory = $this->getMockBuilder(ZendClientFactory::class)
  388. ->disableOriginalConstructor()
  389. ->getMock();
  390. $httpClientFactory->method('create')
  391. ->willReturn($this->httpClient);
  392. return $httpClientFactory;
  393. }
  394. /**
  395. * @return MockObject
  396. */
  397. private function getProductCollectionFactory(): MockObject
  398. {
  399. $productCollection = $this->getMockBuilder(Collection::class)
  400. ->disableOriginalConstructor()
  401. ->getMock();
  402. $productCollection->method('addStoreFilter')
  403. ->willReturnSelf();
  404. $productCollection->method('addFieldToFilter')
  405. ->willReturnSelf();
  406. $productCollection->method('addAttributeToSelect')
  407. ->willReturn([]);
  408. $productCollectionFactory = $this->getMockBuilder(CollectionFactory::class)
  409. ->disableOriginalConstructor()
  410. ->getMock();
  411. $productCollectionFactory->method('create')
  412. ->willReturn($productCollection);
  413. return $productCollectionFactory;
  414. }
  415. /**
  416. * @return CarrierHelper
  417. */
  418. private function getCarrierHelper(): CarrierHelper
  419. {
  420. $localeResolver = $this->getMockForAbstractClass(ResolverInterface::class);
  421. $localeResolver->method('getLocale')->willReturn('fr_FR');
  422. $carrierHelper = $this->objectManager->getObject(
  423. CarrierHelper::class,
  424. [
  425. 'localeResolver' => $localeResolver,
  426. ]
  427. );
  428. return $carrierHelper;
  429. }
  430. }