1<?php
2
3declare(strict_types = 1);
4
5namespace Elasticsearch\Tests\ConnectionPool;
6
7use Elasticsearch;
8use Elasticsearch\ConnectionPool\Selectors\RoundRobinSelector;
9use Elasticsearch\ConnectionPool\StaticConnectionPool;
10use Elasticsearch\Connections\Connection;
11use Elasticsearch\Connections\ConnectionFactory;
12use Mockery as m;
13
14/**
15 * Class StaticConnectionPoolTest
16 *
17 * @category   Tests
18 * @package    Elasticsearch
19 * @subpackage Tests/StaticConnectionPoolTest
20 * @author     Zachary Tong <zachary.tong@elasticsearch.com>
21 * @license    http://www.apache.org/licenses/LICENSE-2.0 Apache2
22 * @link       http://elasticsearch.org
23 */
24class StaticConnectionPoolTest extends \PHPUnit\Framework\TestCase
25{
26    public function tearDown()
27    {
28        m::close();
29    }
30
31    public function testAddOneHostThenGetConnection()
32    {
33        $mockConnection = m::mock(Connection::class)
34            ->shouldReceive('ping')
35            ->andReturn(true)
36            ->getMock()
37            ->shouldReceive('isAlive')
38            ->andReturn(true)
39            ->getMock()
40            ->shouldReceive('markDead')->once()->getMock();
41
42        /**
43 * @var \Elasticsearch\Connections\Connection[]&\Mockery\MockInterface[] $connections
44*/
45        $connections = [$mockConnection];
46
47        $selector = m::mock(RoundRobinSelector::class)
48            ->shouldReceive('select')
49            ->andReturn($connections[0])
50            ->getMock();
51
52        $connectionFactory = m::mock(ConnectionFactory::class);
53
54        $connectionPoolParams = [
55            'randomizeHosts' => false,
56        ];
57        $connectionPool = new StaticConnectionPool($connections, $selector, $connectionFactory, $connectionPoolParams);
58
59        $retConnection = $connectionPool->nextConnection();
60
61        $this->assertSame($mockConnection, $retConnection);
62    }
63
64    public function testAddMultipleHostsThenGetFirst()
65    {
66        $connections = [];
67
68        foreach (range(1, 10) as $index) {
69            $mockConnection = m::mock(Connection::class)
70                ->shouldReceive('ping')
71                ->andReturn(true)
72                ->getMock()
73                ->shouldReceive('isAlive')
74                ->andReturn(true)
75                ->getMock()
76                ->shouldReceive('markDead')->once()->getMock();
77
78            $connections[] = $mockConnection;
79        }
80
81        $selector = m::mock(RoundRobinSelector::class)
82            ->shouldReceive('select')
83            ->andReturn($connections[0])
84            ->getMock();
85
86        $connectionFactory = m::mock(ConnectionFactory::class);
87
88        $connectionPoolParams = [
89            'randomizeHosts' => false,
90        ];
91        $connectionPool = new StaticConnectionPool($connections, $selector, $connectionFactory, $connectionPoolParams);
92
93        $retConnection = $connectionPool->nextConnection();
94
95        $this->assertSame($connections[0], $retConnection);
96    }
97
98    public function testAllHostsFailPing()
99    {
100        $connections = [];
101
102        foreach (range(1, 10) as $index) {
103            $mockConnection = m::mock(Connection::class)
104                ->shouldReceive('ping')
105                ->andReturn(false)
106                ->getMock()
107                ->shouldReceive('isAlive')
108                ->andReturn(false)
109                ->getMock()
110                ->shouldReceive('markDead')->once()->getMock()
111                ->shouldReceive('getPingFailures')->andReturn(0)->once()->getMock()
112                ->shouldReceive('getLastPing')->andReturn(time())->once()->getMock();
113
114            $connections[] = $mockConnection;
115        }
116
117        $selector = m::mock(RoundRobinSelector::class)
118            ->shouldReceive('select')
119            ->andReturnValues($connections)
120            ->getMock();
121
122        $connectionFactory = m::mock(ConnectionFactory::class);
123
124        $connectionPoolParams = [
125            'randomizeHosts' => false,
126        ];
127        $connectionPool = new StaticConnectionPool($connections, $selector, $connectionFactory, $connectionPoolParams);
128
129        $this->expectException(\Elasticsearch\Common\Exceptions\NoNodesAvailableException::class);
130        $this->expectExceptionMessage('No alive nodes found in your cluster');
131
132        $connectionPool->nextConnection();
133    }
134
135    public function testAllExceptLastHostFailPingRevivesInSkip()
136    {
137        $connections = [];
138
139        foreach (range(1, 9) as $index) {
140            $mockConnection = m::mock(Connection::class)
141                ->shouldReceive('ping')
142                ->andReturn(false)
143                ->getMock()
144                ->shouldReceive('isAlive')
145                ->andReturn(false)
146                ->getMock()
147                ->shouldReceive('markDead')->once()->getMock()
148                ->shouldReceive('getPingFailures')->andReturn(0)->once()->getMock()
149                ->shouldReceive('getLastPing')->andReturn(time())->once()->getMock();
150
151            $connections[] = $mockConnection;
152        }
153
154        $goodConnection = m::mock(Connection::class)
155            ->shouldReceive('ping')->once()
156            ->andReturn(true)
157            ->getMock()
158            ->shouldReceive('isAlive')->once()
159            ->andReturn(false)
160            ->getMock()
161            ->shouldReceive('markDead')->once()->getMock()
162            ->shouldReceive('getPingFailures')->andReturn(0)->once()->getMock()
163            ->shouldReceive('getLastPing')->andReturn(time())->once()->getMock();
164
165        $connections[] = $goodConnection;
166
167        $selector = m::mock(RoundRobinSelector::class)
168            ->shouldReceive('select')
169            ->andReturnValues($connections)
170            ->getMock();
171
172        $connectionFactory = m::mock(ConnectionFactory::class);
173
174        $connectionPoolParams = [
175            'randomizeHosts' => false,
176        ];
177        $connectionPool = new StaticConnectionPool($connections, $selector, $connectionFactory, $connectionPoolParams);
178
179        $ret = $connectionPool->nextConnection();
180        $this->assertSame($goodConnection, $ret);
181    }
182
183    public function testAllExceptLastHostFailPingRevivesPreSkip()
184    {
185        $connections = [];
186
187        foreach (range(1, 9) as $index) {
188            $mockConnection = m::mock(Connection::class)
189                ->shouldReceive('ping')
190                ->andReturn(false)
191                ->getMock()
192                ->shouldReceive('isAlive')
193                ->andReturn(false)
194                ->getMock()
195                ->shouldReceive('markDead')->once()->getMock()
196                ->shouldReceive('getPingFailures')->andReturn(0)->once()->getMock()
197                ->shouldReceive('getLastPing')->andReturn(time())->once()->getMock();
198
199            $connections[] = $mockConnection;
200        }
201
202        $goodConnection = m::mock(Connection::class)
203            ->shouldReceive('ping')->once()
204            ->andReturn(true)
205            ->getMock()
206            ->shouldReceive('isAlive')->once()
207            ->andReturn(false)
208            ->getMock()
209            ->shouldReceive('markDead')->once()->getMock()
210            ->shouldReceive('getPingFailures')->andReturn(0)->once()->getMock()
211            ->shouldReceive('getLastPing')->andReturn(time()-10000)->once()->getMock();
212
213        $connections[] = $goodConnection;
214
215        $selector = m::mock(RoundRobinSelector::class)
216            ->shouldReceive('select')
217            ->andReturnValues($connections)
218            ->getMock();
219
220        $connectionFactory = m::mock(ConnectionFactory::class);
221
222        $connectionPoolParams = [
223            'randomizeHosts' => false,
224        ];
225        $connectionPool = new StaticConnectionPool($connections, $selector, $connectionFactory, $connectionPoolParams);
226
227        $ret = $connectionPool->nextConnection();
228        $this->assertSame($goodConnection, $ret);
229    }
230
231    public function testCustomConnectionPoolIT()
232    {
233        $clientBuilder = \Elasticsearch\ClientBuilder::create();
234        $clientBuilder->setHosts(['localhost:1']);
235        $client = $clientBuilder
236            ->setRetries(0)
237            ->setConnectionPool(StaticConnectionPool::class, [])
238            ->build();
239
240        $this->expectException(Elasticsearch\Common\Exceptions\NoNodesAvailableException::class);
241        $this->expectExceptionMessage('No alive nodes found in your cluster');
242
243        $client->search([]);
244    }
245}
246