ElasticsearchTest.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Elasticsearch\Test\Unit\Model\Adapter;
  7. use Magento\AdvancedSearch\Model\Client\ClientOptionsInterface;
  8. use Magento\Elasticsearch\Model\Adapter\Elasticsearch as ElasticsearchAdapter;
  9. use Magento\Elasticsearch\SearchAdapter\ConnectionManager;
  10. use Magento\Elasticsearch\Model\Adapter\BatchDataMapperInterface;
  11. use Magento\Elasticsearch\Model\Adapter\FieldMapperInterface;
  12. use Magento\Elasticsearch\Model\Adapter\Index\BuilderInterface;
  13. use Psr\Log\LoggerInterface;
  14. use Magento\Elasticsearch\Model\Client\Elasticsearch as ElasticsearchClient;
  15. use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
  16. use Magento\Elasticsearch\Model\Adapter\Index\IndexNameResolver;
  17. /**
  18. * Class ElasticsearchTest
  19. *
  20. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  21. */
  22. class ElasticsearchTest extends \PHPUnit\Framework\TestCase
  23. {
  24. /**
  25. * @var ElasticsearchAdapter
  26. */
  27. protected $model;
  28. /**
  29. * @var ConnectionManager|\PHPUnit_Framework_MockObject_MockObject
  30. */
  31. protected $connectionManager;
  32. /**
  33. * @var BatchDataMapperInterface|\PHPUnit_Framework_MockObject_MockObject
  34. */
  35. protected $batchDocumentDataMapper;
  36. /**
  37. * @var FieldMapperInterface|\PHPUnit_Framework_MockObject_MockObject
  38. */
  39. protected $fieldMapper;
  40. /**
  41. * @var ClientOptionsInterface|\PHPUnit_Framework_MockObject_MockObject
  42. */
  43. protected $clientConfig;
  44. /**
  45. * @var BuilderInterface|\PHPUnit_Framework_MockObject_MockObject
  46. */
  47. protected $indexBuilder;
  48. /**
  49. * @var LoggerInterface|\PHPUnit_Framework_MockObject_MockObject
  50. */
  51. protected $logger;
  52. /**
  53. * @var ElasticsearchClient|\PHPUnit_Framework_MockObject_MockObject
  54. */
  55. protected $client;
  56. /**
  57. * @var ObjectManagerHelper
  58. */
  59. protected $objectManager;
  60. /**
  61. * @var IndexNameResolver|\PHPUnit_Framework_MockObject_MockObject
  62. */
  63. protected $indexNameResolver;
  64. /**
  65. * Setup
  66. *
  67. * @return void
  68. */
  69. protected function setUp()
  70. {
  71. $this->objectManager = new ObjectManagerHelper($this);
  72. $this->connectionManager = $this->getMockBuilder(\Magento\Elasticsearch\SearchAdapter\ConnectionManager::class)
  73. ->disableOriginalConstructor()
  74. ->setMethods(['getConnection'])
  75. ->getMock();
  76. $this->documentDataMapper = $this->getMockBuilder(
  77. \Magento\Elasticsearch\Model\Adapter\DataMapperInterface::class
  78. )->disableOriginalConstructor()->getMock();
  79. $this->fieldMapper = $this->getMockBuilder(\Magento\Elasticsearch\Model\Adapter\FieldMapperInterface::class)
  80. ->disableOriginalConstructor()
  81. ->getMock();
  82. $this->clientConfig = $this->getMockBuilder(\Magento\Elasticsearch\Model\Config::class)
  83. ->disableOriginalConstructor()
  84. ->setMethods([
  85. 'getIndexPrefix',
  86. 'getEntityType',
  87. ])->getMock();
  88. $this->indexBuilder = $this->getMockBuilder(\Magento\Elasticsearch\Model\Adapter\Index\BuilderInterface::class)
  89. ->disableOriginalConstructor()
  90. ->getMock();
  91. $this->logger = $this->getMockBuilder(\Psr\Log\LoggerInterface::class)
  92. ->disableOriginalConstructor()
  93. ->getMock();
  94. $elasticsearchClientMock = $this->getMockBuilder(\Elasticsearch\Client::class)
  95. ->setMethods([
  96. 'indices',
  97. 'ping',
  98. 'bulk',
  99. 'search',
  100. ])
  101. ->disableOriginalConstructor()
  102. ->getMock();
  103. $indicesMock = $this->getMockBuilder(\Elasticsearch\Namespaces\IndicesNamespace::class)
  104. ->setMethods([
  105. 'exists',
  106. 'getSettings',
  107. 'create',
  108. 'putMapping',
  109. 'deleteMapping',
  110. 'existsAlias',
  111. 'updateAliases',
  112. 'stats'
  113. ])
  114. ->disableOriginalConstructor()
  115. ->getMock();
  116. $elasticsearchClientMock->expects($this->any())
  117. ->method('indices')
  118. ->willReturn($indicesMock);
  119. $this->client = $this->getMockBuilder(\Magento\Elasticsearch\Model\Client\Elasticsearch::class)
  120. ->setConstructorArgs([
  121. 'options' => $this->getClientOptions(),
  122. 'elasticsearchClient' => $elasticsearchClientMock
  123. ])
  124. ->getMock();
  125. $this->connectionManager->expects($this->any())
  126. ->method('getConnection')
  127. ->willReturn($this->client);
  128. $this->fieldMapper->expects($this->any())
  129. ->method('getAllAttributesTypes')
  130. ->willReturn([
  131. 'name' => 'string',
  132. ]);
  133. $this->clientConfig->expects($this->any())
  134. ->method('getIndexPrefix')
  135. ->willReturn('indexName');
  136. $this->clientConfig->expects($this->any())
  137. ->method('getEntityType')
  138. ->willReturn('product');
  139. $this->indexNameResolver = $this->getMockBuilder(
  140. \Magento\Elasticsearch\Model\Adapter\Index\IndexNameResolver::class
  141. )
  142. ->setMethods([
  143. 'getIndexName',
  144. 'getIndexNamespace',
  145. 'getIndexFromAlias',
  146. 'getIndexNameForAlias',
  147. ])
  148. ->disableOriginalConstructor()
  149. ->getMock();
  150. $this->batchDocumentDataMapper = $this->getMockBuilder(
  151. \Magento\Elasticsearch\Model\Adapter\BatchDataMapperInterface::class
  152. )->disableOriginalConstructor()
  153. ->getMock();
  154. $this->model = $this->objectManager->getObject(
  155. \Magento\Elasticsearch\Model\Adapter\Elasticsearch::class,
  156. [
  157. 'connectionManager' => $this->connectionManager,
  158. 'batchDocumentDataMapper' => $this->batchDocumentDataMapper,
  159. 'fieldMapper' => $this->fieldMapper,
  160. 'clientConfig' => $this->clientConfig,
  161. 'indexBuilder' => $this->indexBuilder,
  162. 'logger' => $this->logger,
  163. 'indexNameResolver' => $this->indexNameResolver,
  164. 'options' => [],
  165. ]
  166. );
  167. }
  168. /**
  169. * Test ping() method
  170. */
  171. public function testPing()
  172. {
  173. $this->client->expects($this->once())
  174. ->method('ping')
  175. ->willReturn(true);
  176. $this->assertEquals(true, $this->model->ping());
  177. }
  178. /**
  179. * Test ping() method
  180. * @expectedException \Magento\Framework\Exception\LocalizedException
  181. */
  182. public function testPingFailure()
  183. {
  184. $this->client->expects($this->once())
  185. ->method('ping')
  186. ->willThrowException(new \Exception('Something went wrong'));
  187. $this->model->ping();
  188. }
  189. /**
  190. * Test prepareDocsPerStore() method
  191. */
  192. public function testPrepareDocsPerStoreEmpty()
  193. {
  194. $this->assertEquals([], $this->model->prepareDocsPerStore([], 1));
  195. }
  196. /**
  197. * Test prepareDocsPerStore() method
  198. */
  199. public function testPrepareDocsPerStore()
  200. {
  201. $this->batchDocumentDataMapper->expects($this->once())
  202. ->method('map')
  203. ->willReturn([
  204. 'name' => 'Product Name',
  205. ]);
  206. $this->assertInternalType(
  207. 'array',
  208. $this->model->prepareDocsPerStore(
  209. [
  210. '1' => [
  211. 'name' => 'Product Name',
  212. ],
  213. ],
  214. 1
  215. )
  216. );
  217. }
  218. /**
  219. * Test addDocs() method
  220. */
  221. public function testAddDocs()
  222. {
  223. $this->client->expects($this->once())
  224. ->method('bulkQuery');
  225. $this->assertSame(
  226. $this->model,
  227. $this->model->addDocs(
  228. [
  229. '1' => [
  230. 'name' => 'Product Name',
  231. ],
  232. ],
  233. 1,
  234. 'product'
  235. )
  236. );
  237. }
  238. /**
  239. * Test addDocs() method
  240. * @expectedException \Exception
  241. */
  242. public function testAddDocsFailure()
  243. {
  244. $this->client->expects($this->once())
  245. ->method('bulkQuery')
  246. ->willThrowException(new \Exception('Something went wrong'));
  247. $this->model->addDocs(
  248. [
  249. '1' => [
  250. 'name' => 'Product Name',
  251. ],
  252. ],
  253. 1,
  254. 'product'
  255. );
  256. }
  257. /**
  258. * Test cleanIndex() method
  259. */
  260. public function testCleanIndex()
  261. {
  262. $this->indexNameResolver->expects($this->any())
  263. ->method('getIndexName')
  264. ->with(1, 'product', [])
  265. ->willReturn('indexName_product_1_v');
  266. $this->client->expects($this->once())
  267. ->method('isEmptyIndex')
  268. ->with('indexName_product_1_v')
  269. ->willReturn(false);
  270. $this->client->expects($this->atLeastOnce())
  271. ->method('indexExists')
  272. ->willReturn(true);
  273. $this->client->expects($this->once())
  274. ->method('deleteIndex')
  275. ->with('_product_1_v1');
  276. $this->assertSame(
  277. $this->model,
  278. $this->model->cleanIndex(1, 'product')
  279. );
  280. }
  281. /**
  282. * Test cleanIndex() method isEmptyIndex is true
  283. */
  284. public function testCleanIndexTrue()
  285. {
  286. $this->indexNameResolver->expects($this->any())
  287. ->method('getIndexName')
  288. ->willReturn('indexName_product_1_v');
  289. $this->client->expects($this->once())
  290. ->method('isEmptyIndex')
  291. ->with('indexName_product_1_v')
  292. ->willReturn(true);
  293. $this->assertSame(
  294. $this->model,
  295. $this->model->cleanIndex(1, 'product')
  296. );
  297. }
  298. /**
  299. * Test deleteDocs() method
  300. */
  301. public function testDeleteDocs()
  302. {
  303. $this->client->expects($this->once())
  304. ->method('bulkQuery');
  305. $this->assertSame(
  306. $this->model,
  307. $this->model->deleteDocs(['1' => 1], 1, 'product')
  308. );
  309. }
  310. /**
  311. * Test deleteDocs() method
  312. * @expectedException \Exception
  313. */
  314. public function testDeleteDocsFailure()
  315. {
  316. $this->client->expects($this->once())
  317. ->method('bulkQuery')
  318. ->willThrowException(new \Exception('Something went wrong'));
  319. $this->model->deleteDocs(['1' => 1], 1, 'product');
  320. }
  321. /**
  322. * Test updateAlias() method
  323. */
  324. public function testUpdateAliasEmpty()
  325. {
  326. $model = $this->objectManager->getObject(
  327. \Magento\Elasticsearch\Model\Adapter\Elasticsearch::class,
  328. [
  329. 'connectionManager' => $this->connectionManager,
  330. 'batchDocumentDataMapper' => $this->batchDocumentDataMapper,
  331. 'fieldMapper' => $this->fieldMapper,
  332. 'clientConfig' => $this->clientConfig,
  333. 'indexBuilder' => $this->indexBuilder,
  334. 'logger' => $this->logger,
  335. 'indexNameResolver' => $this->indexNameResolver,
  336. 'options' => []
  337. ]
  338. );
  339. $this->client->expects($this->never())
  340. ->method('updateAlias');
  341. $this->assertEquals($model, $model->updateAlias(1, 'product'));
  342. }
  343. /**
  344. * @expectedException \Magento\Framework\Exception\LocalizedException
  345. */
  346. public function testConnectException()
  347. {
  348. $connectionManager = $this->getMockBuilder(\Magento\Elasticsearch\SearchAdapter\ConnectionManager::class)
  349. ->disableOriginalConstructor()
  350. ->setMethods([
  351. 'getConnection',
  352. ])
  353. ->getMock();
  354. $connectionManager->expects($this->any())
  355. ->method('getConnection')
  356. ->willThrowException(new \Exception('Something went wrong'));
  357. $this->objectManager->getObject(
  358. \Magento\Elasticsearch\Model\Adapter\Elasticsearch::class,
  359. [
  360. 'connectionManager' => $connectionManager,
  361. 'batchDocumentDataMapper' => $this->batchDocumentDataMapper,
  362. 'fieldMapper' => $this->fieldMapper,
  363. 'clientConfig' => $this->clientConfig,
  364. 'indexBuilder' => $this->indexBuilder,
  365. 'logger' => $this->logger,
  366. 'indexNameResolver' => $this->indexNameResolver,
  367. 'options' => []
  368. ]
  369. );
  370. }
  371. /**
  372. * Test updateAlias() method
  373. */
  374. public function testUpdateAlias()
  375. {
  376. $this->client->expects($this->atLeastOnce())
  377. ->method('updateAlias');
  378. $this->indexNameResolver->expects($this->any())
  379. ->method('getIndexFromAlias')
  380. ->willReturn('_product_1_v1');
  381. $this->model->cleanIndex(1, 'product');
  382. $this->assertEquals($this->model, $this->model->updateAlias(1, 'product'));
  383. }
  384. /**
  385. * Test updateAlias() method
  386. */
  387. public function testUpdateAliasWithOldIndex()
  388. {
  389. $this->model->cleanIndex(1, 'product');
  390. $this->indexNameResolver->expects($this->any())
  391. ->method('getIndexFromAlias')
  392. ->willReturn('_product_1_v2');
  393. $this->indexNameResolver->expects($this->any())
  394. ->method('getIndexNameForAlias')
  395. ->willReturn('_product_1_v2');
  396. $this->client->expects($this->any())
  397. ->method('existsAlias')
  398. ->with('indexName')
  399. ->willReturn(true);
  400. $this->client->expects($this->any())
  401. ->method('getAlias')
  402. ->with('indexName')
  403. ->willReturn(['indexName_product_1_v' => 'indexName_product_1_v']);
  404. $this->assertEquals($this->model, $this->model->updateAlias(1, 'product'));
  405. }
  406. /**
  407. * Test updateAlias() method
  408. */
  409. public function testUpdateAliasWithoutOldIndex()
  410. {
  411. $this->model->cleanIndex(1, 'product');
  412. $this->client->expects($this->any())
  413. ->method('existsAlias')
  414. ->with('indexName')
  415. ->willReturn(true);
  416. $this->client->expects($this->any())
  417. ->method('getAlias')
  418. ->with('indexName')
  419. ->willReturn(['indexName_product_1_v2' => 'indexName_product_1_v2']);
  420. $this->assertEquals($this->model, $this->model->updateAlias(1, 'product'));
  421. }
  422. /**
  423. * Get elasticsearch client options
  424. *
  425. * @return array
  426. */
  427. protected function getClientOptions()
  428. {
  429. return [
  430. 'hostname' => 'localhost',
  431. 'port' => '9200',
  432. 'timeout' => 15,
  433. 'index' => 'magento2',
  434. 'enableAuth' => 1,
  435. 'username' => 'user',
  436. 'password' => 'my-password',
  437. ];
  438. }
  439. }