/tests/ClientTest.php

https://gitlab.com/twinscom/notifier-php-client · PHP · 450 lines · 354 code · 85 blank · 11 comment · 0 complexity · 26c37f9002b472561f918d3f6211777b MD5 · raw file

  1. <?php
  2. declare(strict_types=1);
  3. namespace twinscom\Notifier\Tests;
  4. use GuzzleHttp\Client as GuzzleHttpClient;
  5. use GuzzleHttp\Exception\RequestException;
  6. use GuzzleHttp\Exception\TransferException;
  7. use GuzzleHttp\Handler\MockHandler;
  8. use GuzzleHttp\HandlerStack;
  9. use GuzzleHttp\Middleware;
  10. use GuzzleHttp\Psr7\Request;
  11. use GuzzleHttp\Psr7\Response;
  12. use PHPUnit\Framework\TestCase;
  13. use twinscom\GuzzleComponents\RetryDecider;
  14. use twinscom\Notifier\Client;
  15. /**
  16. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  17. *
  18. * @internal
  19. */
  20. final class ClientTest extends TestCase
  21. {
  22. public function testSend(): void
  23. {
  24. $this->send('sendEmail', '/email');
  25. $this->send('sendPush', '/push');
  26. }
  27. public function testOnSuccess(): void
  28. {
  29. $this->onSuccess('sendEmail');
  30. $this->onSuccess('sendPush');
  31. }
  32. public function testOnError(): void
  33. {
  34. $this->onError('sendEmail');
  35. $this->onError('sendPush');
  36. }
  37. public function testTransferException(): void
  38. {
  39. $this->transferException('sendEmail');
  40. $this->transferException('sendPush');
  41. }
  42. public function testClientError(): void
  43. {
  44. $this->clientError('sendEmail');
  45. $this->clientError('sendPush');
  46. }
  47. public function testResponselessException(): void
  48. {
  49. $this->responselessException('sendEmail');
  50. $this->responselessException('sendPush');
  51. }
  52. public function testGuzzleInstantiation(): void
  53. {
  54. $baseUri = 'http://localhost/test/';
  55. $notifierClient = new Client([
  56. 'baseUri' => $baseUri,
  57. 'apiKey' => 'test-api-key',
  58. ]);
  59. $reflectionClass = new \ReflectionClass($notifierClient);
  60. $paramsProperty = $reflectionClass->getProperty('params');
  61. $paramsProperty->setAccessible(true);
  62. $notifierClientParams = $paramsProperty->getValue($notifierClient);
  63. $guzzleHttpClient = $notifierClientParams['guzzleHttpClient'];
  64. self::assertInstanceOf(GuzzleHttpClient::class, $guzzleHttpClient);
  65. self::assertSame(
  66. $baseUri,
  67. (string) $guzzleHttpClient->getConfig('base_uri')
  68. );
  69. self::assertContains(
  70. 'Middleware::retry',
  71. $guzzleHttpClient->getConfig('handler')->__toString()
  72. );
  73. }
  74. public function testRetryMechanism(): void
  75. {
  76. $mockHandler = new MockHandler([
  77. new Response(500, [], json_encode([
  78. 'message' => 'You should not see it',
  79. ])),
  80. new Response(200, [], '[123]'),
  81. ]);
  82. $container = [];
  83. $notifierClient = new Client([
  84. 'guzzleHttpClient' => $this->getGuzzleHttpClient(
  85. $mockHandler,
  86. $container
  87. ),
  88. 'apiKey' => 'test-api-key',
  89. ]);
  90. $message = [
  91. 'key' => 'value',
  92. ];
  93. $notifierClient->sendEmail([
  94. 'templateId' => 'welcome',
  95. 'message' => $message,
  96. ]);
  97. $request = $container[1]['request'];
  98. assert($request instanceof Request);
  99. self::assertSame('POST', $request->getMethod());
  100. self::assertSame('/email', $request->getUri()->getPath());
  101. self::assertContains(
  102. 'template-id=welcome',
  103. $request->getUri()->getQuery()
  104. );
  105. self::assertContains(
  106. 'api-key=test-api-key',
  107. $request->getUri()->getQuery()
  108. );
  109. self::assertSame(
  110. $message,
  111. json_decode($request->getBody()->getContents(), true)
  112. );
  113. }
  114. /**
  115. * @SuppressWarnings(PHPMD.StaticAccess)
  116. */
  117. private function getGuzzleHttpClient(
  118. MockHandler $mockHandler,
  119. array &$container
  120. ) {
  121. $history = Middleware::history($container);
  122. $handlerStack = HandlerStack::create($mockHandler);
  123. $handlerStack->push(
  124. Middleware::retry(
  125. RetryDecider::make(1)
  126. )
  127. );
  128. $handlerStack->push($history);
  129. return new GuzzleHttpClient([
  130. 'handler' => $handlerStack,
  131. ]);
  132. }
  133. private function send(string $methodName, string $path): void
  134. {
  135. $mockHandler = new MockHandler([
  136. new Response(202, [], ''),
  137. ]);
  138. $container = [];
  139. $guzzleHttpClient = $this->getGuzzleHttpClient(
  140. $mockHandler,
  141. $container
  142. );
  143. $notifierClient = new Client([
  144. 'guzzleHttpClient' => $guzzleHttpClient,
  145. 'apiKey' => 'test-api-key',
  146. ]);
  147. $message = [
  148. 'key' => 'value',
  149. ];
  150. $notifierClient->{$methodName}([
  151. 'templateId' => 'welcome',
  152. 'message' => $message,
  153. ]);
  154. $request = $container[0]['request'];
  155. assert($request instanceof Request);
  156. self::assertSame('POST', $request->getMethod());
  157. self::assertSame($path, $request->getUri()->getPath());
  158. self::assertContains(
  159. 'template-id=welcome',
  160. $request->getUri()->getQuery()
  161. );
  162. self::assertContains(
  163. 'api-key=test-api-key',
  164. $request->getUri()->getQuery()
  165. );
  166. self::assertSame(
  167. $message,
  168. json_decode($request->getBody()->getContents(), true)
  169. );
  170. }
  171. private function onSuccess(string $methodName): void
  172. {
  173. $mockHandler = new MockHandler([
  174. new Response(202, [], ''),
  175. ]);
  176. $container = [];
  177. $guzzleHttpClient = $this->getGuzzleHttpClient(
  178. $mockHandler,
  179. $container
  180. );
  181. $onSuccessMock = $this
  182. ->getMockBuilder(\stdClass::class)
  183. ->setMethods(['onSuccess'])
  184. ->getMock();
  185. $notifierClient = new Client([
  186. 'guzzleHttpClient' => $guzzleHttpClient,
  187. 'apiKey' => 'test-api-key',
  188. ]);
  189. $message = [
  190. 'key' => 'value',
  191. ];
  192. $onSuccessMock
  193. ->expects(self::once())
  194. ->method('onSuccess');
  195. $notifierClient->{$methodName}([
  196. 'templateId' => 'welcome',
  197. 'message' => $message,
  198. 'onSuccess' => [$onSuccessMock, 'onSuccess'],
  199. ]);
  200. }
  201. private function onError(string $methodName): void
  202. {
  203. $response = [
  204. 'code' => 'UnauthorizedError',
  205. 'message' => 'Unauthorized',
  206. ];
  207. $mockHandler = new MockHandler([
  208. new Response(401, [], json_encode($response)),
  209. new Response(401, [], json_encode($response)),
  210. ]);
  211. $container = [];
  212. $guzzleHttpClient = $this->getGuzzleHttpClient(
  213. $mockHandler,
  214. $container
  215. );
  216. $onSuccessMock = $this
  217. ->getMockBuilder(\stdClass::class)
  218. ->setMethods(['onSuccess'])
  219. ->getMock();
  220. $onErrorMock = $this
  221. ->getMockBuilder(\stdClass::class)
  222. ->setMethods(['onError'])
  223. ->getMock();
  224. $notifierClient = new Client([
  225. 'guzzleHttpClient' => $guzzleHttpClient,
  226. 'apiKey' => 'test-api-key',
  227. ]);
  228. $message = [
  229. 'key' => 'value',
  230. ];
  231. $onSuccessMock
  232. ->expects(self::never())
  233. ->method('onSuccess');
  234. $onErrorMock
  235. ->expects(self::once())
  236. ->method('onError')
  237. ->with(self::equalTo($response['message']));
  238. // This should not throw:
  239. $notifierClient->{$methodName}([
  240. 'templateId' => 'welcome',
  241. 'message' => $message,
  242. ]);
  243. $notifierClient->{$methodName}([
  244. 'templateId' => 'welcome',
  245. 'message' => $message,
  246. 'onError' => [$onErrorMock, 'onError'],
  247. 'onSuccess' => [$onSuccessMock, 'onSuccess'],
  248. ]);
  249. }
  250. private function transferException(string $methodName): void
  251. {
  252. // It has to be 2 responses because of the retry mechanism
  253. $mockHandler = new MockHandler([
  254. new TransferException('TransferException message'),
  255. new TransferException('TransferException message'),
  256. ]);
  257. $container = [];
  258. $guzzleHttpClient = $this->getGuzzleHttpClient(
  259. $mockHandler,
  260. $container
  261. );
  262. $onSuccessMock = $this
  263. ->getMockBuilder(\stdClass::class)
  264. ->setMethods(['onSuccess'])
  265. ->getMock();
  266. $onErrorMock = $this
  267. ->getMockBuilder(\stdClass::class)
  268. ->setMethods(['onError'])
  269. ->getMock();
  270. $notifierClient = new Client([
  271. 'guzzleHttpClient' => $guzzleHttpClient,
  272. 'apiKey' => 'test-api-key',
  273. ]);
  274. $message = [
  275. 'key' => 'value',
  276. ];
  277. $onSuccessMock
  278. ->expects(self::never())
  279. ->method('onSuccess');
  280. $onErrorMock
  281. ->expects(self::once())
  282. ->method('onError')
  283. ->with(self::equalTo('TransferException message'));
  284. $notifierClient->{$methodName}([
  285. 'templateId' => 'welcome',
  286. 'message' => $message,
  287. 'onError' => [$onErrorMock, 'onError'],
  288. 'onSuccess' => [$onSuccessMock, 'onSuccess'],
  289. ]);
  290. }
  291. private function clientError(string $methodName): void
  292. {
  293. $response = [
  294. 'code' => 'some code',
  295. 'message' => 'response message',
  296. ];
  297. $mockHandler = new MockHandler([
  298. new Response(401, [], json_encode($response)),
  299. ]);
  300. $container = [];
  301. $guzzleHttpClient = $this->getGuzzleHttpClient(
  302. $mockHandler,
  303. $container
  304. );
  305. $onErrorMock = $this
  306. ->getMockBuilder(\stdClass::class)
  307. ->setMethods(['onError'])
  308. ->getMock();
  309. $notifierClient = new Client([
  310. 'guzzleHttpClient' => $guzzleHttpClient,
  311. 'apiKey' => 'test-api-key',
  312. ]);
  313. $message = [
  314. 'key' => 'value',
  315. ];
  316. $onErrorMock
  317. ->expects(self::once())
  318. ->method('onError')
  319. ->with(self::equalTo($response['message']));
  320. $notifierClient->{$methodName}([
  321. 'templateId' => 'welcome',
  322. 'message' => $message,
  323. 'onError' => [$onErrorMock, 'onError'],
  324. ]);
  325. }
  326. private function responselessException(string $methodName): void
  327. {
  328. // It has to be 2 responses because of the retry mechanism
  329. $mockHandler = new MockHandler([
  330. new RequestException(
  331. 'RequestException message',
  332. new Request('POST', '/email')
  333. ),
  334. new RequestException(
  335. 'RequestException message',
  336. new Request('POST', '/email')
  337. ),
  338. ]);
  339. $container = [];
  340. $guzzleHttpClient = $this->getGuzzleHttpClient(
  341. $mockHandler,
  342. $container
  343. );
  344. $onErrorMock = $this
  345. ->getMockBuilder(\stdClass::class)
  346. ->setMethods(['onError'])
  347. ->getMock();
  348. $notifierClient = new Client([
  349. 'guzzleHttpClient' => $guzzleHttpClient,
  350. 'apiKey' => 'test-api-key',
  351. ]);
  352. $message = [
  353. 'key' => 'value',
  354. ];
  355. $onErrorMock
  356. ->expects(self::once())
  357. ->method('onError')
  358. ->with(self::equalTo('RequestException message'));
  359. $notifierClient->{$methodName}([
  360. 'templateId' => 'welcome',
  361. 'message' => $message,
  362. 'onError' => [$onErrorMock, 'onError'],
  363. ]);
  364. }
  365. }