Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1441 ariadna 1
<?php
2
// This file is part of Moodle - http://moodle.org/
3
//
4
// Moodle is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// Moodle is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
 
17
namespace core\router;
18
 
19
use core\param;
20
use core\router\schema\parameters\path_parameter;
21
use core\router\schema\parameters\query_parameter;
22
use core\router\schema\request_body;
23
use core\router\schema\response\content\payload_response_type;
24
use core\tests\router\route_testcase;
25
use GuzzleHttp\Psr7\ServerRequest;
26
use Psr\Http\Message\ServerRequestInterface;
27
use Slim\Exception\HttpNotFoundException;
28
 
29
/**
30
 * Tests for the request validator.
31
 *
32
 * @package    core
33
 * @category   test
34
 * @copyright  Andrew Lyons <andrew@nicols.co.uk>
35
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
36
 * @covers     \core\router\request_validator
37
 */
38
final class request_validator_test extends route_testcase {
39
    /**
40
     * Request validation on a route which does not have a matching Moodle route attribute.
41
     */
42
    public function test_validate_request_not_moodle_route(): void {
43
        $request = new ServerRequest('GET', '/example');
44
        $validator = \core\di::get(request_validator::class);
45
 
46
        $this->assertInstanceOf(
47
            ServerRequestInterface::class,
48
            $validator->validate_request($request),
49
        );
50
    }
51
 
52
    /**
53
     * A basic test of request validation.
54
     */
55
    public function test_validate_request(): void {
56
        // The route being tested.
57
        $route = new route(
58
            path: '/example/{required}',
59
            pathtypes: [
60
                new path_parameter(
61
                    name: 'required',
62
                    type: param::INT,
63
                ),
64
            ],
65
        );
66
        $request = $this->get_request_for_routed_route($route, '/example/123');
67
        $validator = \core\di::get(request_validator::class);
68
 
69
        $this->assertInstanceOf(
70
            ServerRequestInterface::class,
71
            $validator->validate_request($request),
72
        );
73
    }
74
 
75
    /**
76
     * A basic test of request validation.
77
     */
78
    public function test_validate_request_missing_pathtype(): void {
79
        // A route with a parameter defined in the path, but no pathtype for it.
80
        $route = new route(
81
            path: '/example/{required}',
82
        );
83
 
84
        $request = $this->get_request_for_routed_route($route, '/example/123');
85
 
86
        $validator = \core\di::get(request_validator::class);
87
        $this->assertInstanceOf(
88
            ServerRequestInterface::class,
89
            $validator->validate_request($request),
90
        );
91
    }
92
 
93
    /**
94
     * When a pathtype fails to validate, it will result in an HttpNotFoundException.
95
     */
96
    public function test_validate_request_invalid_path_component(): void {
97
        // Most of the path types are converted to regexes and will lead to a 404 before they get this far.
98
        $type = param::INT;
99
        $this->assertEmpty(
100
            $type->get_clientside_expression(),
101
            'This test requires a type with no clientside expression. Please update the test.',
102
        );
103
 
104
        $route = new route(
105
            path: '/example/{required}',
106
            pathtypes: [
107
                new path_parameter(
108
                    name: 'required',
109
                    type: $type,
110
                ),
111
            ],
112
        );
113
 
114
        $request = $this->get_request_for_routed_route($route, '/example/abc');
115
 
116
        $validator = \core\di::get(request_validator::class);
117
        $this->expectException(HttpNotFoundException::class);
118
        $validator->validate_request($request);
119
    }
120
 
121
    /**
122
     * When a pathtype fails to validate, it will result in an HttpNotFoundException.
123
     */
124
    public function test_validate_request_invalid_path_component_native(): void {
125
        // Most of the path types are converted to regexes and will lead to a 404 before they get this far.
126
        $type = param::ALPHA;
127
        $this->assertNotEmpty(
128
            $type->get_clientside_expression(),
129
            'This test requires a type with clientside expression. Please update the test.',
130
        );
131
 
132
        $route = new route(
133
            path: '/example/{required}',
134
            pathtypes: [
135
                new path_parameter(
136
                    name: 'required',
137
                    type: $type,
138
                ),
139
            ],
140
        );
141
 
142
        // A value which does not meet the param validation.
143
        $request = $this->get_request_for_routed_route($route, '/example/123');
144
 
145
        $validator = \core\di::get(request_validator::class);
146
        $this->expectException(HttpNotFoundException::class);
147
        $validator->validate_request($request);
148
    }
149
 
150
    /**
151
     * Query parameter validation.
152
     */
153
    public function test_validate_request_query_parameter_valid(): void {
154
        $type = param::INT;
155
        $value = 123;
156
 
157
        $route = new route(
158
            path: '/example',
159
            queryparams: [
160
                new query_parameter(
161
                    name: 'required',
162
                    type: $type,
163
                ),
164
            ],
165
        );
166
 
167
        $request = $this->get_request_for_routed_route($route, "/example?required={$value}");
168
        $this->assertEquals($value, $request->getQueryParams()['required']);
169
 
170
        // Validate the request.
171
        $validator = \core\di::get(request_validator::class);
172
        $validatedrequest = $validator->validate_request($request);
173
        $this->assertInstanceOf(ServerRequestInterface::class, $validatedrequest);
174
        $this->assertEquals($value, $validatedrequest->getQueryParams()['required']);
175
    }
176
 
177
    /**
178
     * Query parameter validation failure.
179
     */
180
    public function test_validate_request_query_parameter_invalid(): void {
181
        $type = param::INT;
182
        $value = 'abc';
183
 
184
        $route = new route(
185
            path: '/example',
186
            queryparams: [
187
                new query_parameter(
188
                    name: 'required',
189
                    type: $type,
190
                ),
191
            ],
192
        );
193
 
194
        $request = $this->get_request_for_routed_route($route, "/example?required={$value}");
195
        $this->assertEquals($value, $request->getQueryParams()['required']);
196
 
197
        // Validate the request.
198
        $validator = \core\di::get(request_validator::class);
199
        $this->expectException(\invalid_parameter_exception::class);
200
        $validator->validate_request($request);
201
    }
202
 
203
    /**
204
     * Validate a request body which is expected.
205
     */
206
    public function test_validate_request_body_valid(): void {
207
        $route = new route(
208
            path: '/example',
209
            requestbody: new request_body(
210
                content: new payload_response_type(
211
                    schema: new \core\router\schema\objects\schema_object(
212
                        content: [
213
                            'preferences' => new \core\router\schema\objects\array_of_strings(
214
                                keyparamtype: param::TEXT,
215
                                valueparamtype: param::INT,
216
                            ),
217
                        ],
218
                    ),
219
                ),
220
            ),
221
        );
222
 
223
        $request = $this->get_request_for_routed_route($route, "/example");
224
        $request = $request->withParsedBody([
225
            'preferences' => [
226
                'key' => 42,
227
            ],
228
        ]);
229
 
230
        // Validate the request.
231
        $validator = \core\di::get(request_validator::class);
232
        $result = $validator->validate_request($request);
233
    }
234
 
235
    /**
236
     * Validate a request body which is expected.
237
     */
238
    public function test_validate_request_body_invalid(): void {
239
        $route = new route(
240
            path: '/example',
241
            requestbody: new request_body(
242
                content: new payload_response_type(
243
                    schema: new \core\router\schema\objects\schema_object(
244
                        content: [
245
                            'preferences' => new \core\router\schema\objects\array_of_strings(
246
                                keyparamtype: param::TEXT,
247
                                valueparamtype: param::INT,
248
                            ),
249
                        ],
250
                    ),
251
                ),
252
            ),
253
        );
254
 
255
        $request = $this->get_request_for_routed_route($route, "/example");
256
        $request = $request->withParsedBody([
257
            'preferences' => [
258
                'key' => 'value',
259
            ],
260
        ]);
261
 
262
        // Validate the request.
263
        $validator = \core\di::get(request_validator::class);
264
        $this->expectException(\invalid_parameter_exception::class);
265
        $validator->validate_request($request);
266
    }
267
 
268
    /**
269
     * Validate a request body which is optional.
270
     */
271
    public function test_validate_request_body_missing_optional(): void {
272
        $route = new route(
273
            path: '/example',
274
            requestbody: new request_body(
275
                content: new payload_response_type(
276
                    schema: new \core\router\schema\objects\schema_object(
277
                        content: [
278
                            'preferences' => new \core\router\schema\objects\array_of_strings(
279
                                keyparamtype: param::TEXT,
280
                                valueparamtype: param::INT,
281
                            ),
282
                        ],
283
                        required: false,
284
                    ),
285
                ),
286
            ),
287
        );
288
 
289
        $request = $this->get_request_for_routed_route($route, "/example");
290
 
291
        // Validate the request.
292
        $validator = \core\di::get(request_validator::class);
293
        $result = $validator->validate_request($request);
294
        $this->assertInstanceOf(ServerRequestInterface::class, $result);
295
        $this->assertInstanceOf(
296
            ServerRequestInterface::class,
297
            $result,
298
        );
299
    }
300
 
301
    /**
302
     * Validate a request body which is expected.
303
     */
304
    public function test_validate_request_body_missing_required(): void {
305
        $route = new route(
306
            path: '/example',
307
            requestbody: new request_body(
308
                content: new payload_response_type(
309
                    schema: new \core\router\schema\objects\schema_object(
310
                        content: [
311
                            'preferences' => new \core\router\schema\objects\array_of_strings(
312
                                keyparamtype: param::TEXT,
313
                                valueparamtype: param::INT,
314
                            ),
315
                        ],
316
                    ),
317
                ),
318
                required: true,
319
            ),
320
        );
321
 
322
        $request = $this->get_request_for_routed_route($route, "/example");
323
 
324
        // Validate the request.
325
        $validator = \core\di::get(request_validator::class);
326
        $this->expectException(\invalid_parameter_exception::class);
327
        $validator->validate_request($request);
328
    }
329
 
330
    /**
331
     * Validate a request header is appropriately handled.
332
     */
333
    public function test_validate_request_header_valid(): void {
334
        $route = new route(
335
            path: '/example',
336
            headerparams: [
337
                new \core\router\schema\parameters\header_object(
338
                    name: 'Accept',
339
                    description: 'The media type of the response',
340
                    type: param::TEXT,
341
                ),
342
                new \core\router\schema\parameters\header_object(
343
                    name: 'X-Multiple',
344
                    description: 'A header with multiple values',
345
                    type: param::TEXT,
346
                    multiple: true,
347
                ),
348
            ],
349
        );
350
 
351
        $request = $this->get_request_for_routed_route($route, "/example")
352
            // A known header.
353
            ->withHeader('Accept', 'application/json')
354
            // An unknown header is kept.
355
            ->withHeader('X-Example', 'example')
356
            // A known header with multiple values.
357
            ->withAddedHeader('X-Multiple', 'value1')
358
            ->withAddedHeader('X-Multiple', 'value2')
359
            // An unknown header with multiple values.
360
            ->withAddedHeader('X-Unknown', 'value1')
361
            ->withAddedHeader('X-Unknown', 'value2');
362
 
363
        // Validate the request.
364
        $validator = \core\di::get(request_validator::class);
365
        $result = $validator->validate_request($request);
366
        $this->assertInstanceOf(ServerRequestInterface::class, $result);
367
 
368
        $this->assertEquals('application/json', $result->getHeaderLine('Accept'));
369
        $this->assertEquals('example', $result->getHeaderLine('X-Example'));
370
        $this->assertEquals(['value1', 'value2'], $result->getHeader('X-Multiple'));
371
        $this->assertEquals(['value1', 'value2'], $result->getHeader('X-Unknown'));
372
    }
373
}