Completed
Pull Request — master (#13)
by
unknown
04:14
created

itShouldCreateRequestFromDefinition()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 67
Code Lines 47

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 47
nc 1
nop 3
dl 0
loc 67
rs 9.1563
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace ElevenLabs\Api\Service;
4
5
use ElevenLabs\Api\Definition\Parameter;
6
use ElevenLabs\Api\Definition\Parameters;
7
use ElevenLabs\Api\Definition\RequestDefinition;
8
use ElevenLabs\Api\Definition\ResponseDefinition;
9
use ElevenLabs\Api\Schema;
10
use ElevenLabs\Api\Service\Exception\RequestViolations;
11
use ElevenLabs\Api\Service\Exception\ResponseViolations;
12
use ElevenLabs\Api\Service\Resource\Item;
13
use ElevenLabs\Api\Validator\ConstraintViolation;
14
use ElevenLabs\Api\Validator\MessageValidator;
15
use GuzzleHttp\Psr7\Request;
16
use Http\Client\HttpClient;
17
use Http\Message\MessageFactory;
18
use Http\Message\UriFactory;
19
use PHPUnit\Framework\MockObject\MockObject;
20
use PHPUnit\Framework\TestCase;
21
use Psr\Http\Message\ResponseInterface;
22
use Psr\Http\Message\StreamInterface;
23
use Psr\Http\Message\UriInterface;
24
use Rize\UriTemplate;
25
use Symfony\Component\Serializer\SerializerInterface;
26
27
/**
28
 * Class ApiServiceTest.
29
 */
30
class ApiServiceTest extends TestCase
31
{
32
    /** @var Schema|MockObject */
33
    private $schema;
34
35
    /** @var UriFactory|MockObject */
36
    private $uriFactory;
37
38
    /** @var UriTemplate|MockObject */
39
    private $uriTemplate;
40
41
    /** @var HttpClient|MockObject */
42
    private $httpClient;
43
44
    /** @var MessageFactory|MockObject */
45
    private $messageFactory;
46
47
    /** @var MessageValidator|MockObject */
48
    private $messageValidator;
49
50
    /** @var SerializerInterface|MockObject */
51
    private $serializer;
52
53
    /** @var array */
54
    private $config;
55
56
    public function setUp()
57
    {
58
        $this->schema = $this->createMock(Schema::class);
59
        $this->uriFactory = $this->createMock(UriFactory::class);
60
        $this->uriTemplate = $this->createMock(UriTemplate::class);
61
        $this->httpClient = $this->createMock(HttpClient::class);
62
        $this->messageFactory = $this->createMock(MessageFactory::class);
63
        $this->messageValidator = $this->createMock(MessageValidator::class);
64
        $this->serializer = $this->createMock(SerializerInterface::class);
65
        $this->config = [];
66
    }
67
68
    /** @test */
69
    public function itShouldCheckApiSchemaSchemes()
70
    {
71
        $this->expectException(\LogicException::class);
72
        $this->expectExceptionMessage('You need to provide at least on scheme in your API Schema');
73
74
        $this->getApiService();
75
    }
76
77
    /** @test */
78
    public function itCheckTheHostInApiSchema()
79
    {
80
        $this->expectException(\LogicException::class);
81
        $this->expectExceptionMessage('The host in the API Schema should not be null');
82
83
        $this->schema->expects($this->exactly(2))->method('getSchemes')->willReturn(['https']);
0 ignored issues
show
Bug introduced by
The method expects() does not exist on ElevenLabs\Api\Schema. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

83
        $this->schema->/** @scrutinizer ignore-call */ 
84
                       expects($this->exactly(2))->method('getSchemes')->willReturn(['https']);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
84
        $this->schema->expects($this->once())->method('getHost')->willReturn(null);
85
86
        $this->getApiService();
87
    }
88
89
    /** @test */
90
    public function itOnlySupportHttpAndHttps()
91
    {
92
        $this->expectException(\RuntimeException::class);
93
        $this->expectExceptionMessage('Cannot choose a proper scheme from the API Schema. Supported: https, http');
94
95
        $this->schema->expects($this->any())->method('getSchemes')->willReturn(['ftp']);
96
        $this->schema->expects($this->any())->method('getHost')->willReturn('domain.tld');
97
98
        $this->getApiService();
99
    }
100
101
    /** @test */
102
    public function itShouldPreferHttps()
103
    {
104
        $this->schema->expects($this->exactly(2))->method('getSchemes')->willReturn(['http', 'https']);
105
        $this->schema->expects($this->exactly(1))->method('getHost')->willReturn('domain.tld');
106
107
        $this->uriFactory->expects($this->exactly(1))->method('createUri')->with('https://domain.tld');
0 ignored issues
show
Bug introduced by
The method expects() does not exist on Http\Message\UriFactory. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

107
        $this->uriFactory->/** @scrutinizer ignore-call */ 
108
                           expects($this->exactly(1))->method('createUri')->with('https://domain.tld');

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
108
109
        $this->getApiService();
110
    }
111
112
    /** @test */
113
    public function itShouldPreferHttpsByDefault()
114
    {
115
        $this->schema->expects($this->exactly(2))->method('getSchemes')->willReturn(['http']);
116
        $this->schema->expects($this->exactly(1))->method('getHost')->willReturn('domain.tld');
117
118
        $this->uriFactory->expects($this->exactly(1))->method('createUri')->with('http://domain.tld');
119
120
        $this->getApiService();
121
    }
122
123
    /** @test */
124
    public function itCreateABaseUriUsingTheOneProvidedInTheConfigArray()
125
    {
126
        $this->config['baseUri'] = 'https://somewhere.tld';
127
128
        $this->uriFactory->expects($this->exactly(1))->method('createUri')->with('https://somewhere.tld');
129
130
        $this->getApiService();
131
    }
132
133
    /**
134
     * @test
135
     *
136
     * @dataProvider dataProviderItShouldUseDefaultValues
137
     *
138
     * @param array $requestParams
139
     * @param array $expected
140
     *
141
     * @throws Exception\ConstraintViolations
142
     * @throws \Assert\AssertionFailedException
143
     * @throws \Http\Client\Exception
144
     */
145
    public function itShouldUseDefaultValues(array $requestParams, array $expected)
146
    {
147
        $requestParameters = [];
148
        foreach ($requestParams as $p) {
149
            $requestParameter = $this->getMockBuilder(Parameter::class)->disableOriginalConstructor()->getMock();
150
            $requestParameter->expects($this->once())->method('getLocation')->willReturn($p['location']);
151
            $requestParameter->expects($this->exactly(2))->method('getSchema')->willReturnCallback(function () use ($p) {
152
                $value = new \stdClass();
153
                $value->default = $p['default'];
154
155
                return $value;
156
            });
157
            if ("body" === $p['location']) {
158
                $this->serializer->expects($this->once())->method('serialize')->willReturn(json_encode($p['default']));
0 ignored issues
show
Bug introduced by
The method expects() does not exist on Symfony\Component\Serializer\SerializerInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

158
                $this->serializer->/** @scrutinizer ignore-call */ 
159
                                   expects($this->once())->method('serialize')->willReturn(json_encode($p['default']));

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
159
            } else {
160
                $this->serializer->expects($this->never())->method('serialize');
161
            }
162
            $requestParameters[$p['name']] = $requestParameter;
163
        }
164
        $this->uriTemplate->expects($this->any())->method('expand')->willReturnCallback(function ($pathTemplate, array $pathParameters) use ($expected) {
0 ignored issues
show
Bug introduced by
The method expects() does not exist on Rize\UriTemplate. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

164
        $this->uriTemplate->/** @scrutinizer ignore-call */ 
165
                            expects($this->any())->method('expand')->willReturnCallback(function ($pathTemplate, array $pathParameters) use ($expected) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
165
            $this->assertEquals('/foo/bar', $pathTemplate);
166
            $this->assertEquals($expected['path'], $pathParameters);
167
        });
168
169
        $params = $this->getMockBuilder(Parameters::class)->disableOriginalConstructor()->getMock();
170
        $params->expects($this->once())->method('getIterator')->willReturn($requestParameters);
171
        $params->expects($this->never())->method('getByName');
172
173
        $baseUri = $this->createMock(UriInterface::class);
174
        $baseUri->expects($this->once())->method('withPath')->willReturn($baseUri);
175
        $baseUri->expects($this->once())->method('withQuery')->willReturnCallback(function ($query) use ($baseUri, $expected) {
176
            $this->assertEquals($expected['query'], $query);
177
178
            return $baseUri;
179
        });
180
181
        $responseDefinition = $this->getMockBuilder(ResponseDefinition::class)->disableOriginalConstructor()->getMock();
182
        $responseDefinition->expects($this->once())->method('hasBodySchema')->willReturn(false);
183
184
        $requestDefinition = $this->getMockBuilder(RequestDefinition::class)->disableOriginalConstructor()->getMock();
185
        $requestDefinition->expects($this->once())->method('getContentTypes')->willReturn(['application/json']);
186
        $requestDefinition->expects($this->once())->method('getRequestParameters')->willReturn($params);
187
        $requestDefinition->expects($this->once())->method('getMethod')->willReturn('GET');
188
        $requestDefinition->expects($this->once())->method('getPathTemplate')->willReturn('/foo/bar');
189
        $requestDefinition->expects($this->once())->method('getResponseDefinition')->with(200)->willReturn($responseDefinition);
190
191
        $this->config['baseUri'] = 'https://somewhere.tld';
192
        $this->uriFactory->expects($this->exactly(1))->method('createUri')->with('https://somewhere.tld')->willReturn($baseUri);
193
        $this->schema->expects($this->exactly(1))->method('getRequestDefinition')->with('operationId')->willReturn($requestDefinition);
194
        $this->messageFactory->expects($this->once())->method('createRequest')->willReturnCallback(function ($method, $uri, $headers, $body) use ($expected) {
0 ignored issues
show
Bug introduced by
The method expects() does not exist on Http\Message\MessageFactory. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

194
        $this->messageFactory->/** @scrutinizer ignore-call */ 
195
                               expects($this->once())->method('createRequest')->willReturnCallback(function ($method, $uri, $headers, $body) use ($expected) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
195
            $this->assertEquals('GET', $method);
196
            $this->assertInstanceOf(UriInterface::class, $uri);
197
            $this->assertEquals($expected['headers'], $headers);
198
            $this->assertEquals($expected['body'], $body);
199
200
            $request = $this->getMockBuilder(Request::class)->disableOriginalConstructor()->getMock();
201
            $request->expects($this->once())->method('getHeaders')->willReturn([]);
202
203
            return $request;
204
        });
205
        $this->httpClient->expects($this->once())->method('sendRequest')->willReturnCallback(function ($request) {
0 ignored issues
show
Bug introduced by
The method expects() does not exist on Http\Client\HttpClient. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

205
        $this->httpClient->/** @scrutinizer ignore-call */ 
206
                           expects($this->once())->method('sendRequest')->willReturnCallback(function ($request) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
206
            $this->assertInstanceOf(Request::class, $request);
207
208
            $response = $this->createMock(ResponseInterface::class);
209
            $response->expects($this->once())->method('getStatusCode')->willReturn(200);
210
211
            return $response;
212
        });
213
214
        $service = $this->getApiService();
215
        $service->call('operationId');
216
    }
217
218
    /**
219
     * @return array
220
     */
221
    public function dataProviderItShouldUseDefaultValues()
222
    {
223
        return [
224
            "path" => [
225
                [
226
                    [
227
                        'name' => 'foo',
228
                        'location' => 'path',
229
                        'value' => 'bar',
230
                        "default" => 'bar_default',
231
                    ],
232
                ],
233
                [
234
                    "path" => ['foo' => 'bar_default'],
235
                    "query" => null,
236
                    "headers" => ["Content-Type" => "application/json"],
237
                    'body' => null,
238
                ],
239
            ],
240
            "query" => [
241
                [
242
                    [
243
                        'name' => 'foo',
244
                        'location' => 'query',
245
                        'value' => 'bar',
246
                        "default" => 'bar_default',
247
                    ],
248
                ],
249
                [
250
                    "path" => [],
251
                    "query" => "foo=bar_default",
252
                    "headers" => ["Content-Type" => "application/json"],
253
                    'body' => null,
254
                ],
255
            ],
256
            "header" => [
257
                [
258
                    [
259
                        'name' => 'foo',
260
                        'location' => 'header',
261
                        'value' => 'bar',
262
                        "default" => 'bar_default',
263
                    ],
264
                ],
265
                [
266
                    "path" => [],
267
                    "query" => null,
268
                    "headers" => ["Content-Type" => "application/json", "foo" => "bar_default"],
269
                    'body' => null,
270
                ],
271
            ],
272
            "formData" => [
273
                [
274
                    [
275
                        'name' => 'foo',
276
                        'location' => 'formData',
277
                        'value' => 'bar',
278
                        "default" => 'bar_default',
279
                    ],
280
                    [
281
                        'name' => 'foo2',
282
                        'location' => 'formData',
283
                        'value' => 'bar2',
284
                        "default" => 'bar2_default',
285
                    ],
286
                ],
287
                [
288
                    "path" => [],
289
                    "query" => null,
290
                    "headers" => ["Content-Type" => "application/json"],
291
                    'body' => "foo=bar_default&foo2=bar2_default",
292
                ],
293
            ],
294
            "body" => [
295
                [
296
                    [
297
                        'name' => 'body',
298
                        'location' => 'body',
299
                        'value' => ['foo' => "bar", 'bar' => 'foo'],
300
                        "default" => ['foo' => "bar__default", 'bar' => 'foo__default'],
301
                    ],
302
                ],
303
                [
304
                    "path" => [],
305
                    "query" => null,
306
                    "headers" => ["Content-Type" => "application/json"],
307
                    'body' => json_encode(['foo' => "bar__default", 'bar' => 'foo__default']),
308
                ],
309
            ],
310
        ];
311
    }
312
313
    /**
314
     * @test
315
     *
316
     * @expectedException \InvalidArgumentException
317
     *
318
     * @expectedExceptionMessage foo is not a defined request parameter
319
     */
320
    public function itShouldNotCreateRequestBecauseSomeParameterWasNotDefined()
321
    {
322
        $this->uriTemplate->expects($this->never())->method('expand');
323
324
        $params = $this->getMockBuilder(Parameters::class)->disableOriginalConstructor()->getMock();
325
        $params->expects($this->once())->method('getIterator')->willReturn([]);
326
        $params->expects($this->once())->method('getByName')->willReturn(null);
327
328
        $baseUri = $this->createMock(UriInterface::class);
329
        $baseUri->expects($this->never())->method('withPath');
330
        $baseUri->expects($this->never())->method('withQuery');
331
332
        $requestDefinition = $this->getMockBuilder(RequestDefinition::class)->disableOriginalConstructor()->getMock();
333
        $requestDefinition->expects($this->once())->method('getContentTypes')->willReturn(['application/json']);
334
        $requestDefinition->expects($this->once())->method('getRequestParameters')->willReturn($params);
335
        $requestDefinition->expects($this->never())->method('getMethod');
336
        $requestDefinition->expects($this->never())->method('getPathTemplate');
337
        $requestDefinition->expects($this->never())->method('getResponseDefinition');
338
339
        $this->config['baseUri'] = 'https://somewhere.tld';
340
        $this->uriFactory->expects($this->exactly(1))->method('createUri')->with('https://somewhere.tld')->willReturn($baseUri);
341
        $this->schema->expects($this->exactly(1))->method('getRequestDefinition')->with('operationId')->willReturn($requestDefinition);
342
        $this->messageFactory->expects($this->never())->method('createRequest');
343
        $this->httpClient->expects($this->never())->method('sendRequest');
344
        $this->messageValidator->expects($this->never())->method('validateResponse');
0 ignored issues
show
Bug introduced by
The method expects() does not exist on ElevenLabs\Api\Validator\MessageValidator. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

344
        $this->messageValidator->/** @scrutinizer ignore-call */ 
345
                                 expects($this->never())->method('validateResponse');

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
345
        $this->messageValidator->expects($this->never())->method('hasViolations');
346
347
        $service = $this->getApiService();
348
        $service->call('operationId', ['foo' => 'bar']);
349
    }
350
351
    /**
352
     * @test
353
     *
354
     * @dataProvider dataProviderItShouldCreateRequestFromDefinition
355
     *
356
     * @param array $localisations
357
     * @param array $requestParams
358
     * @param array $expected
359
     */
360
    public function itShouldCreateRequestFromDefinition(array $localisations, array $requestParams, array $expected)
361
    {
362
        $this->uriTemplate->expects($this->any())->method('expand')->willReturnCallback(function ($pathTemplate, array $pathParameters) use ($expected) {
363
            $this->assertEquals('/foo/bar', $pathTemplate);
364
            $this->assertEquals($expected['path'], $pathParameters);
365
        });
366
367
        $params = $this->getMockBuilder(Parameters::class)->disableOriginalConstructor()->getMock();
368
        $params->expects($this->once())->method('getIterator')->willReturn([]);
369
        $params->expects($this->any())->method('getByName')->willReturnCallback(function ($name) use ($localisations, $requestParams) {
370
            $param = $this->getMockBuilder(Parameter::class)->disableOriginalConstructor()->getMock();
371
            $param->expects($this->once())->method('getLocation')->willReturn($localisations[$name]);
372
            if ("body" === $localisations[$name]) {
373
                $this->serializer->expects($this->once())->method('serialize')->willReturn(json_encode($requestParams[$name]));
374
            } else {
375
                $this->serializer->expects($this->never())->method('serialize');
376
            }
377
378
            return $param;
379
        });
380
381
        $baseUri = $this->createMock(UriInterface::class);
382
        $baseUri->expects($this->once())->method('withPath')->willReturn($baseUri);
383
        $baseUri->expects($this->once())->method('withQuery')->willReturnCallback(function ($query) use ($baseUri, $expected) {
384
            $this->assertEquals($expected['query'], $query);
385
386
            return $baseUri;
387
        });
388
389
        $responseDefinition = $this->getMockBuilder(ResponseDefinition::class)->disableOriginalConstructor()->getMock();
390
        $responseDefinition->expects($this->once())->method('hasBodySchema')->willReturn(false);
391
392
        $requestDefinition = $this->getMockBuilder(RequestDefinition::class)->disableOriginalConstructor()->getMock();
393
        $requestDefinition->expects($this->once())->method('getContentTypes')->willReturn(['application/json']);
394
        $requestDefinition->expects($this->once())->method('getRequestParameters')->willReturn($params);
395
        $requestDefinition->expects($this->once())->method('getMethod')->willReturn('GET');
396
        $requestDefinition->expects($this->once())->method('getPathTemplate')->willReturn('/foo/bar');
397
        $requestDefinition->expects($this->once())->method('getResponseDefinition')->with(200)->willReturn($responseDefinition);
398
399
        $this->config['baseUri'] = 'https://somewhere.tld';
400
        $this->config['validateResponse'] = true;
401
        $this->uriFactory->expects($this->exactly(1))->method('createUri')->with('https://somewhere.tld')->willReturn($baseUri);
402
        $this->schema->expects($this->exactly(1))->method('getRequestDefinition')->with('operationId')->willReturn($requestDefinition);
403
        $this->messageFactory->expects($this->once())->method('createRequest')->willReturnCallback(function ($method, $uri, $headers, $body) use ($expected) {
404
            $this->assertEquals('GET', $method);
405
            $this->assertInstanceOf(UriInterface::class, $uri);
406
            $this->assertEquals($expected['headers'], $headers);
407
            $this->assertEquals($expected['body'], $body);
408
409
            $request = $this->getMockBuilder(Request::class)->disableOriginalConstructor()->getMock();
410
            $request->expects($this->once())->method('getHeaders')->willReturn([]);
411
412
            return $request;
413
        });
414
        $this->httpClient->expects($this->once())->method('sendRequest')->willReturnCallback(function ($request) {
415
            $this->assertInstanceOf(Request::class, $request);
416
417
            $response = $this->createMock(ResponseInterface::class);
418
            $response->expects($this->once())->method('getStatusCode')->willReturn(200);
419
420
            return $response;
421
        });
422
        $this->messageValidator->expects($this->once())->method('validateResponse');
423
        $this->messageValidator->expects($this->exactly(2))->method('hasViolations')->willReturn(false);
424
425
        $service = $this->getApiService();
426
        $service->call('operationId', $requestParams);
427
    }
428
429
    /**
430
     * @return array
431
     */
432
    public function dataProviderItShouldCreateRequestFromDefinition()
433
    {
434
        return [
435
            "path" => [
436
                [
437
                    'foo' => 'path',
438
                ],
439
                [
440
                    'foo' => 'bar',
441
                ],
442
                [
443
                    "path" => ['foo' => 'bar'],
444
                    "query" => null,
445
                    "headers" => ["Content-Type" => "application/json"],
446
                    'body' => null,
447
                ],
448
            ],
449
            "query" => [
450
                [
451
                    'foo' => 'query',
452
                ],
453
                [
454
                    'foo' => 'bar',
455
                ],
456
                [
457
                    "path" => [],
458
                    "query" => "foo=bar",
459
                    "headers" => ["Content-Type" => "application/json"],
460
                    'body' => null,
461
                ],
462
            ],
463
            "header" => [
464
                [
465
                    'foo' => 'header',
466
                ],
467
                [
468
                    'foo' => 'bar',
469
                ],
470
                [
471
                    "path" => [],
472
                    "query" => null,
473
                    "headers" => ["Content-Type" => "application/json", "foo" => "bar"],
474
                    'body' => null,
475
                ],
476
            ],
477
            "formData" => [
478
                [
479
                    'foo' => 'formData',
480
                    "foo2" => 'formData',
481
                ],
482
                [
483
                    'foo' => 'bar',
484
                    "foo2" => 'bar2',
485
                ],
486
                [
487
                    "path" => [],
488
                    "query" => null,
489
                    "headers" => ["Content-Type" => "application/json"],
490
                    'body' => "foo=bar&foo2=bar2",
491
                ],
492
            ],
493
            "body" => [
494
                [
495
                    'body' => 'body',
496
                ],
497
                [
498
                    'body' => ['foo' => "bar", 'bar' => 'foo'],
499
                ],
500
                [
501
                    "path" => [],
502
                    "query" => null,
503
                    "headers" => ["Content-Type" => "application/json"],
504
                    'body' => json_encode(['foo' => "bar", 'bar' => 'foo']),
505
                ],
506
            ],
507
        ];
508
    }
509
510
    /** @test */
511
    public function itCanMakeASynchronousCallWithoutDefaultValueAndParameters()
512
    {
513
        $params = $this->getMockBuilder(Parameters::class)->disableOriginalConstructor()->getMock();
514
        $params->expects($this->once())->method('getIterator')->willReturn([]);
515
        $params->expects($this->never())->method('getByName');
516
517
        $baseUri = $this->createMock(UriInterface::class);
518
        $baseUri->expects($this->once())->method('withPath')->willReturn($baseUri);
519
        $baseUri->expects($this->once())->method('withQuery')->willReturn($baseUri);
520
521
        $responseDefinition = $this->getMockBuilder(ResponseDefinition::class)->disableOriginalConstructor()->getMock();
522
        $responseDefinition->expects($this->once())->method('hasBodySchema')->willReturn(true);
523
524
        $requestDefinition = $this->getMockBuilder(RequestDefinition::class)->disableOriginalConstructor()->getMock();
525
        $requestDefinition->expects($this->once())->method('getContentTypes')->willReturn(['application/json']);
526
        $requestDefinition->expects($this->once())->method('getRequestParameters')->willReturn($params);
527
        $requestDefinition->expects($this->once())->method('getMethod')->willReturn('GET');
528
        $requestDefinition->expects($this->once())->method('getPathTemplate')->willReturn('/foo/bar');
529
        $requestDefinition->expects($this->once())->method('getResponseDefinition')->with(200)->willReturn($responseDefinition);
530
531
        $this->config['baseUri'] = 'https://somewhere.tld';
532
        $this->uriFactory->expects($this->exactly(1))->method('createUri')->with('https://somewhere.tld')->willReturn($baseUri);
533
        $this->schema->expects($this->exactly(1))->method('getRequestDefinition')->with('operationId')->willReturn($requestDefinition);
534
        $this->messageFactory->expects($this->once())->method('createRequest')->willReturnCallback(function ($method, $uri, $headers, $body) {
535
            $this->assertEquals('GET', $method);
536
            $this->assertInstanceOf(UriInterface::class, $uri);
537
            $this->assertEquals(['Content-Type' => "application/json"], $headers);
538
            $this->assertNull($body);
539
540
            $request = $this->getMockBuilder(Request::class)->disableOriginalConstructor()->getMock();
541
542
            return $request;
543
        });
544
        $this->httpClient->expects($this->once())->method('sendRequest')->willReturnCallback(function ($request) {
545
            $this->assertInstanceOf(Request::class, $request);
546
547
            $stream =  $this->createMock(StreamInterface::class);
548
            $stream->expects($this->once())->method('__toString')->willReturn('body');
549
550
            $response = $this->createMock(ResponseInterface::class);
551
            $response->expects($this->once())->method('getStatusCode')->willReturn(200);
552
            $response->expects($this->once())->method('getBody')->willReturn($stream);
553
554
            return $response;
555
        });
556
        $this->serializer->expects($this->once())->method('deserialize')->willReturn(["items" => ['foo', 'bar']]);
557
        $this->messageValidator->expects($this->never())->method('validateResponse');
558
        $this->messageValidator->expects($this->exactly(1))->method('hasViolations')->willReturn(false);
559
560
        $service = $this->getApiService();
561
        $data = $service->call('operationId');
562
        $this->assertSame(['items' => ['foo', 'bar']], $data);
563
    }
564
565
    /**
566
     * @dataProvider dataProviderItShouldThrowExceptionBecauseThereAreSomeError
567
     *
568
     * @test
569
     *
570
     * @expectedException \Assert\InvalidArgumentException
571
     */
572
    public function itShouldThrowExceptionBecauseThereAreSomeError($config)
573
    {
574
        $this->uriTemplate->expects($this->never())->method('expand');
575
576
        $params = $this->getMockBuilder(Parameters::class)->disableOriginalConstructor()->getMock();
577
        $params->expects($this->never())->method('getIterator');
578
        $params->expects($this->never())->method('getByName');
579
580
        $responseDefinition = $this->getMockBuilder(ResponseDefinition::class)->disableOriginalConstructor()->getMock();
581
        $responseDefinition->expects($this->never())->method('hasBodySchema');
582
583
        $this->config['baseUri'] = 'https://somewhere.tld';
584
        $this->config = array_merge($this->config, $config);
585
        $this->uriFactory->expects($this->never())->method('createUri')->with('https://somewhere.tld');
586
        $this->schema->expects($this->never())->method('getRequestDefinition')->with('operationId');
587
        $this->messageFactory->expects($this->never())->method('createRequest');
588
        $this->httpClient->expects($this->never())->method('sendRequest');
589
590
        $service = $this->getApiService();
591
        $response = $service->call('operationId', ['foo' => 'bar']);
592
        $this->assertInstanceOf(ResponseInterface::class, $response);
593
    }
594
595
    public function dataProviderItShouldThrowExceptionBecauseThereAreSomeError()
596
    {
597
        return [
598
            [['returnResponse' => 'foo']],
599
            [['validateRequest' => 'foo']],
600
            [['validateResponse' => 'foo']],
601
            [['baseUri' => 42]],
602
        ];
603
    }
604
605
    /** @test */
606
    public function itShouldReturnTheResponse()
607
    {
608
        $this->uriTemplate->expects($this->once())->method('expand')->willReturnCallback(function ($pathTemplate, array $pathParameters) {
609
            $this->assertEquals('/foo/bar', $pathTemplate);
610
            $this->assertEquals([], $pathParameters);
611
        });
612
613
        $params = $this->getMockBuilder(Parameters::class)->disableOriginalConstructor()->getMock();
614
        $params->expects($this->once())->method('getIterator')->willReturn([]);
615
        $params->expects($this->once())->method('getByName')->willReturnCallback(function ($name) {
616
            $this->assertEquals("foo", $name);
617
            $param = $this->getMockBuilder(Parameter::class)->disableOriginalConstructor()->getMock();
618
            $param->expects($this->once())->method('getLocation')->willReturn('query');
619
620
            return $param;
621
        });
622
623
        $baseUri = $this->createMock(UriInterface::class);
624
        $baseUri->expects($this->once())->method('withPath')->willReturn($baseUri);
625
        $baseUri->expects($this->once())->method('withQuery')->willReturnCallback(function ($query) use ($baseUri) {
626
            $this->assertEquals('foo=bar', $query);
627
628
            return $baseUri;
629
        });
630
631
        $responseDefinition = $this->getMockBuilder(ResponseDefinition::class)->disableOriginalConstructor()->getMock();
632
        $responseDefinition->expects($this->never())->method('hasBodySchema');
633
634
        $requestDefinition = $this->getMockBuilder(RequestDefinition::class)->disableOriginalConstructor()->getMock();
635
        $requestDefinition->expects($this->once())->method('getContentTypes')->willReturn(['application/json']);
636
        $requestDefinition->expects($this->once())->method('getRequestParameters')->willReturn($params);
637
        $requestDefinition->expects($this->once())->method('getMethod')->willReturn('GET');
638
        $requestDefinition->expects($this->once())->method('getPathTemplate')->willReturn('/foo/bar');
639
        $requestDefinition->expects($this->once())->method('getResponseDefinition')->with(200)->willReturn($responseDefinition);
640
641
        $this->config['baseUri'] = 'https://somewhere.tld';
642
        $this->config['returnResponse'] = true;
643
        $this->uriFactory->expects($this->exactly(1))->method('createUri')->with('https://somewhere.tld')->willReturn($baseUri);
644
        $this->schema->expects($this->exactly(1))->method('getRequestDefinition')->with('operationId')->willReturn($requestDefinition);
645
        $this->messageFactory->expects($this->once())->method('createRequest')->willReturnCallback(function ($method, $uri, $headers, $body) {
646
            $this->assertEquals('GET', $method);
647
            $this->assertInstanceOf(UriInterface::class, $uri);
648
            $this->assertEquals(['Content-Type' => 'application/json'], $headers);
649
            $this->assertNull($body);
650
651
            $request = $this->getMockBuilder(Request::class)->disableOriginalConstructor()->getMock();
652
653
            return $request;
654
        });
655
        $this->httpClient->expects($this->once())->method('sendRequest')->willReturnCallback(function ($request) {
656
            $this->assertInstanceOf(Request::class, $request);
657
658
            $response = $this->createMock(ResponseInterface::class);
659
            $response->expects($this->once())->method('getStatusCode')->willReturn(200);
660
            $response->expects($this->never())->method('getBody');
661
662
            return $response;
663
        });
664
665
        $service = $this->getApiService();
666
        $response = $service->call('operationId', ['foo' => 'bar']);
667
        $this->assertInstanceOf(ResponseInterface::class, $response);
668
    }
669
670
    /** @test */
671
    public function itShouldNotReturnTheResponseOrAnyDataBecauseThereAreSomeViolationInRequest()
672
    {
673
        $this->uriTemplate->expects($this->once())->method('expand')->willReturnCallback(function ($pathTemplate, array $pathParameters) {
674
            $this->assertEquals('/foo/bar', $pathTemplate);
675
            $this->assertEquals([], $pathParameters);
676
        });
677
678
        $params = $this->getMockBuilder(Parameters::class)->disableOriginalConstructor()->getMock();
679
        $params->expects($this->once())->method('getIterator')->willReturn([]);
680
        $params->expects($this->once())->method('getByName')->willReturnCallback(function ($name) {
681
            $this->assertEquals('foo', $name);
682
            $param = $this->getMockBuilder(Parameter::class)->disableOriginalConstructor()->getMock();
683
            $param->expects($this->once())->method('getLocation')->willReturn('query');
684
685
            return $param;
686
        });
687
688
        $baseUri = $this->createMock(UriInterface::class);
689
        $baseUri->expects($this->once())->method('withPath')->willReturnCallback(function ($path) use ($baseUri) {
690
            $this->assertEquals("", $path);
691
692
            return $baseUri;
693
        });
694
        $baseUri->expects($this->once())->method('withQuery')->willReturnCallback(function ($query) use ($baseUri) {
695
            $this->assertEquals("foo=bar", $query);
696
697
            return $baseUri;
698
        });
699
700
        $requestDefinition = $this->getMockBuilder(RequestDefinition::class)->disableOriginalConstructor()->getMock();
701
        $requestDefinition->expects($this->once())->method('getContentTypes')->willReturn(['application/json']);
702
        $requestDefinition->expects($this->once())->method('getRequestParameters')->willReturn($params);
703
        $requestDefinition->expects($this->once())->method('getMethod')->willReturn('GET');
704
        $requestDefinition->expects($this->once())->method('getPathTemplate')->willReturn('/foo/bar');
705
        $requestDefinition->expects($this->never())->method('getResponseDefinition');
706
707
        $this->config['baseUri'] = 'https://somewhere.tld';
708
        $this->config['validateResponse'] = true;
709
        $this->config['validateRequest'] = true;
710
        $this->uriFactory->expects($this->exactly(1))->method('createUri')->with('https://somewhere.tld')->willReturn($baseUri);
711
        $this->schema->expects($this->exactly(1))->method('getRequestDefinition')->with('operationId')->willReturn($requestDefinition);
712
        $this->messageFactory->expects($this->once())->method('createRequest')->willReturnCallback(function ($method, $uri, $headers, $body) {
713
            $this->assertEquals('GET', $method);
714
            $this->assertInstanceOf(UriInterface::class, $uri);
715
            $this->assertEquals(['Content-Type' => 'application/json'], $headers);
716
            $this->assertNull($body);
717
718
            $request = $this->getMockBuilder(Request::class)->disableOriginalConstructor()->getMock();
719
720
            return $request;
721
        });
722
        $this->httpClient->expects($this->never())->method('sendRequest');
723
        $this->messageValidator->expects($this->never())->method('validateResponse');
724
        $this->messageValidator->expects($this->once())->method('hasViolations')->willReturn(true);
725
        $this->messageValidator->expects($this->once())->method('getViolations')->willReturnCallback(function () {
726
            $violation = $this->getMockBuilder(ConstraintViolation::class)->disableOriginalConstructor()->getMock();
727
            $violation->expects($this->once())->method('getProperty')->willReturn('foo');
728
            $violation->expects($this->once())->method('getMessage')->willReturn('required');
729
            $violation->expects($this->once())->method('getConstraint')->willReturn('bar');
730
            $violation->expects($this->once())->method('getLocation')->willReturn('query');
731
732
            return [$violation];
733
        });
734
735
        try {
736
            $service = $this->getApiService();
737
            $service->call('operationId', ['foo' => 'bar']);
738
        } catch (RequestViolations $e) {
739
            $this->assertEquals("Request constraint violations:\n[property]: foo\n[message]: required\n[constraint]: bar\n[location]: query\n\n", $e->getMessage());
740
741
            return;
742
        }
743
        $this->fail("This test must throw a RequestViolations Exception");
744
    }
745
746
    /** @test */
747
    public function itShouldNotReturnTheResponseOrAnyDataBecauseThereAreSomeViolationInResponse()
748
    {
749
        $this->uriTemplate->expects($this->once())->method('expand')->willReturnCallback(function ($pathTemplate, array $pathParameters) {
750
            $this->assertEquals('/foo/bar', $pathTemplate);
751
            $this->assertEquals([], $pathParameters);
752
        });
753
754
        $params = $this->getMockBuilder(Parameters::class)->disableOriginalConstructor()->getMock();
755
        $params->expects($this->once())->method('getIterator')->willReturn([]);
756
        $params->expects($this->once())->method('getByName')->willReturnCallback(function ($name) {
757
            $this->assertEquals('foo', $name);
758
            $param = $this->getMockBuilder(Parameter::class)->disableOriginalConstructor()->getMock();
759
            $param->expects($this->once())->method('getLocation')->willReturn('query');
760
761
            return $param;
762
        });
763
764
        $baseUri = $this->createMock(UriInterface::class);
765
        $baseUri->expects($this->once())->method('withPath')->willReturnCallback(function ($path) use ($baseUri) {
766
            $this->assertEquals("", $path);
767
768
            return $baseUri;
769
        });
770
        $baseUri->expects($this->once())->method('withQuery')->willReturnCallback(function ($query) use ($baseUri) {
771
            $this->assertEquals("foo=bar", $query);
772
773
            return $baseUri;
774
        });
775
776
        $requestDefinition = $this->getMockBuilder(RequestDefinition::class)->disableOriginalConstructor()->getMock();
777
        $requestDefinition->expects($this->once())->method('getContentTypes')->willReturn(['application/json']);
778
        $requestDefinition->expects($this->once())->method('getRequestParameters')->willReturn($params);
779
        $requestDefinition->expects($this->once())->method('getMethod')->willReturn('GET');
780
        $requestDefinition->expects($this->once())->method('getPathTemplate')->willReturn('/foo/bar');
781
        $requestDefinition->expects($this->never())->method('getResponseDefinition');
782
783
        $this->config['baseUri'] = 'https://somewhere.tld';
784
        $this->config['validateResponse'] = true;
785
        $this->config['validateRequest'] = false;
786
        $this->uriFactory->expects($this->exactly(1))->method('createUri')->with('https://somewhere.tld')->willReturn($baseUri);
787
        $this->schema->expects($this->exactly(1))->method('getRequestDefinition')->with('operationId')->willReturn($requestDefinition);
788
        $this->messageFactory->expects($this->once())->method('createRequest')->willReturnCallback(function ($method, $uri, $headers, $body) {
789
            $this->assertEquals('GET', $method);
790
            $this->assertInstanceOf(UriInterface::class, $uri);
791
            $this->assertEquals(['Content-Type' => 'application/json'], $headers);
792
            $this->assertNull($body);
793
794
            $request = $this->getMockBuilder(Request::class)->disableOriginalConstructor()->getMock();
795
796
            return $request;
797
        });
798
        $this->httpClient->expects($this->once())->method('sendRequest')->willReturnCallback(function ($request) {
799
            $this->assertInstanceOf(Request::class, $request);
800
801
            $response = $this->createMock(ResponseInterface::class);
802
            $response->expects($this->never())->method('getStatusCode');
803
            $response->expects($this->never())->method('getBody');
804
805
            return $response;
806
        });
807
        $this->messageValidator->expects($this->once())->method('validateResponse');
808
        $this->messageValidator->expects($this->once())->method('hasViolations')->willReturn(true);
809
        $this->messageValidator->expects($this->once())->method('getViolations')->willReturnCallback(function () {
810
            $violation = $this->getMockBuilder(ConstraintViolation::class)->disableOriginalConstructor()->getMock();
811
            $violation->expects($this->once())->method('getProperty')->willReturn('foo');
812
            $violation->expects($this->once())->method('getMessage')->willReturn('required');
813
            $violation->expects($this->once())->method('getConstraint')->willReturn('bar');
814
            $violation->expects($this->once())->method('getLocation')->willReturn('query');
815
816
            return [$violation];
817
        });
818
819
        try {
820
            $service = $this->getApiService();
821
            $service->call('operationId', ['foo' => 'bar']);
822
        } catch (ResponseViolations $e) {
823
            $this->assertEquals("Request constraint violations:\n[property]: foo\n[message]: required\n[constraint]: bar\n[location]: query\n\n", $e->getMessage());
824
825
            return;
826
        }
827
        $this->fail("This test must throw a ResponseViolations Exception");
828
    }
829
830
    /**
831
     * @throws \Assert\AssertionFailedException
832
     *
833
     * @return ApiService
834
     */
835
    private function getApiService()
836
    {
837
        return new ApiService(
838
            $this->uriFactory,
839
            $this->uriTemplate,
840
            $this->httpClient,
841
            $this->messageFactory,
842
            $this->schema,
843
            $this->messageValidator,
844
            $this->serializer,
845
            $this->config
846
        );
847
    }
848
}
849