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