xref: /webtrees/tests/app/ValidatorTest.php (revision 202c018b592d5a516e4a465dc6dc515f3be37399)
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 PHPUnit\Framework\Attributes\CoversClass;
26use Psr\Http\Message\ServerRequestInterface;
27
28
29#[CoversClass(Validator::class)]
30class ValidatorTest extends TestCase
31{
32    public function testAttributes(): void
33    {
34        $request = $this->createStub(ServerRequestInterface::class);
35        $request
36            ->method('getAttributes')
37            ->willReturn(['param' => 'test']);
38
39        self::assertSame('test', Validator::attributes($request)->string('param'));
40    }
41
42    public function testParsedBody(): void
43    {
44        $request = $this->createStub(ServerRequestInterface::class);
45        $request
46            ->method('getParsedBody')
47            ->willReturn(['param' => 'test']);
48
49        self::assertSame('test', Validator::parsedBody($request)->string('param'));
50    }
51
52    public function testQueryParams(): void
53    {
54        $request = $this->createStub(ServerRequestInterface::class);
55        $request
56            ->method('getQueryParams')
57            ->willReturn(['param' => 'test']);
58
59        self::assertSame('test', Validator::queryParams($request)->string('param'));
60    }
61
62    public function testServerParams(): void
63    {
64        $request = $this->createStub(ServerRequestInterface::class);
65        $request
66            ->method('getServerParams')
67            ->willReturn(['param' => 'test']);
68
69        self::assertSame('test', Validator::serverParams($request)->string('param'));
70    }
71
72    public function testNonUTF8QueryParameterName(): void
73    {
74        $request = $this->createStub(ServerRequestInterface::class);
75        $request
76            ->method('getQueryParams')
77            ->willReturn(["\xFF" => 'test']);
78
79        $this->expectException(HttpBadRequestException::class);
80
81        Validator::queryParams($request);
82    }
83
84    public function testNonUTF8QueryParameterValue(): void
85    {
86        $request = $this->createStub(ServerRequestInterface::class);
87        $request
88            ->method('getQueryParams')
89            ->willReturn(['test' => "\xFF"]);
90
91        $this->expectException(HttpBadRequestException::class);
92
93        Validator::queryParams($request);
94    }
95
96    public function testRequiredArrayParameter(): void
97    {
98        $request = $this->createStub(ServerRequestInterface::class);
99        $request
100            ->method('getQueryParams')
101            ->willReturn(['param' => ['test'], 'invalid' => 'not_array']);
102
103
104        self::assertSame(['test'], Validator::queryParams($request)->array('param'));
105
106        $this->expectException(HttpBadRequestException::class);
107
108        Validator::queryParams($request)->array('invalid');
109    }
110
111    public function testRequiredBooleanParameter(): void
112    {
113        $request = $this->createStub(ServerRequestInterface::class);
114        $request
115            ->method('getQueryParams')
116            ->willReturn([
117                'a'    => '1',
118                'b'    => 'on',
119                'c'    => true,
120                'd' => '0',
121                'e' => '',
122                'f' => false,
123            ]);
124
125        self::assertSame(true, Validator::queryParams($request)->boolean('a'));
126        self::assertSame(true, Validator::queryParams($request)->boolean('b'));
127        self::assertSame(true, Validator::queryParams($request)->boolean('c'));
128        self::assertSame(false, Validator::queryParams($request)->boolean('d'));
129        self::assertSame(false, Validator::queryParams($request)->boolean('e'));
130        self::assertSame(false, Validator::queryParams($request)->boolean('f'));
131        self::assertSame(false, Validator::queryParams($request)->boolean('g', false));
132
133        $this->expectException(HttpBadRequestException::class);
134
135        Validator::queryParams($request)->boolean('h');
136    }
137
138    public function testRequiredIntegerParameter(): void
139    {
140        $request = $this->createStub(ServerRequestInterface::class);
141        $request
142            ->method('getQueryParams')
143            ->willReturn([
144                'int_type_positive'    => 42,
145                'int_type_negative'    => -42,
146                'string_type_positive' => '42',
147                'string_type_negative' => '-42',
148                'invalid'              => 'not_int',
149            ]);
150
151        self::assertSame(42, Validator::queryParams($request)->integer('int_type_positive'));
152        self::assertSame(-42, Validator::queryParams($request)->integer('int_type_negative'));
153        self::assertSame(42, Validator::queryParams($request)->integer('string_type_positive'));
154        self::assertSame(-42, Validator::queryParams($request)->integer('string_type_negative'));
155
156        $this->expectException(HttpBadRequestException::class);
157
158        Validator::queryParams($request)->integer('invalid');
159    }
160
161    public function testRequiredRouteParameter(): void
162    {
163        $route = $this->createStub(Route::class);
164
165        $request = $this->createStub(ServerRequestInterface::class);
166        $request
167            ->method('getQueryParams')
168            ->willReturn([
169                'valid-route' => $route,
170                'not-route'   => '',
171            ]);
172
173        self::assertSame($route, Validator::queryParams($request)->route('valid-route'));
174
175        $this->expectException(HttpBadRequestException::class);
176
177        Validator::queryParams($request)->route('not-route');
178    }
179
180    public function testRequiredStringParameter(): void
181    {
182        $request = $this->createStub(ServerRequestInterface::class);
183        $request
184            ->method('getQueryParams')
185            ->willReturn(['param' => 'test', 'invalid' => ['not_string']]);
186
187        self::assertSame('test', Validator::queryParams($request)->string('param'));
188
189        $this->expectException(HttpBadRequestException::class);
190
191        Validator::queryParams($request)->string('invalid');
192    }
193
194    public function testRequiredTreeParameter(): void
195    {
196        $tree = $this->createStub(Tree::class);
197
198        $request = $this->createStub(ServerRequestInterface::class);
199        $request
200            ->method('getQueryParams')
201            ->willReturn([
202                'valid-tree' => $tree,
203                'not-tree'   => '',
204            ]);
205
206        self::assertSame($tree, Validator::queryParams($request)->tree('valid-tree'));
207
208        $this->expectException(HttpBadRequestException::class);
209
210        Validator::queryParams($request)->tree('no-tree');
211    }
212
213    public function testOptionalTreeParameter(): void
214    {
215        $tree = $this->createStub(Tree::class);
216
217        $request = $this->createStub(ServerRequestInterface::class);
218        $request
219            ->method('getQueryParams')
220            ->willReturn([
221                'valid-tree' => $tree,
222                'not-tree'   => '',
223            ]);
224
225        self::assertSame($tree, Validator::queryParams($request)->treeOptional('valid-tree'));
226        self::assertSame(null, Validator::queryParams($request)->treeOptional('missing-tree'));
227
228        $this->expectException(HttpBadRequestException::class);
229
230        Validator::queryParams($request)->treeOptional('not-tree');
231    }
232
233    public function testRequiredUserParameter(): void
234    {
235        $user = $this->createStub(UserInterface::class);
236
237        $request = $this->createStub(ServerRequestInterface::class);
238        $request
239            ->method('getQueryParams')
240            ->willReturn([
241                'valid-user' => $user,
242                'not-user'   => '',
243            ]);
244
245        self::assertSame($user, Validator::queryParams($request)->user('valid-user'));
246
247        $this->expectException(HttpBadRequestException::class);
248
249        Validator::queryParams($request)->user('not-user');
250    }
251
252    public function testIsBetweenParameter(): void
253    {
254        $request = $this->createStub(ServerRequestInterface::class);
255        $request
256            ->method('getQueryParams')
257            ->willReturn(['param' => '42', 'invalid' => '10', 'wrongtype' => 'not_integer']);
258
259        self::assertSame(42, Validator::queryParams($request)->isBetween(40, 45)->integer('param'));
260        self::assertSame(42, Validator::queryParams($request)->isBetween(40, 45)->integer('invalid', 42));
261        self::assertSame(42, Validator::queryParams($request)->isBetween(40, 45)->integer('wrongtype', 42));
262    }
263
264    public function testIsInArray(): void
265    {
266        $request = $this->createStub(ServerRequestInterface::class);
267        $request
268            ->method('getQueryParams')
269            ->willReturn(['param' => 'foo']);
270
271        self::assertSame('foo', Validator::queryParams($request)->isInArray(['foo', 'bar'])->string('param'));
272
273        $this->expectException(HttpBadRequestException::class);
274
275        Validator::queryParams($request)->isInArray(['baz'])->string('param');
276    }
277
278    public function testIsInArrayKeys(): void
279    {
280        $request = $this->createStub(ServerRequestInterface::class);
281        $request
282            ->method('getQueryParams')
283            ->willReturn(['param' => 'foo']);
284
285        self::assertSame('foo', Validator::queryParams($request)->isInArrayKeys(['foo' => 1, 'bar' => 2])->string('param'));
286
287        $this->expectException(HttpBadRequestException::class);
288
289        Validator::queryParams($request)->isInArrayKeys(['baz' => 3])->string('param');
290    }
291
292    public function testIsNotEmpty(): void
293    {
294        $request = $this->createStub(ServerRequestInterface::class);
295        $request
296            ->method('getQueryParams')
297            ->willReturn(['empty' => '', 'not-empty' => 'foo']);
298
299        self::assertSame('foo', Validator::queryParams($request)->isNotEmpty()->string('not-empty'));
300
301        $this->expectException(HttpBadRequestException::class);
302
303        Validator::queryParams($request)->isNotEmpty()->string('empty');
304    }
305
306    public function testIsTagParameter(): void
307    {
308        $request = $this->createStub(ServerRequestInterface::class);
309        $request
310            ->method('getQueryParams')
311            ->willReturn(['valid' => 'BIRT', 'invalid' => '@X1@']);
312
313        self::assertSame('BIRT', Validator::queryParams($request)->isTag()->string('valid'));
314
315        $this->expectException(HttpBadRequestException::class);
316
317        Validator::queryParams($request)->isTag()->string('invalid');
318    }
319
320    public function testIsXrefParameter(): void
321    {
322        $request = $this->createStub(ServerRequestInterface::class);
323        $request
324            ->method('getQueryParams')
325            ->willReturn(['valid' => 'X1', 'invalid' => '@X1@', 'valid-array' => ['X1'], 'invalid-array' => ['@X1@']]);
326
327        self::assertSame('X1', Validator::queryParams($request)->isXref()->string('valid'));
328        self::assertSame(['X1'], Validator::queryParams($request)->isXref()->array('valid-array'));
329        self::assertSame([], Validator::queryParams($request)->isXref()->array('invalid-array'));
330
331        $this->expectException(HttpBadRequestException::class);
332
333        Validator::queryParams($request)->isXref()->string('invalid');
334    }
335
336    public function testIsLocalUrlParameter(): void
337    {
338        $request = $this->createStub(ServerRequestInterface::class);
339        $request
340            ->method('getAttribute')
341            ->with('base_url')->willReturn('http://example.local/wt');
342        $request
343            ->method('getQueryParams')
344            ->willReturn(['param' => 'http://example.local/wt/page', 'noscheme' => '//example.local/wt/page']);
345
346
347        self::assertSame('http://example.local/wt/page', Validator::queryParams($request)->isLocalUrl()->string('param'));
348        self::assertSame('//example.local/wt/page', Validator::queryParams($request)->isLocalUrl()->string('noscheme'));
349    }
350
351    public function testIsLocalUrlParameterWrongScheme(): void
352    {
353        $request = $this->createStub(ServerRequestInterface::class);
354        $request
355            ->method('getAttribute')
356            ->with('base_url')
357            ->willReturn('http://example.local/wt');
358        $request
359            ->method('getQueryParams')
360            ->willReturn(['https' => 'https://example.local/wt/page']);
361
362        $this->expectException(HttpBadRequestException::class);
363
364        Validator::queryParams($request)->isLocalUrl()->string('https');
365    }
366
367    public function testIsLocalUrlParameterWrongDomain(): void
368    {
369        $request = $this->createStub(ServerRequestInterface::class);
370        $request
371            ->method('getAttribute')
372            ->with('base_url')
373            ->willReturn('http://example.local/wt');
374        $request
375            ->method('getQueryParams')
376            ->willReturn(['invalid' => 'http://example.com/wt/page']);
377
378        $this->expectException(HttpBadRequestException::class);
379
380        Validator::queryParams($request)->isLocalUrl()->string('invalid');
381    }
382
383    public function testIsLocalUrlParameterWrongType(): void
384    {
385        $request = $this->createStub(ServerRequestInterface::class);
386        $request
387            ->method('getQueryParams')
388            ->willReturn(['wrongtype' => ['42']]);
389
390        $this->expectException(HttpBadRequestException::class);
391
392        Validator::queryParams($request)->isLocalUrl()->isLocalUrl()->string('wrongtype');
393    }
394}
395