Completed
Push — master ( ee6bc9...3f15df )
by Eugene
01:30
created

ClientMiddlewareTest.php$1 ➔ process()   A

Complexity

Conditions 2

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
dl 0
loc 13
rs 9.8333
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;
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
use Tarantool\Client\Tests\SpyMiddleware;
29
30
final class ClientMiddlewareTest extends TestCase
31
{
32
    public function testSpaceInheritsMiddleware() : void
33
    {
34
        $middleware = SpyMiddleware::fromTraceId(1);
35
36
        // Cache the space
37
        $this->client->getSpaceById(Space::VSPACE_ID)->select(Criteria::key([]));
38
39
        $clientWithMiddleware = $this->client->withMiddleware($middleware);
40
41
        $clientWithMiddleware->ping();
42
        self::assertSame([1], $middleware->getTraceLogArray());
43
44
        $spaceWithMiddleware = $clientWithMiddleware->getSpaceById(Space::VSPACE_ID);
45
46
        $spaceWithMiddleware->select(Criteria::key([]));
47
        self::assertSame([1, 1], $middleware->getTraceLogArray());
48
49
        $spaceWithMiddleware->select(Criteria::key([]));
50
        self::assertSame([1, 1, 1], $middleware->getTraceLogArray());
51
    }
52
53
    /**
54
     * @doesNotPerformAssertions
55
     *
56
     * @lua fiber = require('fiber')
57
     * @lua function test() try_drop_user('foobar') fiber.sleep(.5) create_user('foobar', '') end
58
     * @lua fiber.create(test)
59
     */
60
    public function testAuthenticationRetrySucceeds() : void
61
    {
62
        $client = Client::fromOptions([
63
            'uri' => ClientBuilder::createFromEnv()->getUri(),
64
            'username' => 'foobar',
65
            'max_retries' => 4, // 4 linear retries with a difference of 100 ms give 1 sec in total (> 0.5)
66
        ]);
67
68
        $client->ping();
69
    }
70
71
    public function testAuthenticationRetryFails() : void
72
    {
73
        $client = Client::fromOptions([
74
            'uri' => ClientBuilder::createFromEnv()->getUri(),
75
            'username' => 'ghost',
76
            'max_retries' => 2,
77
        ]);
78
79
        $this->expectException(RequestFailed::class);
80
        $this->expectExceptionMessage("User 'ghost' is not found");
81
82
        $client->ping();
83
    }
84
85
    public function testRetry() : void
86
    {
87
        $clientBuilder = ClientBuilder::createFromEnv();
88
        $client = $clientBuilder->build();
89
        $retryableClient = $clientBuilder->setOptions(['max_retries' => 1])->build();
90
91
        $client->evaluate($luaInit = 'create_space("connection_retry")');
92
        // Trigger an error only on the first call
93
        $retryableClient->evaluate($luaCall = '
94
            if box.space.connection_retry then
95
                box.space.connection_retry:drop() 
96
                box.error{code = 42, reason = "Foobar"}
97
            end
98
        ');
99
100
        $client->evaluate($luaInit);
101
        $this->expectException(RequestFailed::class);
102
        $client->evaluate($luaCall);
103
    }
104
105
    public function testNoRetriesOnUnexpectedResponse() : void
106
    {
107
        $clientBuilder = ClientBuilder::createFromEnv();
108
        $client = $clientBuilder->setOptions(['max_retries' => 5])->build();
109
110
        self::triggerUnexpectedResponse($client->getHandler(), new PingRequest());
111
112
        $this->expectException(UnexpectedResponse::class);
113
        $client->ping();
114
    }
115
116
    /**
117
     * @doesNotPerformAssertions
118
     */
119
    public function testReconnectOnBrokenConnection() : void
120
    {
121
        $clientBuilder = ClientBuilder::createFromEnv();
122
        $client = $clientBuilder->setConnectionOptions(['socket_timeout' => 1])->build();
123
124
        $client = $client->withMiddleware(RetryMiddleware::constant(1));
125
        $client = $client->withMiddleware(self::createBrokenConnectionMiddleware());
126
127
        $client->ping();
128
        $client->ping();
129
    }
130
131
    public function testThrowOnBrokenConnection() : void
132
    {
133
        $clientBuilder = ClientBuilder::createFromEnv();
134
        $client = $clientBuilder->setConnectionOptions(['socket_timeout' => 1])->build();
135
136
        $client = $client->withMiddleware(self::createBrokenConnectionMiddleware());
137
138
        $client->ping();
139
140
        $this->expectException(CommunicationFailed::class);
141
        $this->expectExceptionMessage('Unable to write request');
142
        $client->ping();
143
    }
144
145
    private static function createBrokenConnectionMiddleware() : Middleware
146
    {
147
        return new class() implements Middleware {
148
            private $count = 0;
149
150
            public function process(Request $request, Handler $handler) : Response
151
            {
152
                if (1 === $this->count) {
153
                    $connection = $handler->getConnection();
154
                    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...
155
                }
156
157
                ++$this->count;
158
159
                // Suppress PHPUnit\Framework\Error\Notice:
160
                // fwrite(): send of 15 bytes failed with errno=32 Broken pipe
161
                return @$handler->handle($request);
162
            }
163
        };
164
    }
165
}
166