Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
75.00% |
195 / 260 |
|
88.89% |
8 / 9 |
CRAP | |
0.00% |
0 / 1 |
| version_helper_remote_test | |
75.00% |
195 / 260 |
|
88.89% |
8 / 9 |
12.89 | |
0.00% |
0 / 1 |
| setUp | |
100.00% |
46 / 46 |
|
100.00% |
1 / 1 |
1 | |||
| provider_get_versions | |
0.00% |
0 / 65 |
|
0.00% |
0 / 1 |
2 | |||
| test_get_versions | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
3 | |||
| test_version_phpbb_com | |
100.00% |
51 / 51 |
|
100.00% |
1 / 1 |
1 | |||
| test_file_downloader_file_not_found | |
100.00% |
17 / 17 |
|
100.00% |
1 / 1 |
1 | |||
| test_file_downloader_exception_not_found | |
100.00% |
19 / 19 |
|
100.00% |
1 / 1 |
1 | |||
| test_file_downloader_exception_moved | |
100.00% |
19 / 19 |
|
100.00% |
1 / 1 |
1 | |||
| test_file_downloader_exception_timeout | |
100.00% |
18 / 18 |
|
100.00% |
1 / 1 |
1 | |||
| test_file_downloader_exception_other | |
100.00% |
17 / 17 |
|
100.00% |
1 / 1 |
1 | |||
| 1 | <?php |
| 2 | |
| 3 | use GuzzleHttp\Exception\RequestException; |
| 4 | |
| 5 | /** |
| 6 | * |
| 7 | * This file is part of the phpBB Forum Software package. |
| 8 | * |
| 9 | * @copyright (c) phpBB Limited <https://www.phpbb.com> |
| 10 | * @license GNU General Public License, version 2 (GPL-2.0) |
| 11 | * |
| 12 | * For full copyright and license information, please see |
| 13 | * the docs/CREDITS.txt file. |
| 14 | * |
| 15 | */ |
| 16 | |
| 17 | class version_helper_remote_test extends \phpbb_test_case |
| 18 | { |
| 19 | protected $file_downloader; |
| 20 | protected $cache; |
| 21 | protected $version_helper; |
| 22 | protected $user; |
| 23 | |
| 24 | // Guzzle mock data |
| 25 | protected $guzzle_status = 200; // Default to 200 status |
| 26 | protected $guzzle_data; |
| 27 | protected $guzzle_mock; |
| 28 | |
| 29 | protected function setUp(): void |
| 30 | { |
| 31 | parent::setUp(); |
| 32 | |
| 33 | global $phpbb_root_path, $phpEx; |
| 34 | |
| 35 | include_once($phpbb_root_path . 'includes/functions.' . $phpEx); |
| 36 | |
| 37 | $config = new \phpbb\config\config(array( |
| 38 | 'version' => '3.1.0', |
| 39 | )); |
| 40 | $container = new \phpbb_mock_container_builder(); |
| 41 | $phpbb_dispatcher = new phpbb_mock_event_dispatcher(); |
| 42 | $db = new \phpbb\db\driver\factory($container); |
| 43 | $this->cache = $this->getMockBuilder('\phpbb\cache\service') |
| 44 | ->addMethods(array('get')) |
| 45 | ->setConstructorArgs(array(new \phpbb\cache\driver\dummy(), $config, $db, $phpbb_dispatcher, '../../', 'php')) |
| 46 | ->getMock(); |
| 47 | |
| 48 | $this->cache->expects($this->any()) |
| 49 | ->method('get') |
| 50 | ->withAnyParameters() |
| 51 | ->will($this->returnValue(false)); |
| 52 | |
| 53 | $this->guzzle_mock = $this->getMockBuilder('\GuzzleHttp\Client') |
| 54 | ->addMethods(['set_data']) |
| 55 | ->onlyMethods(['request']) |
| 56 | ->getMock(); |
| 57 | $this->guzzle_mock->method('set_data') |
| 58 | ->will($this->returnCallback(function($data) |
| 59 | { |
| 60 | $this->guzzle_data = $data; |
| 61 | } |
| 62 | )); |
| 63 | $this->guzzle_mock->method('request') |
| 64 | ->will($this->returnCallback(function() |
| 65 | { |
| 66 | return new \GuzzleHttp\Psr7\Response($this->guzzle_status, [], $this->guzzle_data); |
| 67 | } |
| 68 | )); |
| 69 | |
| 70 | $this->file_downloader = $this->getMockBuilder('\phpbb\file_downloader') |
| 71 | ->onlyMethods(['create_client']) |
| 72 | ->getMock(); |
| 73 | $this->file_downloader->method('create_client') |
| 74 | ->will($this->returnValue($this->guzzle_mock)); |
| 75 | |
| 76 | $lang_loader = new \phpbb\language\language_file_loader($phpbb_root_path, $phpEx); |
| 77 | |
| 78 | $this->version_helper = new \phpbb\version_helper( |
| 79 | $this->cache, |
| 80 | $config, |
| 81 | $this->file_downloader |
| 82 | ); |
| 83 | $this->user = new \phpbb\user(new \phpbb\language\language($lang_loader), '\phpbb\datetime'); |
| 84 | $this->user->add_lang('acp/common'); |
| 85 | } |
| 86 | |
| 87 | public static function provider_get_versions() |
| 88 | { |
| 89 | return array( |
| 90 | array('', false, '', 'VERSIONCHECK_FAIL'), |
| 91 | array('foobar', false, '', 'VERSIONCHECK_FAIL'), |
| 92 | array('{ |
| 93 | "stable": { |
| 94 | "1.0": { |
| 95 | "current": "1.0.1", |
| 96 | "download": "https://www.phpbb.com/customise/db/download/104136", |
| 97 | "announcement": "https://www.phpbb.com/customise/db/extension/boardrules/", |
| 98 | "eol": null, |
| 99 | "security": false |
| 100 | } |
| 101 | } |
| 102 | }', true, array ( |
| 103 | 'stable' => array ( |
| 104 | '1.0' => array ( |
| 105 | 'current' => '1.0.1', |
| 106 | 'download' => 'https://www.phpbb.com/customise/db/download/104136', |
| 107 | 'announcement' => 'https://www.phpbb.com/customise/db/extension/boardrules/', |
| 108 | 'eol' => NULL, |
| 109 | 'security' => false, |
| 110 | ), |
| 111 | ), |
| 112 | 'unstable' => array ( |
| 113 | '1.0' => array ( |
| 114 | 'current' => '1.0.1', |
| 115 | 'download' => 'https://www.phpbb.com/customise/db/download/104136', |
| 116 | 'announcement' => 'https://www.phpbb.com/customise/db/extension/boardrules/', |
| 117 | 'eol' => NULL, |
| 118 | 'security' => false, |
| 119 | ), |
| 120 | ), |
| 121 | )), |
| 122 | array('{ |
| 123 | "foobar": { |
| 124 | "1.0": { |
| 125 | "current": "1.0.1", |
| 126 | "download": "https://www.phpbb.com/customise/db/download/104136", |
| 127 | "announcement": "https://www.phpbb.com/customise/db/extension/boardrules/", |
| 128 | "eol": null, |
| 129 | "security": false |
| 130 | } |
| 131 | } |
| 132 | }', false, '', 'VERSIONCHECK_FAIL'), |
| 133 | array('{ |
| 134 | "stable": { |
| 135 | "1.0": { |
| 136 | "current": "1.0.1<script>alert(\'foo\');</script>", |
| 137 | "download": "https://www.phpbb.com/customise/db/download/104136<script>alert(\'foo\');</script>", |
| 138 | "announcement": "https://www.phpbb.com/customise/db/extension/boardrules/<script>alert(\'foo\');</script>", |
| 139 | "eol": "<script>alert(\'foo\');</script>", |
| 140 | "security": "<script>alert(\'foo\');</script>" |
| 141 | } |
| 142 | } |
| 143 | }', false, null, 'VERSIONCHECK_INVALID_VERSION'), |
| 144 | array('{ |
| 145 | "unstable": { |
| 146 | "1.0": { |
| 147 | "current": "1.0.1<script>alert(\'foo\');</script>", |
| 148 | "download": "https://www.phpbb.com/customise/db/download/104136<script>alert(\'foo\');</script>", |
| 149 | "announcement": "https://www.phpbb.com/customise/db/extension/boardrules/<script>alert(\'foo\');</script>", |
| 150 | "eol": "<script>alert(\'foo\');</script>", |
| 151 | "security": "<script>alert(\'foo\');</script>" |
| 152 | } |
| 153 | } |
| 154 | }', false, null, 'VERSIONCHECK_INVALID_VERSION'), |
| 155 | array('{ |
| 156 | "unstable": { |
| 157 | "1.0<script>alert(\'foo\');</script>": { |
| 158 | "current": "1.0.1", |
| 159 | "download": "https://www.phpbb.com/customise/db/download/104136", |
| 160 | "announcement": "https://www.phpbb.com/customise/db/extension/boardrules/", |
| 161 | "eol": "", |
| 162 | "security": "" |
| 163 | } |
| 164 | } |
| 165 | }', false, array('stable' => array(), 'unstable' => array()), 'VERSIONCHECK_INVALID_VERSION'), |
| 166 | array('{ |
| 167 | "\"\n<script>alert(\'foo\');</script>\n": "test", |
| 168 | "stable": { |
| 169 | "1.0": { |
| 170 | "current": "1.0.1", |
| 171 | "download": "https://www.phpbb.com/customise/db/download/104136", |
| 172 | "announcement": "https://www.phpbb.com/customise/db/extension/boardrules/", |
| 173 | "eol": null, |
| 174 | "security": false |
| 175 | } |
| 176 | } |
| 177 | }', true, array ( |
| 178 | 'stable' => array ( |
| 179 | '1.0' => array ( |
| 180 | 'current' => '1.0.1', |
| 181 | 'download' => 'https://www.phpbb.com/customise/db/download/104136', |
| 182 | 'announcement' => 'https://www.phpbb.com/customise/db/extension/boardrules/', |
| 183 | 'eol' => NULL, |
| 184 | 'security' => false, |
| 185 | ), |
| 186 | ), |
| 187 | 'unstable' => array ( |
| 188 | '1.0' => array ( |
| 189 | 'current' => '1.0.1', |
| 190 | 'download' => 'https://www.phpbb.com/customise/db/download/104136', |
| 191 | 'announcement' => 'https://www.phpbb.com/customise/db/extension/boardrules/', |
| 192 | 'eol' => NULL, |
| 193 | 'security' => false, |
| 194 | ), |
| 195 | ), |
| 196 | )), |
| 197 | array('{ |
| 198 | "unstable": { |
| 199 | "1.0": { |
| 200 | "current": "1.0.1", |
| 201 | "download": "https://www.phpbb.com/customise/db/download/104136", |
| 202 | "announcement": "https://www.phpbb.com/customise/db/extension/boardrules/", |
| 203 | "eol": null, |
| 204 | "security": false, |
| 205 | "foobar": "<script>alert(\'test\');<script>" |
| 206 | } |
| 207 | } |
| 208 | }', true, array('stable' => array(), 'unstable' => array('1.0' => array( |
| 209 | 'current' => '1.0.1', |
| 210 | 'download' => 'https://www.phpbb.com/customise/db/download/104136', |
| 211 | 'announcement' => 'https://www.phpbb.com/customise/db/extension/boardrules/', |
| 212 | 'security' => false, |
| 213 | ))), 'VERSIONCHECK_INVALID_ENTRY'), |
| 214 | array('{ |
| 215 | "unstable": { |
| 216 | "1.0": { |
| 217 | "current<script>alert(\'foo\');</script>": "1.0.1", |
| 218 | "download2": "https://www.phpbb.com/customise/db/download/104136", |
| 219 | "bannouncement": "https://www.phpbb.com/customise/db/extension/boardrules/", |
| 220 | "eol": null, |
| 221 | "security": false, |
| 222 | "foobar": "<script>alert(\'test\');<script>" |
| 223 | } |
| 224 | } |
| 225 | }', true, array('stable' => array(), 'unstable' => array('1.0' => array( |
| 226 | 'security' => false, |
| 227 | ))), 'VERSIONCHECK_INVALID_ENTRY'), |
| 228 | ); |
| 229 | } |
| 230 | |
| 231 | /** |
| 232 | * @dataProvider provider_get_versions |
| 233 | */ |
| 234 | public function test_get_versions($input, $valid_data, $expected_return = '', $expected_exception = '') |
| 235 | { |
| 236 | $this->guzzle_mock->set_data($input); |
| 237 | |
| 238 | // version_helper->get_versions() doesn't return a value on VERSIONCHECK_FAIL but only throws exception |
| 239 | // so the $return is undefined. Define it here |
| 240 | $return = false; |
| 241 | |
| 242 | if (!$valid_data) |
| 243 | { |
| 244 | try { |
| 245 | $return = $this->version_helper->get_versions(); |
| 246 | } catch (\phpbb\exception\runtime_exception $e) { |
| 247 | $this->assertEquals($expected_exception, $e->getMessage()); |
| 248 | } |
| 249 | } |
| 250 | else |
| 251 | { |
| 252 | $return = $this->version_helper->get_versions(); |
| 253 | } |
| 254 | |
| 255 | $this->assertEquals($expected_return, $return); |
| 256 | } |
| 257 | |
| 258 | public function test_version_phpbb_com() |
| 259 | { |
| 260 | $guzzle_mock = $this->getMockBuilder('\GuzzleHttp\Client') |
| 261 | ->onlyMethods(['request']) |
| 262 | ->getMock(); |
| 263 | |
| 264 | $guzzle_mock->method('request') |
| 265 | ->will($this->returnCallback(function() |
| 266 | { |
| 267 | return new \GuzzleHttp\Psr7\Response(200, [], file_get_contents(__DIR__ . '/fixture/30x.txt')); |
| 268 | } |
| 269 | )); |
| 270 | |
| 271 | $file_downloader = $this->getMockBuilder(\phpbb\file_downloader::class) |
| 272 | ->onlyMethods(['create_client']) |
| 273 | ->getMock(); |
| 274 | |
| 275 | $file_downloader->method('create_client') |
| 276 | ->willReturn($guzzle_mock); |
| 277 | |
| 278 | $hostname = 'version.phpbb.com'; |
| 279 | |
| 280 | $file = $file_downloader->get($hostname, '/phpbb', '30x.txt'); |
| 281 | $errstr = $file_downloader->get_error_string(); |
| 282 | $errno = $file_downloader->get_error_number(); |
| 283 | |
| 284 | $this->assertNotEquals( |
| 285 | 0, |
| 286 | strlen($file), |
| 287 | 'Failed asserting that the response is not empty.' |
| 288 | ); |
| 289 | |
| 290 | $this->assertSame( |
| 291 | '', |
| 292 | $errstr, |
| 293 | 'Failed asserting that the error string is empty.' |
| 294 | ); |
| 295 | |
| 296 | $this->assertSame( |
| 297 | 0, |
| 298 | $errno, |
| 299 | 'Failed asserting that the error number is 0 (i.e. no error occurred).' |
| 300 | ); |
| 301 | |
| 302 | $lines = explode("\n", $file); |
| 303 | |
| 304 | $this->assertGreaterThanOrEqual( |
| 305 | 2, |
| 306 | count($lines), |
| 307 | 'Failed asserting that the version file has at least two lines.' |
| 308 | ); |
| 309 | |
| 310 | $this->assertStringStartsWith( |
| 311 | '3.', |
| 312 | $lines[0], |
| 313 | "Failed asserting that the first line of the version file starts with '3.'" |
| 314 | ); |
| 315 | |
| 316 | $this->assertNotSame( |
| 317 | false, |
| 318 | filter_var($lines[1], FILTER_VALIDATE_URL), |
| 319 | 'Failed asserting that the second line of the version file is a valid URL.' |
| 320 | ); |
| 321 | |
| 322 | $this->assertStringContainsString('http', $lines[1]); |
| 323 | $this->assertStringContainsString('phpbb.com', $lines[1], '', true); |
| 324 | } |
| 325 | |
| 326 | public function test_file_downloader_file_not_found() |
| 327 | { |
| 328 | $this->guzzle_mock = $this->getMockBuilder('\GuzzleHttp\Client') |
| 329 | ->onlyMethods(['request']) |
| 330 | ->getMock(); |
| 331 | |
| 332 | $this->guzzle_mock->method('request') |
| 333 | ->will($this->returnCallback(function() |
| 334 | { |
| 335 | return new \GuzzleHttp\Psr7\Response(404, [], ''); |
| 336 | } |
| 337 | )); |
| 338 | |
| 339 | $file_downloader = $this->getMockBuilder(\phpbb\file_downloader::class) |
| 340 | ->onlyMethods(['create_client']) |
| 341 | ->getMock(); |
| 342 | |
| 343 | $file_downloader->method('create_client') |
| 344 | ->willReturn($this->guzzle_mock); |
| 345 | |
| 346 | $this->expectException(\phpbb\exception\runtime_exception::class); |
| 347 | $this->expectExceptionMessage('FILE_NOT_FOUND'); |
| 348 | |
| 349 | $file_downloader->get('foo.com', 'bar', 'foo.txt'); |
| 350 | } |
| 351 | |
| 352 | public function test_file_downloader_exception_not_found() |
| 353 | { |
| 354 | $this->guzzle_mock = $this->getMockBuilder('\GuzzleHttp\Client') |
| 355 | ->onlyMethods(['request']) |
| 356 | ->getMock(); |
| 357 | |
| 358 | $this->guzzle_mock->method('request') |
| 359 | ->will($this->returnCallback(function($method, $uri) |
| 360 | { |
| 361 | $request = new \GuzzleHttp\Psr7\Request('GET', $uri); |
| 362 | $response = new \GuzzleHttp\Psr7\Response(404, [], ''); |
| 363 | throw new RequestException('FILE_NOT_FOUND', $request, $response); |
| 364 | } |
| 365 | )); |
| 366 | |
| 367 | $file_downloader = $this->getMockBuilder(\phpbb\file_downloader::class) |
| 368 | ->onlyMethods(['create_client']) |
| 369 | ->getMock(); |
| 370 | |
| 371 | $file_downloader->method('create_client') |
| 372 | ->willReturn($this->guzzle_mock); |
| 373 | |
| 374 | $this->expectException(\phpbb\exception\runtime_exception::class); |
| 375 | $this->expectExceptionMessage('FILE_NOT_FOUND'); |
| 376 | |
| 377 | $file_downloader->get('foo.com', 'bar', 'foo.txt'); |
| 378 | } |
| 379 | |
| 380 | public function test_file_downloader_exception_moved() |
| 381 | { |
| 382 | $this->guzzle_mock = $this->getMockBuilder('\GuzzleHttp\Client') |
| 383 | ->onlyMethods(['request']) |
| 384 | ->getMock(); |
| 385 | |
| 386 | $this->guzzle_mock->method('request') |
| 387 | ->will($this->returnCallback(function($method, $uri) |
| 388 | { |
| 389 | $request = new \GuzzleHttp\Psr7\Request('GET', $uri); |
| 390 | $response = new \GuzzleHttp\Psr7\Response(302, [], ''); |
| 391 | throw new RequestException('FILE_MOVED', $request, $response); |
| 392 | } |
| 393 | )); |
| 394 | |
| 395 | $file_downloader = $this->getMockBuilder(\phpbb\file_downloader::class) |
| 396 | ->onlyMethods(['create_client']) |
| 397 | ->getMock(); |
| 398 | |
| 399 | $file_downloader->method('create_client') |
| 400 | ->willReturn($this->guzzle_mock); |
| 401 | |
| 402 | $this->assertFalse($file_downloader->get('foo.com', 'bar', 'foo.txt')); |
| 403 | $this->assertEquals(302, $file_downloader->get_error_number()); |
| 404 | $this->assertEquals('FILE_MOVED', $file_downloader->get_error_string()); |
| 405 | } |
| 406 | |
| 407 | public function test_file_downloader_exception_timeout() |
| 408 | { |
| 409 | $this->guzzle_mock = $this->getMockBuilder('\GuzzleHttp\Client') |
| 410 | ->onlyMethods(['request']) |
| 411 | ->getMock(); |
| 412 | |
| 413 | $this->guzzle_mock->method('request') |
| 414 | ->will($this->returnCallback(function($method, $uri) |
| 415 | { |
| 416 | $request = new \GuzzleHttp\Psr7\Request('GET', $uri); |
| 417 | throw new RequestException('FILE_NOT_FOUND', $request); |
| 418 | } |
| 419 | )); |
| 420 | |
| 421 | $file_downloader = $this->getMockBuilder(\phpbb\file_downloader::class) |
| 422 | ->onlyMethods(['create_client']) |
| 423 | ->getMock(); |
| 424 | |
| 425 | $file_downloader->method('create_client') |
| 426 | ->willReturn($this->guzzle_mock); |
| 427 | |
| 428 | $this->expectException(\phpbb\exception\runtime_exception::class); |
| 429 | $this->expectExceptionMessage('FSOCK_TIMEOUT'); |
| 430 | |
| 431 | $file_downloader->get('foo.com', 'bar', 'foo.txt'); |
| 432 | } |
| 433 | |
| 434 | public function test_file_downloader_exception_other() |
| 435 | { |
| 436 | $this->guzzle_mock = $this->getMockBuilder('\GuzzleHttp\Client') |
| 437 | ->onlyMethods(['request']) |
| 438 | ->getMock(); |
| 439 | |
| 440 | $this->guzzle_mock->method('request') |
| 441 | ->will($this->returnCallback(function($method, $uri) |
| 442 | { |
| 443 | throw new \RuntimeException('FSOCK_NOT_SUPPORTED'); |
| 444 | } |
| 445 | )); |
| 446 | |
| 447 | $file_downloader = $this->getMockBuilder(\phpbb\file_downloader::class) |
| 448 | ->onlyMethods(['create_client']) |
| 449 | ->getMock(); |
| 450 | |
| 451 | $file_downloader->method('create_client') |
| 452 | ->willReturn($this->guzzle_mock); |
| 453 | |
| 454 | $this->expectException(\phpbb\exception\runtime_exception::class); |
| 455 | $this->expectExceptionMessage('FSOCK_DISABLED'); |
| 456 | |
| 457 | $file_downloader->get('foo.com', 'bar', 'foo.txt'); |
| 458 | } |
| 459 | } |