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