Passed
Pull Request — master (#12)
by romain
09:45
created

ClientSpec   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 286
Duplicated Lines 15.38 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 0
Metric Value
wmc 15
lcom 1
cbo 8
dl 44
loc 286
rs 10
c 0
b 0
f 0

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
namespace spec\Yproximite\Api\Client;
4
5
use Http\Client\HttpClient;
6
use PhpSpec\ObjectBehavior;
7
use Http\Message\MessageFactory;
8
use Psr\Http\Message\StreamInterface;
9
use Psr\Http\Message\RequestInterface;
10
use Psr\Http\Message\ResponseInterface;
11
use Http\Client\Exception\HttpException;
12
use Http\Discovery\MessageFactoryDiscovery;
13
14
use Yproximite\Api\Client\Client;
15
use Yproximite\Api\Exception\TransferException;
16
use Yproximite\Api\Exception\AuthenficationException;
17
use Yproximite\Api\Exception\InvalidResponseException;
18
19
class ClientSpec extends ObjectBehavior
20
{
21
    const BASE_URL = 'http://api.host';
22
23
    function it_is_initializable()
24
    {
25
        $this->shouldHaveType(Client::class);
26
    }
27
28
    function let(
29
        HttpClient $httpClient,
30
        MessageFactory $messageFactory,
31
        RequestInterface $tokenRequest,
32
        ResponseInterface $tokenResponse,
33
        StreamInterface $tokenStream
34
    ) {
35
        $tokenHeaders     = ['Content-Type' => 'application/x-www-form-urlencoded'];
36
        $tokenRequestUri  = sprintf('%s/login_check', self::BASE_URL);
37
        $tokenRawRequest  = http_build_query(['api_key' => 'abcd']);
38
        $tokenRawResponse = json_encode(['token' => 'efgh']);
39
40
        $messageFactory->createRequest('POST', $tokenRequestUri, $tokenHeaders, $tokenRawRequest)->willReturn($tokenRequest);
41
        $httpClient->sendRequest($tokenRequest)->willReturn($tokenResponse);
42
        $tokenResponse->getStatusCode()->willReturn(200);
43
        $tokenResponse->getBody()->willReturn($tokenStream);
44
        $tokenStream->__toString()->willReturn($tokenRawResponse);
45
46
        $this->beConstructedWith($httpClient, 'abcd', self::BASE_URL, $messageFactory);
47
    }
48
49
    function it_should_send_get_request(
50
        HttpClient $httpClient,
51
        MessageFactory $messageFactory,
52
        RequestInterface $request,
53
        ResponseInterface $response,
54
        StreamInterface $stream
55
    ) {
56
        $headers     = ['Content-Type' => 'application/x-www-form-urlencoded', 'Authorization' => 'Bearer efgh'];
57
        $rawQuery    = http_build_query(['query' => 'test']);
58
        $requestUri  = sprintf('%s/example?%s', self::BASE_URL, $rawQuery);
59
        $rawResponse = json_encode(['foo' => 'bar']);
60
61
        $messageFactory->createRequest('GET', $requestUri, $headers, null)->willReturn($request);
62
        $httpClient->sendRequest($request)->willReturn($response);
63
        $response->getStatusCode()->willReturn(200);
64
        $response->getBody()->willReturn($stream);
65
        $stream->__toString()->willReturn($rawResponse);
66
67
        $httpClient->sendRequest($request)->shouldBeCalled();
68
69
        $this->sendRequest('GET', 'example', ['query' => 'test']);
70
    }
71
72
    function it_should_send_post_request(
73
        HttpClient $httpClient,
74
        MessageFactory $messageFactory,
75
        RequestInterface $request,
76
        ResponseInterface $response,
77
        StreamInterface $stream
78
    ) {
79
        $headers     = ['Content-Type' => 'application/x-www-form-urlencoded', 'Authorization' => 'Bearer efgh'];
80
        $requestUri  = sprintf('%s/example', self::BASE_URL);
81
        $rawRequest  = http_build_query(['query' => 'test']);
82
        $rawResponse = json_encode(['foo' => 'bar']);
83
84
        $messageFactory->createRequest('POST', $requestUri, $headers, $rawRequest)->willReturn($request);
85
        $httpClient->sendRequest($request)->willReturn($response);
86
        $response->getStatusCode()->willReturn(200);
87
        $response->getBody()->willReturn($stream);
88
        $stream->__toString()->willReturn($rawResponse);
89
90
        $httpClient->sendRequest($request)->shouldBeCalled();
91
92
        $this->sendRequest('POST', 'example', ['query' => 'test']);
93
    }
94
95
    function it_should_ask_for_api_token_once(
96
        HttpClient $httpClient,
97
        MessageFactory $messageFactory,
98
        RequestInterface $firstRequest,
99
        ResponseInterface $firstResponse,
100
        StreamInterface $firstStream,
101
        RequestInterface $secondRequest,
102
        ResponseInterface $secondResponse,
103
        StreamInterface $secondStream,
104
        RequestInterface $tokenRequest
105
    ) {
106
        // first request
107
        $firstHeaders     = ['Content-Type' => 'application/x-www-form-urlencoded', 'Authorization' => 'Bearer efgh'];
108
        $firstRequestUri  = sprintf('%s/first', self::BASE_URL);
109
        $firstRawResponse = json_encode(['foo' => 'bar']);
110
111
        $messageFactory->createRequest('GET', $firstRequestUri, $firstHeaders, null)->willReturn($firstRequest);
112
        $httpClient->sendRequest($firstRequest)->willReturn($firstResponse);
113
        $firstResponse->getStatusCode()->willReturn(200);
114
        $firstResponse->getBody()->willReturn($firstStream);
115
        $firstStream->__toString()->willReturn($firstRawResponse);
116
117
        $httpClient->sendRequest($firstRequest)->shouldBeCalled();
118
119
        $this->sendRequest('GET', 'first');
120
121
        // second request
122
        $secondHeaders     = ['Content-Type' => 'application/x-www-form-urlencoded', 'Authorization' => 'Bearer efgh'];
123
        $secondRequestUri  = sprintf('%s/second', self::BASE_URL);
124
        $secondRawResponse = json_encode(['foo' => 'bar']);
125
126
        $messageFactory->createRequest('GET', $secondRequestUri, $secondHeaders, null)->willReturn($secondRequest);
127
        $httpClient->sendRequest($secondRequest)->willReturn($secondResponse);
128
        $secondResponse->getStatusCode()->willReturn(200);
129
        $secondResponse->getBody()->willReturn($secondStream);
130
        $secondStream->__toString()->willReturn($secondRawResponse);
131
132
        $httpClient->sendRequest($secondRequest)->shouldBeCalled();
133
134
        $this->sendRequest('GET', 'second');
135
136
        $httpClient->sendRequest($tokenRequest)->shouldHaveBeenCalledTimes(1);
137
    }
138
139
    function it_should_renew_the_token_and_resend_the_request(
140
        HttpClient $httpClient,
141
        MessageFactory $messageFactory,
142
        RequestInterface $firstRequest,
143
        ResponseInterface $firstResponse,
144
        StreamInterface $firstStream,
145
        RequestInterface $secondRequest,
146
        ResponseInterface $secondResponse,
147
        StreamInterface $secondStream,
148
        RequestInterface $tokenRequest
149
    ) {
150
        // first request
151
        $firstHeaders     = ['Content-Type' => 'application/x-www-form-urlencoded', 'Authorization' => 'Bearer efgh'];
152
        $firstRequestUri  = sprintf('%s/first', self::BASE_URL);
153
        $firstRawResponse = json_encode(['foo' => 'bar']);
154
155
        $messageFactory->createRequest('GET', $firstRequestUri, $firstHeaders, null)->willReturn($firstRequest);
156
        $httpClient->sendRequest($firstRequest)->willReturn($firstResponse);
157
        $firstResponse->getStatusCode()->willReturn(200);
158
        $firstResponse->getBody()->willReturn($firstStream);
159
        $firstStream->__toString()->willReturn($firstRawResponse);
160
161
        $httpClient->sendRequest($firstRequest)->shouldBeCalled();
162
163
        $this->sendRequest('GET', 'first');
164
165
        // second request
166
        $secondHeaders     = ['Content-Type' => 'application/x-www-form-urlencoded', 'Authorization' => 'Bearer efgh'];
167
        $secondRequestUri  = sprintf('%s/second', self::BASE_URL);
168
        $secondRawResponse = json_encode(['foo' => 'bar']);
169
170
        $messageFactory->createRequest('GET', $secondRequestUri, $secondHeaders, null)->willReturn($secondRequest);
171
        $secondResponse->getStatusCode()->willReturn(200);
172
        $secondResponse->getBody()->willReturn($secondStream);
173
        $secondStream->__toString()->willReturn($secondRawResponse);
174
175
        $secondRequestCounter = 0;
176
177
        $httpClient->sendRequest($secondRequest)->will(function () use ($secondRequestCounter, $secondResponse) {
178
            $secondRequestCounter ++;
179
180
            if ($secondRequestCounter === 1) {
181
                return MessageFactoryDiscovery::find()->createResponse(401);
182
            }
183
184
            return $secondResponse;
185
        });
186
187
        $httpClient->sendRequest($secondRequest)->shouldBeCalledTimes(2);
188
189
        $this->shouldThrow(InvalidResponseException::class)->during('sendRequest', ['GET', 'second']);
190
191
        $httpClient->sendRequest($tokenRequest)->shouldHaveBeenCalledTimes(2);
192
    }
193
194
    function it_should_not_renew_the_token(
195
        HttpClient $httpClient,
196
        MessageFactory $messageFactory,
197
        RequestInterface $request,
198
        ResponseInterface $response,
199
        StreamInterface $stream,
200
        RequestInterface $tokenRequest
201
    ) {
202
        $headers     = ['Content-Type' => 'application/x-www-form-urlencoded', 'Authorization' => 'Bearer efgh'];
203
        $requestUri  = sprintf('%s/example', self::BASE_URL);
204
        $rawResponse = json_encode(['foo' => 'bar']);
205
206
        $messageFactory->createRequest('GET', $requestUri, $headers, null)->willReturn($request);
207
        $httpClient->sendRequest($request)->willReturn($response);
208
        $response->getStatusCode()->willReturn(401);
209
        $stream->__toString()->willReturn($rawResponse);
210
211
        $httpClient->sendRequest($request)->shouldBeCalled();
212
213
        $this->shouldThrow(InvalidResponseException::class)->during('sendRequest', ['GET', 'example']);
214
215
        $httpClient->sendRequest($tokenRequest)->shouldHaveBeenCalledTimes(1);
216
    }
217
218
    function it_should_throw_transfer_exception_on_http_exception(
219
        HttpClient $httpClient,
220
        MessageFactory $messageFactory,
221
        RequestInterface $request
222
    ) {
223
        $headers     = ['Content-Type' => 'application/x-www-form-urlencoded', 'Authorization' => 'Bearer efgh'];
224
        $requestUri  = sprintf('%s/example', self::BASE_URL);
225
226
        $errorResponse = MessageFactoryDiscovery::find()->createResponse(500);
227
        $httpException = HttpException::create($request->getWrappedObject(), $errorResponse);
228
229
        $messageFactory->createRequest('GET', $requestUri, $headers, null)->willReturn($request);
230
        $httpClient->sendRequest($request)->willThrow($httpException);
231
232
        $this->shouldThrow(TransferException::class)->during('sendRequest', ['GET', 'example']);
233
    }
234
235
    function it_should_throw_invalid_response_exception_on_bad_status_code(
236
        HttpClient $httpClient,
237
        MessageFactory $messageFactory,
238
        RequestInterface $request,
239
        ResponseInterface $response
240
    ) {
241
        $headers     = ['Content-Type' => 'application/x-www-form-urlencoded', 'Authorization' => 'Bearer efgh'];
242
        $requestUri  = sprintf('%s/example', self::BASE_URL);
243
244
        $messageFactory->createRequest('GET', $requestUri, $headers, null)->willReturn($request);
245
        $httpClient->sendRequest($request)->willReturn($response);
246
        $response->getStatusCode()->willReturn(400);
247
248
        $this->shouldThrow(InvalidResponseException::class)->during('sendRequest', ['GET', 'example']);
249
    }
250
251
    function it_should_throw_invalid_response_exception_on_broken_response_body(
252
        HttpClient $httpClient,
253
        MessageFactory $messageFactory,
254
        RequestInterface $request,
255
        ResponseInterface $response,
256
        StreamInterface $stream
257
    ) {
258
        $headers     = ['Content-Type' => 'application/x-www-form-urlencoded', 'Authorization' => 'Bearer efgh'];
259
        $requestUri  = sprintf('%s/example', self::BASE_URL);
260
261
        $messageFactory->createRequest('GET', $requestUri, $headers, null)->willReturn($request);
262
        $httpClient->sendRequest($request)->willReturn($response);
263
        $response->getStatusCode()->willReturn(200);
264
        $response->getBody()->willReturn($stream);
265
        $stream->__toString()->willReturn('it is not a json');
266
267
        $this->shouldThrow(InvalidResponseException::class)->during('sendRequest', ['GET', 'example']);
268
    }
269
270
    function it_should_throw_authenfication_exception_on_http_exception(
271
        HttpClient $httpClient,
272
        RequestInterface $tokenRequest
273
    ) {
274
        $errorResponse = MessageFactoryDiscovery::find()->createResponse(500);
275
        $httpException = HttpException::create($tokenRequest->getWrappedObject(), $errorResponse);
276
277
        $httpClient->sendRequest($tokenRequest)->willThrow($httpException);
278
279
        $this->shouldThrow(AuthenficationException::class)->during('sendRequest', ['GET', 'example']);
280
    }
281
282
    function it_should_throw_authenfication_exception_on_bad_status_code(ResponseInterface $tokenResponse)
283
    {
284
        $tokenResponse->getStatusCode()->willReturn(400);
285
286
        $this->shouldThrow(AuthenficationException::class)->during('sendRequest', ['GET', 'example']);
287
    }
288
289
    function it_should_throw_authenfication_exception_on_broken_response_body(StreamInterface $tokenStream)
290
    {
291
        $tokenStream->__toString()->willReturn('it is not a json');
292
293
        $this->shouldThrow(AuthenficationException::class)->during('sendRequest', ['GET', 'example']);
294
    }
295
296
    function it_should_throw_authenfication_exception_on_bad_response_body(StreamInterface $tokenStream)
297
    {
298
        $rawResponse = json_encode(['it' => 'is not a token']);
299
300
        $tokenStream->__toString()->willReturn($rawResponse);
301
302
        $this->shouldThrow(AuthenficationException::class)->during('sendRequest', ['GET', 'example']);
303
    }
304
}
305