Completed
Branch master (00332a)
by Eugene
05:11
created

hp$0   A

Complexity

Total Complexity 1

Size/Duplication

Total Lines 10
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 10
c 0
b 0
f 0
wmc 1
lcom 1
cbo 1
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;
15
16
use Tarantool\Client\Client;
17
use Tarantool\Client\Exception\CommunicationFailed;
18
use Tarantool\Client\Exception\RequestFailed;
19
use Tarantool\Client\Exception\UnexpectedResponse;
20
use Tarantool\Client\Handler\Handler;
21
use Tarantool\Client\Middleware\Middleware;
22
use Tarantool\Client\Middleware\RetryMiddleware;
23
use Tarantool\Client\Request\PingRequest;
24
use Tarantool\Client\Request\Request;
25
use Tarantool\Client\Response;
26
use Tarantool\Client\Schema\Criteria;
27
use Tarantool\Client\Schema\Space;
28
29
final class ClientMiddlewareTest extends TestCase
30
{
31
    public function testSpaceInheritsMiddleware() : void
32
    {
33
        $middleware = new class() implements Middleware {
34
            public $counter = 0;
35
36
            public function process(Request $request, Handler $handler) : Response
37
            {
38
                ++$this->counter;
39
40
                return $handler->handle($request);
41
            }
42
        };
43
44
        // cache the space
45
        $this->client->getSpaceById(Space::VSPACE_ID)->select(Criteria::key([]));
46
47
        $clientWithMiddleware = $this->client->withMiddleware($middleware);
48
49
        $clientWithMiddleware->ping();
50
        self::assertSame(1, $middleware->counter);
51
52
        $spaceWithMiddleware = $clientWithMiddleware->getSpaceById(Space::VSPACE_ID);
53
54
        $spaceWithMiddleware->select(Criteria::key([]));
55
        self::assertSame(2, $middleware->counter);
56
57
        $spaceWithMiddleware->select(Criteria::key([]));
58
        self::assertSame(3, $middleware->counter);
59
    }
60
61
    /**
62
     * @doesNotPerformAssertions
63
     *
64
     * @eval fiber = require('fiber')
65
     * @eval function test() try_drop_user('foobar') fiber.sleep(.5) create_user('foobar', '') end
66
     * @eval fiber.create(test)
67
     */
68
    public function testAuthenticationRetrySucceeds() : void
69
    {
70
        $client = Client::fromOptions([
71
            'uri' => ClientBuilder::createFromEnv()->getUri(),
72
            'username' => 'foobar',
73
            'max_retries' => 4, // 4 linear retries with a difference of 100 ms give 1 sec in total (> 0.5)
74
        ]);
75
76
        $client->ping();
77
    }
78
79
    public function testAuthenticationRetryFails() : void
80
    {
81
        $client = Client::fromOptions([
82
            'uri' => ClientBuilder::createFromEnv()->getUri(),
83
            'username' => 'ghost',
84
            'max_retries' => 2,
85
        ]);
86
87
        $this->expectException(RequestFailed::class);
88
        $this->expectExceptionMessage("User 'ghost' is not found");
89
90
        $client->ping();
91
    }
92
93
    public function testRetry() : void
94
    {
95
        $clientBuilder = ClientBuilder::createFromEnv();
96
        $client = $clientBuilder->build();
97
        $retryableClient = $clientBuilder->setOptions(['max_retries' => 1])->build();
98
99
        $client->evaluate($luaInit = 'create_space("connection_retry")');
100
        // trigger an error only on the first call
101
        $retryableClient->evaluate($luaCall = '
102
            if box.space.connection_retry then
103
                box.space.connection_retry:drop() 
104
                box.error{code = 42, reason = "Foobar"}
105
            end
106
        ');
107
108
        $client->evaluate($luaInit);
109
        $this->expectException(RequestFailed::class);
110
        $client->evaluate($luaCall);
111
    }
112
113
    public function testNoRetriesOnUnexpectedResponse() : void
114
    {
115
        $clientBuilder = ClientBuilder::createFromEnv();
116
        $client = $clientBuilder->setOptions(['max_retries' => 5])->build();
117
118
        self::triggerUnexpectedResponse($client->getHandler(), new PingRequest());
119
120
        $this->expectException(UnexpectedResponse::class);
121
        $client->ping();
122
    }
123
124
    /**
125
     * @doesNotPerformAssertions
126
     */
127
    public function testReconnectOnBrokenConnection() : void
128
    {
129
        $clientBuilder = ClientBuilder::createFromEnv();
130
        $client = $clientBuilder->setConnectionOptions(['socket_timeout' => 1])->build();
131
132
        $client = $client->withMiddleware(RetryMiddleware::constant(1));
133
        $client = $client->withMiddleware(self::createBrokenConnectionMiddleware());
134
135
        $client->ping();
136
        $client->ping();
137
    }
138
139
    public function testThrowOnBrokenConnection() : void
140
    {
141
        $clientBuilder = ClientBuilder::createFromEnv();
142
        $client = $clientBuilder->setConnectionOptions(['socket_timeout' => 1])->build();
143
144
        $client = $client->withMiddleware(self::createBrokenConnectionMiddleware());
145
146
        $client->ping();
147
148
        $this->expectException(CommunicationFailed::class);
149
        $this->expectExceptionMessage('Unable to write request');
150
        $client->ping();
151
    }
152
153
    private static function createBrokenConnectionMiddleware() : Middleware
154
    {
155
        return new class() implements Middleware {
156
            private $count = 0;
157
158
            public function process(Request $request, Handler $handler) : Response
159
            {
160
                if (1 === $this->count) {
161
                    $connection = $handler->getConnection();
162
                    stream_socket_shutdown(TestCase::getRawStream($connection), STREAM_SHUT_WR);
0 ignored issues
show
Compatibility introduced by
$connection of type object<Tarantool\Client\Connection\Connection> is not a sub-type of object<Tarantool\Client\...ction\StreamConnection>. It seems like you assume a concrete implementation of the interface Tarantool\Client\Connection\Connection to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
163
                }
164
165
                ++$this->count;
166
167
                // Suppress PHPUnit\Framework\Error\Notice:
168
                // fwrite(): send of 15 bytes failed with errno=32 Broken pipe
169
                return @$handler->handle($request);
170
            }
171
        };
172
    }
173
}
174