GatewayTest.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Signifyd\Test\Unit\Model\SignifydGateway;
  7. use Magento\Sales\Api\Data\OrderInterface;
  8. use Magento\Sales\Api\OrderRepositoryInterface;
  9. use Magento\Signifyd\Api\CaseRepositoryInterface;
  10. use Magento\Signifyd\Api\Data\CaseInterface;
  11. use PHPUnit_Framework_MockObject_MockObject as MockObject;
  12. use Magento\Signifyd\Model\SignifydGateway\Gateway;
  13. use Magento\Signifyd\Model\SignifydGateway\GatewayException;
  14. use Magento\Signifyd\Model\SignifydGateway\Request\CreateCaseBuilderInterface;
  15. use Magento\Signifyd\Model\SignifydGateway\ApiClient;
  16. use Magento\Signifyd\Model\SignifydGateway\ApiCallException;
  17. class GatewayTest extends \PHPUnit\Framework\TestCase
  18. {
  19. /**
  20. * @var CreateCaseBuilderInterface|MockObject
  21. */
  22. private $createCaseBuilder;
  23. /**
  24. * @var ApiClient|MockObject
  25. */
  26. private $apiClient;
  27. /**
  28. * @var Gateway
  29. */
  30. private $gateway;
  31. /**
  32. * @var OrderRepositoryInterface|MockObject
  33. */
  34. private $orderRepository;
  35. /**
  36. * @var CaseRepositoryInterface|MockObject
  37. */
  38. private $caseRepository;
  39. public function setUp()
  40. {
  41. $this->createCaseBuilder = $this->getMockBuilder(CreateCaseBuilderInterface::class)
  42. ->getMockForAbstractClass();
  43. $this->apiClient = $this->getMockBuilder(ApiClient::class)
  44. ->disableOriginalConstructor()
  45. ->getMock();
  46. $this->orderRepository = $this->getMockBuilder(OrderRepositoryInterface::class)
  47. ->getMockForAbstractClass();
  48. $this->caseRepository= $this->getMockBuilder(CaseRepositoryInterface::class)
  49. ->getMockForAbstractClass();
  50. $this->gateway = new Gateway(
  51. $this->createCaseBuilder,
  52. $this->apiClient,
  53. $this->orderRepository,
  54. $this->caseRepository
  55. );
  56. }
  57. public function testCreateCaseForSpecifiedOrder()
  58. {
  59. $dummyOrderId = 1;
  60. $dummyStoreId = 2;
  61. $dummySignifydInvestigationId = 42;
  62. $this->withOrderEntity($dummyOrderId, $dummyStoreId);
  63. $this->apiClient
  64. ->method('makeApiCall')
  65. ->willReturn([
  66. 'investigationId' => $dummySignifydInvestigationId
  67. ]);
  68. $this->createCaseBuilder
  69. ->expects($this->atLeastOnce())
  70. ->method('build')
  71. ->with($this->equalTo($dummyOrderId))
  72. ->willReturn([]);
  73. $result = $this->gateway->createCase($dummyOrderId);
  74. $this->assertEquals(42, $result);
  75. }
  76. public function testCreateCaseCallsValidApiMethod()
  77. {
  78. $dummyOrderId = 1;
  79. $dummyStoreId = 2;
  80. $dummySignifydInvestigationId = 42;
  81. $this->withOrderEntity($dummyOrderId, $dummyStoreId);
  82. $this->createCaseBuilder
  83. ->method('build')
  84. ->willReturn([]);
  85. $this->apiClient
  86. ->expects($this->atLeastOnce())
  87. ->method('makeApiCall')
  88. ->with(
  89. $this->equalTo('/cases'),
  90. $this->equalTo('POST'),
  91. $this->isType('array'),
  92. $this->equalTo($dummyStoreId)
  93. )
  94. ->willReturn([
  95. 'investigationId' => $dummySignifydInvestigationId
  96. ]);
  97. $result = $this->gateway->createCase($dummyOrderId);
  98. $this->assertEquals(42, $result);
  99. }
  100. public function testCreateCaseNormalFlow()
  101. {
  102. $dummyOrderId = 1;
  103. $dummyStoreId = 2;
  104. $dummySignifydInvestigationId = 42;
  105. $this->withOrderEntity($dummyOrderId, $dummyStoreId);
  106. $this->createCaseBuilder
  107. ->method('build')
  108. ->willReturn([]);
  109. $this->apiClient
  110. ->method('makeApiCall')
  111. ->willReturn([
  112. 'investigationId' => $dummySignifydInvestigationId
  113. ]);
  114. $returnedInvestigationId = $this->gateway->createCase($dummyOrderId);
  115. $this->assertEquals(
  116. $dummySignifydInvestigationId,
  117. $returnedInvestigationId,
  118. 'Method must return value specified in "investigationId" response parameter'
  119. );
  120. }
  121. public function testCreateCaseWithFailedApiCall()
  122. {
  123. $dummyOrderId = 1;
  124. $dummyStoreId = 2;
  125. $apiCallFailureMessage = 'Api call failed';
  126. $this->withOrderEntity($dummyOrderId, $dummyStoreId);
  127. $this->createCaseBuilder
  128. ->method('build')
  129. ->willReturn([]);
  130. $this->apiClient
  131. ->method('makeApiCall')
  132. ->willThrowException(new ApiCallException($apiCallFailureMessage));
  133. $this->expectException(GatewayException::class);
  134. $this->expectExceptionMessage($apiCallFailureMessage);
  135. $this->gateway->createCase($dummyOrderId);
  136. }
  137. public function testCreateCaseWithMissedResponseRequiredData()
  138. {
  139. $dummyOrderId = 1;
  140. $dummyStoreId = 2;
  141. $this->withOrderEntity($dummyOrderId, $dummyStoreId);
  142. $this->createCaseBuilder
  143. ->method('build')
  144. ->willReturn([]);
  145. $this->apiClient
  146. ->method('makeApiCall')
  147. ->willReturn([
  148. 'someOtherParameter' => 'foo',
  149. ]);
  150. $this->expectException(GatewayException::class);
  151. $this->gateway->createCase($dummyOrderId);
  152. }
  153. public function testCreateCaseWithAdditionalResponseData()
  154. {
  155. $dummyOrderId = 1;
  156. $dummyStoreId = 2;
  157. $dummySignifydInvestigationId = 42;
  158. $this->withOrderEntity($dummyOrderId, $dummyStoreId);
  159. $this->createCaseBuilder
  160. ->method('build')
  161. ->willReturn([]);
  162. $this->apiClient
  163. ->method('makeApiCall')
  164. ->willReturn([
  165. 'investigationId' => $dummySignifydInvestigationId,
  166. 'someOtherParameter' => 'foo',
  167. ]);
  168. $returnedInvestigationId = $this->gateway->createCase($dummyOrderId);
  169. $this->assertEquals(
  170. $dummySignifydInvestigationId,
  171. $returnedInvestigationId,
  172. 'Method must return value specified in "investigationId" response parameter and ignore any other parameters'
  173. );
  174. }
  175. public function testSubmitCaseForGuaranteeCallsValidApiMethod()
  176. {
  177. $dummySygnifydCaseId = 42;
  178. $dummyStoreId = 1;
  179. $dummyDisposition = 'APPROVED';
  180. $this->withCaseEntity($dummySygnifydCaseId, $dummyStoreId);
  181. $this->apiClient
  182. ->expects($this->atLeastOnce())
  183. ->method('makeApiCall')
  184. ->with(
  185. $this->equalTo('/guarantees'),
  186. $this->equalTo('POST'),
  187. $this->equalTo([
  188. 'caseId' => $dummySygnifydCaseId
  189. ]),
  190. $this->equalTo($dummyStoreId)
  191. )->willReturn([
  192. 'disposition' => $dummyDisposition
  193. ]);
  194. $result = $this->gateway->submitCaseForGuarantee($dummySygnifydCaseId);
  195. $this->assertEquals('APPROVED', $result);
  196. }
  197. public function testSubmitCaseForGuaranteeWithFailedApiCall()
  198. {
  199. $dummySygnifydCaseId = 42;
  200. $dummyStoreId = 1;
  201. $apiCallFailureMessage = 'Api call failed';
  202. $this->withCaseEntity($dummySygnifydCaseId, $dummyStoreId);
  203. $this->apiClient
  204. ->method('makeApiCall')
  205. ->willThrowException(new ApiCallException($apiCallFailureMessage));
  206. $this->expectException(GatewayException::class);
  207. $this->expectExceptionMessage($apiCallFailureMessage);
  208. $result = $this->gateway->submitCaseForGuarantee($dummySygnifydCaseId);
  209. $this->assertEquals('Api call failed', $result);
  210. }
  211. public function testSubmitCaseForGuaranteeReturnsDisposition()
  212. {
  213. $dummySygnifydCaseId = 42;
  214. $dummyStoreId = 1;
  215. $dummyDisposition = 'APPROVED';
  216. $dummyGuaranteeId = 123;
  217. $dummyRereviewCount = 0;
  218. $this->withCaseEntity($dummySygnifydCaseId, $dummyStoreId);
  219. $this->apiClient
  220. ->method('makeApiCall')
  221. ->willReturn([
  222. 'guaranteeId' => $dummyGuaranteeId,
  223. 'disposition' => $dummyDisposition,
  224. 'rereviewCount' => $dummyRereviewCount,
  225. ]);
  226. $actualDisposition = $this->gateway->submitCaseForGuarantee($dummySygnifydCaseId);
  227. $this->assertEquals(
  228. $dummyDisposition,
  229. $actualDisposition,
  230. 'Method must return guarantee disposition retrieved in Signifyd API response as a result'
  231. );
  232. }
  233. public function testSubmitCaseForGuaranteeWithMissedDisposition()
  234. {
  235. $dummySygnifydCaseId = 42;
  236. $dummyStoreId = 1;
  237. $dummyGuaranteeId = 123;
  238. $dummyRereviewCount = 0;
  239. $this->withCaseEntity($dummySygnifydCaseId, $dummyStoreId);
  240. $this->apiClient
  241. ->method('makeApiCall')
  242. ->willReturn([
  243. 'guaranteeId' => $dummyGuaranteeId,
  244. 'rereviewCount' => $dummyRereviewCount,
  245. ]);
  246. $this->expectException(GatewayException::class);
  247. $this->gateway->submitCaseForGuarantee($dummySygnifydCaseId);
  248. }
  249. public function testSubmitCaseForGuaranteeWithUnexpectedDisposition()
  250. {
  251. $dummySygnifydCaseId = 42;
  252. $dummyStoreId = 1;
  253. $dummyUnexpectedDisposition = 'UNEXPECTED';
  254. $this->withCaseEntity($dummySygnifydCaseId, $dummyStoreId);
  255. $this->apiClient
  256. ->method('makeApiCall')
  257. ->willReturn([
  258. 'disposition' => $dummyUnexpectedDisposition,
  259. ]);
  260. $this->expectException(GatewayException::class);
  261. $result = $this->gateway->submitCaseForGuarantee($dummySygnifydCaseId);
  262. $this->assertEquals('UNEXPECTED', $result);
  263. }
  264. /**
  265. * @dataProvider supportedGuaranteeDispositionsProvider
  266. */
  267. public function testSubmitCaseForGuaranteeWithExpectedDisposition($dummyExpectedDisposition)
  268. {
  269. $dummySygnifydCaseId = 42;
  270. $dummyStoreId = 1;
  271. $this->withCaseEntity($dummySygnifydCaseId, $dummyStoreId);
  272. $this->apiClient
  273. ->method('makeApiCall')
  274. ->willReturn([
  275. 'disposition' => $dummyExpectedDisposition,
  276. ]);
  277. try {
  278. $result = $this->gateway->submitCaseForGuarantee($dummySygnifydCaseId);
  279. $this->assertEquals($dummyExpectedDisposition, $result);
  280. } catch (GatewayException $e) {
  281. $this->fail(sprintf(
  282. 'Expected disposition "%s" was not accepted with message "%s"',
  283. $dummyExpectedDisposition,
  284. $e->getMessage()
  285. ));
  286. }
  287. }
  288. /**
  289. * Checks a test case when guarantee for a case is successfully canceled
  290. *
  291. * @covers \Magento\Signifyd\Model\SignifydGateway\Gateway::cancelGuarantee
  292. */
  293. public function testCancelGuarantee()
  294. {
  295. $caseId = 123;
  296. $dummyStoreId = 1;
  297. $this->withCaseEntity($caseId, $dummyStoreId);
  298. $this->apiClient->expects(self::once())
  299. ->method('makeApiCall')
  300. ->with(
  301. '/cases/' . $caseId . '/guarantee',
  302. 'PUT',
  303. ['guaranteeDisposition' => Gateway::GUARANTEE_CANCELED],
  304. $dummyStoreId
  305. )
  306. ->willReturn(
  307. ['disposition' => Gateway::GUARANTEE_CANCELED]
  308. );
  309. $result = $this->gateway->cancelGuarantee($caseId);
  310. self::assertEquals(Gateway::GUARANTEE_CANCELED, $result);
  311. }
  312. /**
  313. * Checks a case when API request returns unexpected guarantee disposition.
  314. *
  315. * @covers \Magento\Signifyd\Model\SignifydGateway\Gateway::cancelGuarantee
  316. * @expectedException \Magento\Signifyd\Model\SignifydGateway\GatewayException
  317. * @expectedExceptionMessage API returned unexpected disposition: DECLINED.
  318. */
  319. public function testCancelGuaranteeWithUnexpectedDisposition()
  320. {
  321. $caseId = 123;
  322. $dummyStoreId = 1;
  323. $this->withCaseEntity($caseId, $dummyStoreId);
  324. $this->apiClient->expects(self::once())
  325. ->method('makeApiCall')
  326. ->with(
  327. '/cases/' . $caseId . '/guarantee',
  328. 'PUT',
  329. ['guaranteeDisposition' => Gateway::GUARANTEE_CANCELED],
  330. $dummyStoreId
  331. )
  332. ->willReturn(['disposition' => Gateway::GUARANTEE_DECLINED]);
  333. $result = $this->gateway->cancelGuarantee($caseId);
  334. $this->assertEquals(Gateway::GUARANTEE_CANCELED, $result);
  335. }
  336. /**
  337. * @return array
  338. */
  339. public function supportedGuaranteeDispositionsProvider()
  340. {
  341. return [
  342. 'APPROVED' => ['APPROVED'],
  343. 'DECLINED' => ['DECLINED'],
  344. 'PENDING' => ['PENDING'],
  345. 'CANCELED' => ['CANCELED'],
  346. 'IN_REVIEW' => ['IN_REVIEW'],
  347. 'UNREQUESTED' => ['UNREQUESTED'],
  348. ];
  349. }
  350. /**
  351. * Specifies order entity mock execution.
  352. *
  353. * @param int $orderId
  354. * @param int $storeId
  355. * @return void
  356. */
  357. private function withOrderEntity(int $orderId, int $storeId): void
  358. {
  359. $orderEntity = $this->getMockBuilder(OrderInterface::class)
  360. ->disableOriginalConstructor()
  361. ->getMock();
  362. $orderEntity->method('getStoreId')
  363. ->willReturn($storeId);
  364. $this->orderRepository->method('get')
  365. ->with($orderId)
  366. ->willReturn($orderEntity);
  367. }
  368. /**
  369. * Specifies case entity mock execution.
  370. *
  371. * @param int $caseId
  372. * @param int $storeId
  373. * @return void
  374. */
  375. private function withCaseEntity(int $caseId, int $storeId): void
  376. {
  377. $orderId = 1;
  378. $caseEntity = $this->getMockBuilder(CaseInterface::class)
  379. ->disableOriginalConstructor()
  380. ->getMock();
  381. $caseEntity->method('getOrderId')
  382. ->willReturn($orderId);
  383. $this->caseRepository->method('getByCaseId')
  384. ->with($caseId)
  385. ->willReturn($caseEntity);
  386. $this->withOrderEntity($orderId, $storeId);
  387. }
  388. }