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