Completed
Push — master ( 7ea182...abc512 )
by Eugene
05:35
created

ConnectionTest::testConnectTimedOut()   B

Complexity

Conditions 5
Paths 104

Size

Total Lines 38

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
nc 104
nop 0
dl 0
loc 38
rs 8.9742
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * This file is part of the tarantool/client package.
5
 *
6
 * (c) Eugene Leonovich <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace Tarantool\Client\Tests\Integration\Connection;
15
16
use Tarantool\Client\Exception\CommunicationFailed;
17
use Tarantool\Client\Exception\ConnectionFailed;
18
use Tarantool\Client\Exception\UnexpectedResponse;
19
use Tarantool\Client\Request\PingRequest;
20
use Tarantool\Client\Schema\Criteria;
21
use Tarantool\Client\Schema\Operations;
22
use Tarantool\Client\Tests\GreetingDataProvider;
23
use Tarantool\Client\Tests\Integration\ClientBuilder;
24
use Tarantool\Client\Tests\Integration\FakeServer\FakeServerBuilder;
25
use Tarantool\Client\Tests\Integration\FakeServer\Handler\AtConnectionHandler;
26
use Tarantool\Client\Tests\Integration\FakeServer\Handler\WriteHandler;
27
use Tarantool\Client\Tests\Integration\TestCase;
28
29
final class ConnectionTest extends TestCase
30
{
31
    /**
32
     * @dataProvider provideAutoConnectData
33
     * @doesNotPerformAssertions
34
     *
35
     * @lua create_space('test_auto_connect'):create_index('primary', {type = 'tree', parts = {1, 'unsigned'}})
36
     */
37
    public function testAutoConnect(string $methodName, array $methodArgs, ?string $space = null) : void
38
    {
39
        $object = $space ? $this->client->getSpace($space) : $this->client;
40
        $this->client->getHandler()->getConnection()->close();
41
42
        $object->$methodName(...$methodArgs);
43
    }
44
45
    public function provideAutoConnectData() : iterable
46
    {
47
        return [
48
            ['ping', []],
49
            ['call', ['box.stat']],
50
            ['evaluate', ['return 1']],
51
52
            ['select', [Criteria::key([42])], 'test_auto_connect'],
53
            ['insert', [[time()]], 'test_auto_connect'],
54
            ['replace', [[1, 2]], 'test_auto_connect'],
55
            ['update', [[1], Operations::add(1, 2)], 'test_auto_connect'],
56
            ['delete', [[1]], 'test_auto_connect'],
57
        ];
58
    }
59
60
    public function testMultipleConnect() : void
61
    {
62
        $conn = $this->client->getHandler()->getConnection();
63
64
        self::assertTrue($conn->isClosed());
65
66
        $conn->open();
67
        self::assertFalse($conn->isClosed());
68
69
        $conn->open();
70
        self::assertFalse($conn->isClosed());
71
    }
72
73
    public function tesMultipleDisconnect() : void
74
    {
75
        $conn = $this->client->getHandler()->getConnection();
76
77
        $conn->open();
78
        self::assertFalse($conn->isClosed());
79
80
        $conn->close();
81
        self::assertTrue($conn->isClosed());
82
83
        $conn->close();
84
        self::assertTrue($conn->isClosed());
85
    }
86
87
    public function testReturnSameGreeting() : void
88
    {
89
        $conn = $this->client->getHandler()->getConnection();
90
91
        $greeting1 = $conn->open();
92
        $greeting2 = $conn->open();
93
94
        self::assertSame($greeting1, $greeting2);
95
    }
96
97
    public function testReturnNewGreeting() : void
98
    {
99
        $conn = $this->client->getHandler()->getConnection();
100
101
        $greeting1 = $conn->open();
102
        $conn->close();
103
        $greeting2 = $conn->open();
104
105
        self::assertNotSame($greeting1, $greeting2);
106
    }
107
108
    public function testConnectInvalidHost() : void
109
    {
110
        $builder = ClientBuilder::createFromEnv()
111
            ->setHost('invalid_host');
112
113
        if (!$builder->isTcpConnection()) {
114
            self::markTestSkipped(sprintf('For the tcp connections only (current: "%s")', $builder->getUri()));
115
        }
116
117
        $client = $builder->build();
118
119
        $this->expectException(ConnectionFailed::class);
120
        $client->ping();
121
    }
122
123
    public function testConnectInvalidPort() : void
124
    {
125
        $builder = ClientBuilder::createFromEnv()
126
            ->setPort(123456);
127
128
        if (!$builder->isTcpConnection()) {
129
            self::markTestSkipped(sprintf('For the tcp connections only (current: "%s")', $builder->getUri()));
130
        }
131
132
        $client = $builder->build();
133
134
        $this->expectException(ConnectionFailed::class);
135
        $client->ping();
136
    }
137
138
    public function testConnectTimedOut() : void
139
    {
140
        $builder = ClientBuilder::createFromEnvForTheFakeServer();
141
        if (!$builder->isTcpConnection()) {
142
            self::markTestSkipped(sprintf('For tcp connections only (current: "%s")', $builder->getUri()));
143
        }
144
145
        $connectTimeout = 2;
146
        $builder->setConnectionOptions(['connect_timeout' => $connectTimeout]);
147
        $client = $builder->build();
148
149
        // STREAM_SERVER_BIND is used here intentionally with the tcp transport to simulate timeout error
150
        $socket = stream_socket_server($builder->getUri(), $errno, $errstr, STREAM_SERVER_BIND);
151
        if (!$socket) {
152
            self::markTestSkipped("Unable to create socket server: $errstr ($errno)");
153
        }
154
155
        $start = microtime(true);
156
157
        try {
158
            $client->ping();
159
        } catch (ConnectionFailed $e) {
160
            if (false !== strpos($e->getMessage(), 'No route to host')) {
161
                self::markTestSkipped(sprintf('Unable to route to host %s', $host));
0 ignored issues
show
Bug introduced by
The variable $host does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
162
            }
163
164
            $time = microtime(true) - $start;
165
            self::assertRegExp('/Failed to connect to .+?: (Connection|Operation) timed out/', $e->getMessage());
0 ignored issues
show
Deprecated Code introduced by
The method PHPUnit\Framework\Assert::assertRegExp() has been deprecated with message: https://github.com/sebastianbergmann/phpunit/issues/4086

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
166
            self::assertGreaterThanOrEqual($connectTimeout, $time);
167
            self::assertLessThanOrEqual($connectTimeout + 0.1, $time);
168
169
            return;
170
        } finally {
171
            fclose($socket);
172
        }
173
174
        self::fail();
175
    }
176
177
    public function testUnexpectedResponse() : void
178
    {
179
        $client = ClientBuilder::createFromEnv()->build();
180
        $connection = self::triggerUnexpectedResponse($client->getHandler(), new PingRequest());
181
182
        // Tarantool will answer with the ping response
183
        try {
184
            $client->evaluate('return 42');
185
        } catch (UnexpectedResponse $e) {
186
            self::assertTrue($connection->isClosed());
187
188
            return;
189
        }
190
191
        self::fail(UnexpectedResponse::class.' was not thrown');
192
    }
193
194
    public function testOpenConnectionHandlesTheMissingGreetingCorrectly() : void
195
    {
196
        $clientBuilder = ClientBuilder::createFromEnvForTheFakeServer();
197
198
        FakeServerBuilder::create(
199
            new AtConnectionHandler(1, new WriteHandler('')),
200
            new AtConnectionHandler(2, new WriteHandler(GreetingDataProvider::generateGreeting()))
201
        )
202
            ->setUri($clientBuilder->getUri())
203
            ->start();
204
205
        $client = $clientBuilder->build();
206
        $connection = $client->getHandler()->getConnection();
207
208
        try {
209
            $connection->open();
210
            self::fail('Connection not established');
211
        } catch (CommunicationFailed $e) {
212
            self::assertSame('Unable to read greeting', $e->getMessage());
213
            // at that point the connection was successfully established,
214
            // but the greeting message was not read
215
        }
216
217
        // the second call should correctly handle
218
        // the missing greeting from the previous call
219
        $connection->open();
220
    }
221
}
222