Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
75.00% covered (warning)
75.00%
195 / 260
88.89% covered (warning)
88.89%
8 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
version_helper_remote_test
75.00% covered (warning)
75.00%
195 / 260
88.89% covered (warning)
88.89%
8 / 9
12.89
0.00% covered (danger)
0.00%
0 / 1
 setUp
100.00% covered (success)
100.00%
46 / 46
100.00% covered (success)
100.00%
1 / 1
1
 provider_get_versions
0.00% covered (danger)
0.00%
0 / 65
0.00% covered (danger)
0.00%
0 / 1
2
 test_get_versions
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
3
 test_version_phpbb_com
100.00% covered (success)
100.00%
51 / 51
100.00% covered (success)
100.00%
1 / 1
1
 test_file_downloader_file_not_found
100.00% covered (success)
100.00%
17 / 17
100.00% covered (success)
100.00%
1 / 1
1
 test_file_downloader_exception_not_found
100.00% covered (success)
100.00%
19 / 19
100.00% covered (success)
100.00%
1 / 1
1
 test_file_downloader_exception_moved
100.00% covered (success)
100.00%
19 / 19
100.00% covered (success)
100.00%
1 / 1
1
 test_file_downloader_exception_timeout
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
1 / 1
1
 test_file_downloader_exception_other
100.00% covered (success)
100.00%
17 / 17
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3use 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
17class 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}