Completed
Push — master ( 14fe81...675c2f )
by Guillem
02:29
created

itValidatesPathWithCoercion()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 29
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 18
dl 0
loc 29
rs 9.6666
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
namespace ElevenLabs\Api\Validator;
3
4
use ElevenLabs\Api\Decoder\Adapter\SymfonyDecoderAdapter;
5
use ElevenLabs\Api\Definition\MessageDefinition;
6
use ElevenLabs\Api\Definition\RequestDefinition;
7
use ElevenLabs\Api\Definition\ResponseDefinition;
8
use JsonSchema\Validator;
9
use PHPUnit\Framework\TestCase;
10
use Psr\Http\Message\MessageInterface;
11
use Psr\Http\Message\RequestInterface;
12
use Psr\Http\Message\ResponseInterface;
13
use Psr\Http\Message\UriInterface;
14
use Symfony\Component\Serializer\Encoder\JsonDecode;
15
16
/**
17
 * Class RequestValidatorTest
18
 */
19
class MessageValidatorTest extends TestCase
20
{
21
    /** @var MessageValidator */
22
    private $messageValidator;
23
24
    public function setUp()
25
    {
26
        $validator = new Validator();
27
        $decoder = new SymfonyDecoderAdapter(new JsonDecode());
28
29
        $this->messageValidator = new MessageValidator(
30
            $validator,
31
            $decoder
32
        );
33
    }
34
35
    /** @test */
36
    public function itValidateAMessageContentType()
37
    {
38
        $expectedViolations = [
39
            new ConstraintViolation(
40
                'Content-Type',
41
                'Content-Type should not be empty',
42
                'required',
43
                'header'
44
            )
45
        ];
46
47
        $message = $this->prophesize(MessageInterface::class);
48
        $message->getHeaderLine('Content-Type')->willReturn('');
49
50
        $definition = $this->prophesize(MessageDefinition::class);
51
        $definition->getContentTypes()->willReturn(['application/json']);
52
53
        $this->messageValidator->validateContentType(
54
            $message->reveal(),
55
            $definition->reveal()
56
        );
57
58
        assertThat($this->messageValidator->hasViolations(), isTrue());
59
        assertThat($this->messageValidator->getViolations(), containsOnlyInstancesOf(ConstraintViolation::class));
60
        assertThat($this->messageValidator->getViolations(), equalTo($expectedViolations));
61
    }
62
63
    /** @test */
64
    public function itValidateAMessageUnsupportedContentType()
65
    {
66
        $expectedViolations = [
67
            new ConstraintViolation(
68
                'Content-Type',
69
                'text/plain is not a supported content type, supported: application/json',
70
                'enum',
71
                'header'
72
            )
73
        ];
74
75
        $message = $this->prophesize(MessageInterface::class);
76
        $message->getHeaderLine('Content-Type')->willReturn('text/plain');
77
78
        $definition = $this->prophesize(MessageDefinition::class);
79
        $definition->getContentTypes()->willReturn(['application/json']);
80
81
        $this->messageValidator->validateContentType(
82
            $message->reveal(),
83
            $definition->reveal()
84
        );
85
86
        assertThat($this->messageValidator->hasViolations(), isTrue());
87
        assertThat($this->messageValidator->getViolations(), containsOnlyInstancesOf(ConstraintViolation::class));
88
        assertThat($this->messageValidator->getViolations(), equalTo($expectedViolations));
89
    }
90
91
    /** @test */
92
    public function itValidateAMessageHeaders()
93
    {
94
        $expectedViolation = [
95
            new ConstraintViolation(
96
                'X-Required-Header',
97
                'The property X-Required-Header is required',
98
                'required',
99
                'header'
100
            )
101
        ];
102
103
        $headersSchema = $this->toObject([
104
            'type' => 'object',
105
            'required' => ['X-Required-Header'],
106
            'properties' => [
107
                'X-Required-Header' => [
108
                    'type' => 'string'
109
                ]
110
            ]
111
        ]);
112
113
        $message = $this->prophesize(MessageInterface::class);
114
        $message->getHeaders()->willReturn(['X-Foo' => ['bar', 'baz']]);
115
116
        $definition = $this->prophesize(MessageDefinition::class);
117
        $definition->hasHeadersSchema()->willReturn(true);
118
        $definition->getHeadersSchema()->willReturn($headersSchema);
119
120
        $this->messageValidator->validateHeaders(
121
            $message->reveal(),
122
            $definition->reveal()
123
        );
124
125
        assertThat($this->messageValidator->hasViolations(), isTrue());
126
        assertThat($this->messageValidator->getViolations(), containsOnlyInstancesOf(ConstraintViolation::class));
127
        assertThat($this->messageValidator->getViolations(), equalTo($expectedViolation));
128
    }
129
130
    /** @test */
131
    public function itValidateTheRequestBody()
132
    {
133
        $expectedViolation = [
134
            new ConstraintViolation(
135
                'id',
136
                'String value found, but an integer is required',
137
                'type',
138
                'body'
139
            ),
140
        ];
141
142
        $bodySchema = $this->toObject([
143
            'type' => 'object',
144
            'properties' => [
145
                'id' => [
146
                    'type' => 'integer',
147
                    'format' => 'int32'
148
                ]
149
            ]
150
        ]);
151
152
        $message = $this->prophesize(MessageInterface::class);
153
        $message->getHeaderLine('Content-Type')->willReturn('application/json');
154
        $message->getBody()->willReturn('{"id": "invalid"}');
155
156
        $definition = $this->prophesize(MessageDefinition::class);
157
        $definition->getContentTypes()->willReturn(['application/json']);
158
        $definition->hasBodySchema()->willReturn(true);
159
        $definition->getBodySchema()->willReturn($bodySchema);
160
161
        $this->messageValidator->validateMessageBody(
162
            $message->reveal(),
163
            $definition->reveal()
164
        );
165
166
        assertThat($this->messageValidator->hasViolations(), isTrue());
167
        assertThat($this->messageValidator->getViolations(), containsOnlyInstancesOf(ConstraintViolation::class));
168
        assertThat($this->messageValidator->getViolations(), equalTo($expectedViolation));
169
    }
170
171
    /** @test */
172
    public function itValidatesPathWithCoercion()
173
    {
174
        $pathParametersSchema = $this->toObject([
175
            'type' => 'object',
176
            'properties' => [
177
                'id' => [
178
                    'type' => 'integer',
179
                    'minimum' => 1,
180
                ],
181
            ],
182
        ]);
183
184
        $requestUri = $this->prophesize(UriInterface::class);
185
        $requestUri->getPath()->willReturn('/users/1');
186
187
        $request = $this->prophesize(RequestInterface::class);
188
        $request->getUri()->willReturn($requestUri);
189
190
        $definition = $this->prophesize(RequestDefinition::class);
191
        $definition->getPathTemplate()->willReturn('/users/{id}');
192
        $definition->hasPathSchema()->willReturn(true);
193
        $definition->getPathSchema()->willReturn($pathParametersSchema);
194
195
        $this->messageValidator->validatePath(
196
            $request->reveal(),
197
            $definition->reveal()
198
        );
199
200
        assertThat($this->messageValidator->hasViolations(), isFalse());
201
    }
202
203
    /** @test */
204
    public function itValidateARequestQueryParameters()
205
    {
206
        $expectedViolation = [
207
            new ConstraintViolation(
208
                'limit',
209
                'String value found, but an integer is required',
210
                'type',
211
                'query'
212
            )
213
        ];
214
215
        $queryParametersSchema = $this->toObject([
216
            'type' => 'object',
217
            'properties' => [
218
                'limit' => [
219
                    'type' => 'integer'
220
                ]
221
            ]
222
        ]);
223
224
        $requestUri = $this->prophesize(UriInterface::class);
225
        $requestUri->getQuery()->willreturn('limit=invalid');
226
227
        $request = $this->prophesize(RequestInterface::class);
228
        $request->getUri()->willReturn($requestUri);
229
230
        $definition = $this->prophesize(RequestDefinition::class);
231
        $definition->hasQueryParametersSchema()->willReturn(true);
232
        $definition->getQueryParametersSchema()->willReturn($queryParametersSchema);
233
234
        $this->messageValidator->validateQueryParameters(
235
            $request->reveal(),
236
            $definition->reveal()
237
        );
238
239
        assertThat($this->messageValidator->hasViolations(), isTrue());
240
        assertThat($this->messageValidator->getViolations(), containsOnlyInstancesOf(ConstraintViolation::class));
241
        assertThat($this->messageValidator->getViolations(), equalTo($expectedViolation));
242
    }
243
244
    /** @test */
245
    public function itValidateARequest()
246
    {
247
        $expectedViolations = [
248
            new ConstraintViolation('id', 'String value found, but an integer is required', 'type', 'body'),
249
            new ConstraintViolation('X-Required-Header', 'The property X-Required-Header is required', 'required', 'header'),
250
            new ConstraintViolation('id', 'String value found, but an integer is required', 'type', 'path'),
251
            new ConstraintViolation('limit', 'String value found, but an integer is required', 'type', 'query'),
252
        ];
253
254
        $headersSchema = $this->toObject([
255
            'type' => 'object',
256
            'required' => ['X-Required-Header'],
257
            'properties' => [
258
                'X-Required-Header' => [
259
                    'type' => 'string'
260
                ]
261
            ]
262
        ]);
263
264
        $pathSchema = $this->toObject([
265
            'type' => 'object',
266
            'properties' => [
267
                'id' => [
268
                    'type' => 'integer',
269
                ],
270
            ],
271
        ]);
272
273
        $bodySchema = $this->toObject([
274
            'type' => 'object',
275
            'properties' => [
276
                'id' => [
277
                    'type' => 'integer',
278
                    'format' => 'int32'
279
                ]
280
            ]
281
        ]);
282
283
        $queryParametersSchema = $this->toObject([
284
            'type' => 'object',
285
            'properties' => [
286
                'limit' => [
287
                    'type' => 'integer'
288
                ]
289
            ]
290
        ]);
291
292
        $uri = $this->prophesize(UriInterface::class);
293
        $uri->getPath()->willReturn('/users');
294
        $uri->getQuery()->willReturn('limit=invalid');
295
296
        $request = $this->prophesize(RequestInterface::class);
297
        $request->getMethod()->willReturn('POST');
298
        $request->getUri()->willReturn($uri);
299
        $request->getBody()->willReturn('{"id": "invalid"}');
300
        $request->getHeaderLine('Content-Type')->willReturn('application/json');
301
        $request->getHeaders()->willReturn([]);
302
303
        $definition = $this->prophesize(RequestDefinition::class);
304
        $definition->getContentTypes()->willReturn(['application/json']);
305
        $definition->getPathTemplate()->willReturn('/users/{id}');
306
        $definition->hasBodySchema()->willReturn(true);
307
        $definition->getBodySchema()->willReturn($bodySchema);
308
        $definition->hasHeadersSchema()->willReturn(true);
309
        $definition->getHeadersSchema()->willReturn($headersSchema);
310
        $definition->hasPathSchema()->willReturn(true);
311
        $definition->getPathSchema()->willReturn($pathSchema);
312
        $definition->hasQueryParametersSchema()->willReturn(true);
313
        $definition->getQueryParametersSchema()->willReturn($queryParametersSchema);
314
315
        $this->messageValidator->validateRequest(
316
            $request->reveal(),
317
            $definition->reveal()
318
        );
319
320
        assertThat($this->messageValidator->hasViolations(), isTrue());
321
        assertThat($this->messageValidator->getViolations(), containsOnlyInstancesOf(ConstraintViolation::class));
322
        assertThat($this->messageValidator->getViolations(), equalTo($expectedViolations));
323
    }
324
325
    /** @test */
326
    public function itValidateAResponse()
327
    {
328
        $expectedViolations = [
329
            new ConstraintViolation('id', 'String value found, but an integer is required', 'type', 'body'),
330
            new ConstraintViolation('X-Required-Header', 'The property X-Required-Header is required', 'required', 'header'),
331
        ];
332
333
        $headersSchema = $this->toObject([
334
            'type' => 'object',
335
            'required' => ['X-Required-Header'],
336
            'properties' => [
337
                'X-Required-Header' => [
338
                    'type' => 'string'
339
                ]
340
            ]
341
        ]);
342
343
        $bodySchema = $this->toObject([
344
            'type' => 'object',
345
            'properties' => [
346
                'id' => [
347
                    'type' => 'integer',
348
                    'format' => 'int32'
349
                ]
350
            ]
351
        ]);
352
353
        $response = $this->prophesize(ResponseInterface::class);
354
        $response->getStatusCode()->willReturn('200');
355
        $response->getBody()->willReturn('{"id": "invalid"}');
356
        $response->getHeaderLine('Content-Type')->willReturn('application/json');
357
        $response->getHeaders()->willReturn([]);
358
359
        $responseDefinition = $this->prophesize(RequestDefinition::class);
360
        $responseDefinition->getContentTypes()->willReturn(['application/json']);
361
        $responseDefinition->hasBodySchema()->willReturn(true);
362
        $responseDefinition->getBodySchema()->willReturn($bodySchema);
363
        $responseDefinition->hasHeadersSchema()->willReturn(true);
364
        $responseDefinition->getHeadersSchema()->willReturn($headersSchema);
365
366
        $definition = $this->prophesize(RequestDefinition::class);
367
        $definition->getResponseDefinition('200')->willReturn($responseDefinition);
368
369
        $this->messageValidator->validateResponse(
370
            $response->reveal(),
371
            $definition->reveal()
372
        );
373
374
        assertThat($this->messageValidator->hasViolations(), isTrue());
375
        assertThat($this->messageValidator->getViolations(), containsOnlyInstancesOf(ConstraintViolation::class));
376
        assertThat($this->messageValidator->getViolations(), equalTo($expectedViolations));
377
    }
378
379
    private function toObject(array $array)
380
    {
381
        return json_decode(json_encode($array));
382
    }
383
}
384