Passed
Pull Request — master (#63)
by Eugene
03:00
created

php$0 ➔ testNoRetriesOnUnexpectedResponse()   A

Complexity

Conditions 1

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
dl 0
loc 9
rs 9.9666
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
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);
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