xref: /webtrees/tests/app/ValidatorTest.php (revision a5f003cf4c42de438f568dff311c4fbb7495e541)
1<?php
2
3/**
4 * webtrees: online genealogy
5 * Copyright (C) 2022 webtrees development team
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18declare(strict_types=1);
19
20namespace Fisharebest\Webtrees;
21
22use Aura\Router\Route;
23use Fisharebest\Webtrees\Contracts\UserInterface;
24use Fisharebest\Webtrees\Http\Exceptions\HttpBadRequestException;
25use Psr\Http\Message\ServerRequestInterface;
26
27/**
28 * Test harness for the class Validator
29 */
30class ValidatorTest extends TestCase
31{
32
33    /**
34     * @covers \Fisharebest\Webtrees\Validator::attributes
35     * @covers \Fisharebest\Webtrees\Validator::__construct
36     */
37    public function testAttributes(): void
38    {
39        $request = $this->createStub(ServerRequestInterface::class);
40        $request
41            ->method('getAttributes')
42            ->willReturn(['param' => 'test']);
43
44        self::assertSame('test', Validator::attributes($request)->string('param'));
45    }
46
47    /**
48     * @covers \Fisharebest\Webtrees\Validator::parsedBody
49     * @covers \Fisharebest\Webtrees\Validator::__construct
50     */
51    public function testParsedBody(): void
52    {
53        $request = $this->createStub(ServerRequestInterface::class);
54        $request
55            ->method('getParsedBody')
56            ->willReturn(['param' => 'test']);
57
58        self::assertSame('test', Validator::parsedBody($request)->string('param'));
59    }
60
61    /**
62     * @covers \Fisharebest\Webtrees\Validator::queryParams
63     * @covers \Fisharebest\Webtrees\Validator::__construct
64     */
65    public function testQueryParams(): void
66    {
67        $request = $this->createStub(ServerRequestInterface::class);
68        $request
69            ->method('getQueryParams')
70            ->willReturn(['param' => 'test']);
71
72        self::assertSame('test', Validator::queryParams($request)->string('param'));
73    }
74
75    /**
76     * @covers \Fisharebest\Webtrees\Validator::serverParams
77     * @covers \Fisharebest\Webtrees\Validator::__construct
78     */
79    public function testServerParams(): void
80    {
81        $request = $this->createStub(ServerRequestInterface::class);
82        $request
83            ->method('getServerParams')
84            ->willReturn(['param' => 'test']);
85
86        self::assertSame('test', Validator::serverParams($request)->string('param'));
87    }
88
89    /**
90     * @covers \Fisharebest\Webtrees\Validator::queryParams
91     * @covers \Fisharebest\Webtrees\Validator::__construct
92     */
93    public function testNonUTF8QueryParameterName(): void
94    {
95        $request = $this->createStub(ServerRequestInterface::class);
96        $request
97            ->method('getQueryParams')
98            ->willReturn(["\xFF" => 'test']);
99
100        $this->expectException(HttpBadRequestException::class);
101
102        Validator::queryParams($request);
103    }
104
105    /**
106     * @covers \Fisharebest\Webtrees\Validator::queryParams
107     * @covers \Fisharebest\Webtrees\Validator::__construct
108     */
109    public function testNonUTF8QueryParameterValue(): void
110    {
111        $request = $this->createStub(ServerRequestInterface::class);
112        $request
113            ->method('getQueryParams')
114            ->willReturn(['test' => "\xFF"]);
115
116        $this->expectException(HttpBadRequestException::class);
117
118        Validator::queryParams($request);
119    }
120
121    /**
122     * @covers \Fisharebest\Webtrees\Validator::array
123     * @covers \Fisharebest\Webtrees\Validator::__construct
124     */
125    public function testRequiredArrayParameter(): void
126    {
127        $request = $this->createStub(ServerRequestInterface::class);
128        $request
129            ->method('getQueryParams')
130            ->willReturn(['param' => ['test'], 'invalid' => 'not_array']);
131
132
133        self::assertSame(['test'], Validator::queryParams($request)->array('param'));
134
135        $this->expectException(HttpBadRequestException::class);
136
137        Validator::queryParams($request)->array('invalid');
138    }
139
140    /**
141     * @covers \Fisharebest\Webtrees\Validator::boolean
142     * @covers \Fisharebest\Webtrees\Validator::__construct
143     */
144    public function testRequiredBooleanParameter(): void
145    {
146        $request = $this->createStub(ServerRequestInterface::class);
147        $request
148            ->method('getQueryParams')
149            ->willReturn([
150                'a'    => '1',
151                'b'    => 'on',
152                'c'    => true,
153                'd' => '0',
154                'e' => '',
155                'f' => false,
156            ]);
157
158        self::assertSame(true, Validator::queryParams($request)->boolean('a'));
159        self::assertSame(true, Validator::queryParams($request)->boolean('b'));
160        self::assertSame(true, Validator::queryParams($request)->boolean('c'));
161        self::assertSame(false, Validator::queryParams($request)->boolean('d'));
162        self::assertSame(false, Validator::queryParams($request)->boolean('e'));
163        self::assertSame(false, Validator::queryParams($request)->boolean('f'));
164        self::assertSame(false, Validator::queryParams($request)->boolean('g', false));
165
166        $this->expectException(HttpBadRequestException::class);
167
168        Validator::queryParams($request)->boolean('h');
169    }
170
171    /**
172     * @covers \Fisharebest\Webtrees\Validator::integer
173     * @covers \Fisharebest\Webtrees\Validator::__construct
174     */
175    public function testRequiredIntegerParameter(): void
176    {
177        $request = $this->createStub(ServerRequestInterface::class);
178        $request
179            ->method('getQueryParams')
180            ->willReturn([
181                'int_type_positive'    => 42,
182                'int_type_negative'    => -42,
183                'string_type_positive' => '42',
184                'string_type_negative' => '-42',
185                'invalid'              => 'not_int',
186            ]);
187
188        self::assertSame(42, Validator::queryParams($request)->integer('int_type_positive'));
189        self::assertSame(-42, Validator::queryParams($request)->integer('int_type_negative'));
190        self::assertSame(42, Validator::queryParams($request)->integer('string_type_positive'));
191        self::assertSame(-42, Validator::queryParams($request)->integer('string_type_negative'));
192
193        $this->expectException(HttpBadRequestException::class);
194
195        Validator::queryParams($request)->integer('invalid');
196    }
197
198    /**
199     * @covers \Fisharebest\Webtrees\Validator::route
200     * @covers \Fisharebest\Webtrees\Validator::__construct
201     */
202    public function testRequiredRouteParameter(): void
203    {
204        $route = $this->createStub(Route::class);
205
206        $request = $this->createStub(ServerRequestInterface::class);
207        $request
208            ->method('getQueryParams')
209            ->willReturn([
210                'valid-route' => $route,
211                'not-route'   => '',
212            ]);
213
214        self::assertSame($route, Validator::queryParams($request)->route('valid-route'));
215
216        $this->expectException(HttpBadRequestException::class);
217
218        Validator::queryParams($request)->route('not-route');
219    }
220
221    /**
222     * @covers \Fisharebest\Webtrees\Validator::string
223     * @covers \Fisharebest\Webtrees\Validator::__construct
224     */
225    public function testRequiredStringParameter(): void
226    {
227        $request = $this->createStub(ServerRequestInterface::class);
228        $request
229            ->method('getQueryParams')
230            ->willReturn(['param' => 'test', 'invalid' => ['not_string']]);
231
232        self::assertSame('test', Validator::queryParams($request)->string('param'));
233
234        $this->expectException(HttpBadRequestException::class);
235
236        Validator::queryParams($request)->string('invalid');
237    }
238
239    /**
240     * @covers \Fisharebest\Webtrees\Validator::tree
241     * @covers \Fisharebest\Webtrees\Validator::__construct
242     */
243    public function testRequiredTreeParameter(): void
244    {
245        $tree = $this->createStub(Tree::class);
246
247        $request = $this->createStub(ServerRequestInterface::class);
248        $request
249            ->method('getQueryParams')
250            ->willReturn([
251                'valid-tree' => $tree,
252                'not-tree'   => '',
253            ]);
254
255        self::assertSame($tree, Validator::queryParams($request)->tree('valid-tree'));
256
257        $this->expectException(HttpBadRequestException::class);
258
259        Validator::queryParams($request)->tree('no-tree');
260    }
261
262    /**
263     * @covers \Fisharebest\Webtrees\Validator::treeOptional
264     * @covers \Fisharebest\Webtrees\Validator::__construct
265     */
266    public function testOptionalTreeParameter(): void
267    {
268        $tree = $this->createStub(Tree::class);
269
270        $request = $this->createStub(ServerRequestInterface::class);
271        $request
272            ->method('getQueryParams')
273            ->willReturn([
274                'valid-tree' => $tree,
275                'not-tree'   => '',
276            ]);
277
278        self::assertSame($tree, Validator::queryParams($request)->treeOptional('valid-tree'));
279        self::assertSame(null, Validator::queryParams($request)->treeOptional('missing-tree'));
280
281        $this->expectException(HttpBadRequestException::class);
282
283        Validator::queryParams($request)->treeOptional('not-tree');
284    }
285
286    /**
287     * @covers \Fisharebest\Webtrees\Validator::user
288     * @covers \Fisharebest\Webtrees\Validator::__construct
289     */
290    public function testRequiredUserParameter(): void
291    {
292        $user = $this->createStub(UserInterface::class);
293
294        $request = $this->createStub(ServerRequestInterface::class);
295        $request
296            ->method('getQueryParams')
297            ->willReturn([
298                'valid-user' => $user,
299                'not-user'   => '',
300            ]);
301
302        self::assertSame($user, Validator::queryParams($request)->user('valid-user'));
303
304        $this->expectException(HttpBadRequestException::class);
305
306        Validator::queryParams($request)->user('not-user');
307    }
308
309    /**
310     * @covers \Fisharebest\Webtrees\Validator::isBetween
311     * @covers \Fisharebest\Webtrees\Validator::__construct
312     */
313    public function testIsBetweenParameter(): void
314    {
315        $request = $this->createStub(ServerRequestInterface::class);
316        $request
317            ->method('getQueryParams')
318            ->willReturn(['param' => '42', 'invalid' => '10', 'wrongtype' => 'not_integer']);
319
320        self::assertSame(42, Validator::queryParams($request)->isBetween(40, 45)->integer('param'));
321        self::assertSame(42, Validator::queryParams($request)->isBetween(40, 45)->integer('invalid', 42));
322        self::assertSame(42, Validator::queryParams($request)->isBetween(40, 45)->integer('wrongtype', 42));
323    }
324
325    /**
326     * @covers \Fisharebest\Webtrees\Validator::isInArray
327     * @covers \Fisharebest\Webtrees\Validator::__construct
328     */
329    public function testIsInArray(): void
330    {
331        $request = $this->createStub(ServerRequestInterface::class);
332        $request
333            ->method('getQueryParams')
334            ->willReturn(['param' => 'foo']);
335
336        self::assertSame('foo', Validator::queryParams($request)->isInArray(['foo', 'bar'])->string('param'));
337
338        $this->expectException(HttpBadRequestException::class);
339
340        Validator::queryParams($request)->isInArray(['baz'])->string('param');
341    }
342
343    /**
344     * @covers \Fisharebest\Webtrees\Validator::isInArrayKeys
345     * @covers \Fisharebest\Webtrees\Validator::__construct
346     */
347    public function testIsInArrayKeys(): void
348    {
349        $request = $this->createStub(ServerRequestInterface::class);
350        $request
351            ->method('getQueryParams')
352            ->willReturn(['param' => 'foo']);
353
354        self::assertSame('foo', Validator::queryParams($request)->isInArrayKeys(['foo' => 1, 'bar' => 2])->string('param'));
355
356        $this->expectException(HttpBadRequestException::class);
357
358        Validator::queryParams($request)->isInArrayKeys(['baz' => 3])->string('param');
359    }
360
361    /**
362     * @covers \Fisharebest\Webtrees\Validator::isNotEmpty
363     * @covers \Fisharebest\Webtrees\Validator::__construct
364     */
365    public function testIsNotEmpty(): void
366    {
367        $request = $this->createStub(ServerRequestInterface::class);
368        $request
369            ->method('getQueryParams')
370            ->willReturn(['empty' => '', 'not-empty' => 'foo']);
371
372        self::assertSame('foo', Validator::queryParams($request)->isNotEmpty()->string('not-empty'));
373
374        $this->expectException(HttpBadRequestException::class);
375
376        Validator::queryParams($request)->isNotEmpty()->string('empty');
377    }
378
379    /**
380     * @covers \Fisharebest\Webtrees\Validator::isTag
381     * @covers \Fisharebest\Webtrees\Validator::__construct
382     */
383    public function testIsTagParameter(): void
384    {
385        $request = $this->createStub(ServerRequestInterface::class);
386        $request
387            ->method('getQueryParams')
388            ->willReturn(['valid' => 'BIRT', 'invalid' => '@X1@']);
389
390        self::assertSame('BIRT', Validator::queryParams($request)->isTag()->string('valid'));
391
392        $this->expectException(HttpBadRequestException::class);
393
394        Validator::queryParams($request)->isTag()->string('invalid');
395    }
396
397    /**
398     * @covers \Fisharebest\Webtrees\Validator::isXref
399     * @covers \Fisharebest\Webtrees\Validator::__construct
400     */
401    public function testIsXrefParameter(): void
402    {
403        $request = $this->createStub(ServerRequestInterface::class);
404        $request
405            ->method('getQueryParams')
406            ->willReturn(['valid' => 'X1', 'invalid' => '@X1@', 'valid-array' => ['X1'], 'invalid-array' => ['@X1@']]);
407
408        self::assertSame('X1', Validator::queryParams($request)->isXref()->string('valid'));
409        self::assertSame(['X1'], Validator::queryParams($request)->isXref()->array('valid-array'));
410        self::assertSame([], Validator::queryParams($request)->isXref()->array('invalid-array'));
411
412        $this->expectException(HttpBadRequestException::class);
413
414        Validator::queryParams($request)->isXref()->string('invalid');
415    }
416
417    /**
418     * @covers \Fisharebest\Webtrees\Validator::isLocalUrl
419     * @covers \Fisharebest\Webtrees\Validator::__construct
420     */
421    public function testIsLocalUrlParameter(): void
422    {
423        $request = $this->createStub(ServerRequestInterface::class);
424        $request
425            ->method('getAttribute')
426            ->with('base_url')->willReturn('http://example.local/wt');
427        $request
428            ->method('getQueryParams')
429            ->willReturn(['param' => 'http://example.local/wt/page', 'noscheme' => '//example.local/wt/page']);
430
431
432        self::assertSame('http://example.local/wt/page', Validator::queryParams($request)->isLocalUrl()->string('param'));
433        self::assertSame('//example.local/wt/page', Validator::queryParams($request)->isLocalUrl()->string('noscheme'));
434    }
435
436    /**
437     * @covers \Fisharebest\Webtrees\Validator::isLocalUrl
438     * @covers \Fisharebest\Webtrees\Validator::__construct
439     */
440    public function testIsLocalUrlParameterWrongScheme(): void
441    {
442        $request = $this->createStub(ServerRequestInterface::class);
443        $request
444            ->method('getAttribute')
445            ->with('base_url')
446            ->willReturn('http://example.local/wt');
447        $request
448            ->method('getQueryParams')
449            ->willReturn(['https' => 'https://example.local/wt/page']);
450
451        $this->expectException(HttpBadRequestException::class);
452
453        Validator::queryParams($request)->isLocalUrl()->string('https');
454    }
455
456    /**
457     * @covers \Fisharebest\Webtrees\Validator::isLocalUrl
458     * @covers \Fisharebest\Webtrees\Validator::__construct
459     */
460    public function testIsLocalUrlParameterWrongDomain(): void
461    {
462        $request = $this->createStub(ServerRequestInterface::class);
463        $request
464            ->method('getAttribute')
465            ->with('base_url')
466            ->willReturn('http://example.local/wt');
467        $request
468            ->method('getQueryParams')
469            ->willReturn(['invalid' => 'http://example.com/wt/page']);
470
471        $this->expectException(HttpBadRequestException::class);
472
473        Validator::queryParams($request)->isLocalUrl()->string('invalid');
474    }
475
476    /**
477     * @covers \Fisharebest\Webtrees\Validator::isLocalUrl
478     * @covers \Fisharebest\Webtrees\Validator::__construct
479     */
480    public function testIsLocalUrlParameterWrongType(): void
481    {
482        $request = $this->createStub(ServerRequestInterface::class);
483        $request
484            ->method('getQueryParams')
485            ->willReturn(['wrongtype' => ['42']]);
486
487        $this->expectException(HttpBadRequestException::class);
488
489        Validator::queryParams($request)->isLocalUrl()->isLocalUrl()->string('wrongtype');
490    }
491}
492