Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
76.53% covered (warning)
76.53%
287 / 375
78.57% covered (warning)
78.57%
11 / 14
CRAP
0.00% covered (danger)
0.00%
0 / 1
test_ucp_controller_webpush_test
76.53% covered (warning)
76.53%
287 / 375
78.57% covered (warning)
78.57%
11 / 14
16.53
0.00% covered (danger)
0.00%
0 / 1
 getDataSet
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setUp
100.00% covered (success)
100.00%
41 / 41
100.00% covered (success)
100.00%
1 / 1
1
 data_notification_exceptions
0.00% covered (danger)
0.00%
0 / 60
0.00% covered (danger)
0.00%
0 / 1
2
 test_notification_no_data
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
1
 test_get_user_notification
100.00% covered (success)
100.00%
44 / 44
100.00% covered (success)
100.00%
1 / 1
1
 test_get_user_notification_anonymous
100.00% covered (success)
100.00%
45 / 45
100.00% covered (success)
100.00%
1 / 1
1
 test_get_user_notification_anonymous_invalid_token
64.10% covered (warning)
64.10%
25 / 39
0.00% covered (danger)
0.00%
0 / 1
1.05
 test_get_user_notification_legacy
68.18% covered (warning)
68.18%
30 / 44
0.00% covered (danger)
0.00%
0 / 1
1.03
 test_worker
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
1
 test_worker_bot
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 test_check_subscribe_requests_invalid_form_token
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 test_check_subscribe_requests_anonymous_user
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 test_subscribe_success
100.00% covered (success)
100.00%
27 / 27
100.00% covered (success)
100.00%
1 / 1
1
 test_unsubscribe_success
100.00% covered (success)
100.00%
36 / 36
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2/**
3 *
4 * This file is part of the phpBB Forum Software package.
5 *
6 * @copyright (c) phpBB Limited <https://www.phpbb.com>
7 * @license GNU General Public License, version 2 (GPL-2.0)
8 *
9 * For full copyright and license information, please see
10 * the docs/CREDITS.txt file.
11 *
12 */
13
14use phpbb\exception\http_exception;
15use phpbb\notification\type\quote;
16use phpbb\request\request_interface;
17use phpbb\ucp\controller\webpush;
18use Symfony\Component\HttpFoundation\JsonResponse;
19use Symfony\Component\HttpFoundation\Response;
20
21class test_ucp_controller_webpush_test extends phpbb_database_test_case
22{
23    protected $auth;
24    protected $avatar_helper;
25    protected $config;
26    protected $controller;
27    protected $controller_helper;
28    protected $db;
29    protected $form_helper;
30    protected $language;
31    protected $notification_manager;
32    protected $path_helper;
33    protected $phpbb_root_path;
34    protected $php_ext;
35    protected $request;
36    protected $template;
37    protected $user;
38    protected $user_loader;
39
40    protected function getDataSet()
41    {
42        return $this->createXMLDataSet(__DIR__ . '/fixtures/webpush.xml');
43    }
44
45    protected function setUp(): void
46    {
47        global $auth, $config, $phpbb_dispatcher, $user, $phpbb_root_path, $phpEx;
48
49        parent::setUp();
50
51        $phpbb_dispatcher = new phpbb_mock_event_dispatcher();
52
53        $this->auth = $this->createMock(\phpbb\auth\auth::class);
54        $auth = $this->auth;
55        $this->avatar_helper = $this->createMock(\phpbb\avatar\helper::class);
56        $this->config = new \phpbb\config\config([
57            'allow_nocensors'    => false,
58            'sitename'            => 'yourdomain.com',
59        ]);
60        $config = $this->config;
61        $this->controller_helper = $this->createMock(\phpbb\controller\helper::class);
62        $this->db = $this->new_dbal();
63        $this->form_helper = $this->createMock(\phpbb\form\form_helper::class);
64        $lang_loader = new \phpbb\language\language_file_loader($phpbb_root_path, $phpEx);
65        $this->language = new \phpbb\language\language($lang_loader);
66        $this->notification_manager = $this->createMock(\phpbb\notification\manager::class);
67        $this->phpbb_root_path = $phpbb_root_path;
68        $this->php_ext = $phpEx;
69        $symfony_request = $this->createMock(\phpbb\symfony_request::class);
70        $this->request = $this->createMock(\phpbb\request\request_interface::class);
71        $this->template = $this->createMock(\Twig\Environment::class);
72        $this->user = new \phpbb\user($this->language, '\phpbb\datetime');
73
74        $user = $this->user;
75        $this->user_loader = new \phpbb\user_loader($this->avatar_helper, $this->db, $this->phpbb_root_path, $this->php_ext, 'phpbb_users');
76        $this->path_helper = new \phpbb\path_helper($symfony_request, $this->request, $phpbb_root_path, $phpEx);
77
78        $this->controller = new webpush(
79            $this->config,
80            $this->controller_helper,
81            $this->db,
82            $this->form_helper,
83            $this->language,
84            $this->notification_manager,
85            $this->path_helper,
86            $this->request,
87            $this->user_loader,
88            $this->user,
89            $this->template,
90            'phpbb_notification_push',
91            'phpbb_push_subscriptions'
92        );
93    }
94
95    public static function data_notification_exceptions(): array
96    {
97        return [
98            'not_ajax' => [
99                false,
100                false,
101                USER_NORMAL,
102                2,
103                [],
104                'NO_AUTH_OPERATION',
105            ],
106            'is_bot' => [
107                true,
108                true,
109                USER_NORMAL,
110                2,
111                [],
112                'NO_AUTH_OPERATION',
113            ],
114            'inactive_user' => [
115                true,
116                false,
117                USER_INACTIVE,
118                2,
119                [],
120                'NO_AUTH_OPERATION',
121            ],
122            'ignore_user' => [
123                true,
124                false,
125                USER_IGNORE,
126                2,
127                [],
128                'NO_AUTH_OPERATION',
129            ],
130            'no_notification' => [
131                true,
132                false,
133                USER_NORMAL,
134                2,
135                [],
136                'AJAX_ERROR_TEXT',
137            ],
138            'no_notification_anonymous' => [
139                true,
140                false,
141                USER_NORMAL,
142                ANONYMOUS,
143                [
144                    ['token', '', false, request_interface::REQUEST, 'foobar'],
145                ],
146                'AJAX_ERROR_TEXT',
147            ],
148            'no_notification_anonymous_no_token' => [
149                true,
150                false,
151                USER_NORMAL,
152                ANONYMOUS,
153                [],
154                'NO_AUTH_OPERATION',
155            ],
156        ];
157    }
158
159    /**
160     * @dataProvider data_notification_exceptions
161     */
162    public function test_notification_no_data($is_ajax, $is_bot, $user_type, $user_id, $request_data, $expected_message)
163    {
164        $this->request->method('is_ajax')->willReturn($is_ajax);
165        $this->request->expects($this->any())
166            ->method('variable')
167            ->willReturnMap($request_data);
168        $this->user->data = [
169            'is_bot'        => $is_bot,
170            'user_type'        => $user_type,
171            'user_id'        => $user_id,
172        ];
173
174        $this->expectException(http_exception::class);
175        $this->expectExceptionMessage($expected_message);
176
177        $this->controller->notification();
178    }
179
180    public function test_get_user_notification()
181    {
182        global $cache;
183        $cache = $this->createMock(\phpbb\cache\service::class);
184        $cache->method('obtain_word_list')->willReturn([]);
185
186        $this->auth->method('acl_get')->willReturn(true);
187
188        $this->notification_manager->method('get_item_type_class')
189            ->willReturnCallback(function(string $type_name, array $row_data) {
190                $notification_type = new quote(
191                    $this->avatar_helper,
192                    $this->controller_helper,
193                    $this->db,
194                    $this->language,
195                    $this->user,
196                    $this->auth,
197                    $this->phpbb_root_path,
198                    $this->php_ext,
199                    'phpbb_notifications'
200                );
201
202                $notification_type->set_user_loader($this->user_loader);
203                $notification_type->set_initial_data($row_data);
204
205                return $notification_type;
206            });
207
208        $this->request->method('is_ajax')->willReturn(true);
209        $this->request->expects($this->any())
210            ->method('variable')
211            ->willReturnMap([
212                ['token', '', false, request_interface::REQUEST, 'foobar'],
213                ['item_id', 0, false, request_interface::REQUEST, 1],
214                ['type_id', 0, false, request_interface::REQUEST, 4],
215            ]);
216        $this->user->data = [
217            'is_bot'        => false,
218            'user_type'        => USER_NORMAL,
219            'user_id'        => 2,
220            'user_options'    => 230271,
221        ];
222
223        $json_response = $this->controller->notification();
224
225        $response_data = json_decode($json_response->getContent(), true);
226
227        $this->assertEquals([
228            'heading' => 'yourdomain.com',
229            'title' => 'Quoted by Guest in:',
230            'text' => '"Welcome to phpBB"',
231            'url' => 'phpBB/viewtopic.php?p=1#p1',
232            'avatar' => [],
233        ], $response_data);
234    }
235
236    public function test_get_user_notification_anonymous()
237    {
238        global $cache;
239        $cache = $this->createMock(\phpbb\cache\service::class);
240        $cache->method('obtain_word_list')->willReturn([]);
241
242        $this->auth->method('acl_get')->willReturn(true);
243
244        $this->notification_manager->method('get_item_type_class')
245            ->willReturnCallback(function(string $type_name, array $row_data) {
246                $notification_type = new quote(
247                    $this->avatar_helper,
248                    $this->controller_helper,
249                    $this->db,
250                    $this->language,
251                    $this->user,
252                    $this->auth,
253                    $this->phpbb_root_path,
254                    $this->php_ext,
255                    'phpbb_notifications'
256                );
257
258                $notification_type->set_user_loader($this->user_loader);
259                $notification_type->set_initial_data($row_data);
260
261                return $notification_type;
262            });
263
264        $this->request->method('is_ajax')->willReturn(true);
265        $this->request->expects($this->any())
266            ->method('variable')
267            ->willReturnMap([
268                ['token', '', false, request_interface::REQUEST, '0ccf8fcd96a66297b77b66109cbe9870e1a6fa66e42b9bf36d1f2c7263240058'],
269                ['item_id', 0, false, request_interface::REQUEST, 1],
270                ['type_id', 0, false, request_interface::REQUEST, 4],
271                ['user_id', 0, false, request_interface::REQUEST, 2],
272            ]);
273        $this->user->data = [
274            'is_bot'        => false,
275            'user_type'        => USER_NORMAL,
276            'user_id'        => ANONYMOUS,
277            'user_options'    => 230271,
278        ];
279
280        $json_response = $this->controller->notification();
281
282        $response_data = json_decode($json_response->getContent(), true);
283
284        $this->assertEquals([
285            'heading' => 'yourdomain.com',
286            'title' => 'Quoted by Guest in:',
287            'text' => '"Welcome to phpBB"',
288            'url' => 'phpBB/viewtopic.php?p=1#p1',
289            'avatar' => [],
290        ], $response_data);
291    }
292
293    public function test_get_user_notification_anonymous_invalid_token()
294    {
295        global $cache;
296        $cache = $this->createMock(\phpbb\cache\service::class);
297        $cache->method('obtain_word_list')->willReturn([]);
298
299        $this->auth->method('acl_get')->willReturn(true);
300
301        $this->notification_manager->method('get_item_type_class')
302            ->willReturnCallback(function(string $type_name, array $row_data) {
303                $notification_type = new quote(
304                    $this->avatar_helper,
305                    $this->controller_helper,
306                    $this->db,
307                    $this->language,
308                    $this->user,
309                    $this->auth,
310                    $this->phpbb_root_path,
311                    $this->php_ext,
312                    'phpbb_notifications'
313                );
314
315                $notification_type->set_user_loader($this->user_loader);
316                $notification_type->set_initial_data($row_data);
317
318                return $notification_type;
319            });
320
321        $this->request->method('is_ajax')->willReturn(true);
322        $this->request->expects($this->any())
323            ->method('variable')
324            ->willReturnMap([
325                ['token', '', false, request_interface::REQUEST, '488c17afe4f18714c235b395e21b78df1c3d78bf1e13d0633ed9425d2eebf967'],
326                ['item_id', 0, false, request_interface::REQUEST, 1],
327                ['type_id', 0, false, request_interface::REQUEST, 4],
328                ['user_id', 0, false, request_interface::REQUEST, 2],
329            ]);
330        $this->user->data = [
331            'is_bot'        => false,
332            'user_type'        => USER_NORMAL,
333            'user_id'        => ANONYMOUS,
334            'user_options'    => 230271,
335        ];
336
337        $this->expectException(http_exception::class);
338        $this->expectExceptionMessage('NO_AUTH_OPERATION');
339
340        $this->controller->notification();
341    }
342
343    public function test_get_user_notification_legacy()
344    {
345        global $cache;
346        $cache = $this->createMock(\phpbb\cache\service::class);
347        $cache->method('obtain_word_list')->willReturn([]);
348
349        $this->auth->method('acl_get')->willReturn(true);
350
351        $this->notification_manager->method('get_item_type_class')
352            ->willReturnCallback(function(string $type_name, array $row_data) {
353                $notification_type = new quote(
354                    $this->avatar_helper,
355                    $this->controller_helper,
356                    $this->db,
357                    $this->language,
358                    $this->user,
359                    $this->auth,
360                    $this->phpbb_root_path,
361                    $this->php_ext,
362                    'phpbb_notifications'
363                );
364
365                $notification_type->set_user_loader($this->user_loader);
366                $notification_type->set_initial_data($row_data);
367
368                return $notification_type;
369            });
370
371        $this->request->method('is_ajax')->willReturn(true);
372        $this->request->expects($this->any())
373            ->method('variable')
374            ->willReturnMap([
375                ['token', '', false, request_interface::REQUEST, 'foobar'],
376                ['item_id', 0, false, request_interface::REQUEST, 2],
377                ['type_id', 0, false, request_interface::REQUEST, 4],
378            ]);
379        $this->user->data = [
380            'is_bot'        => false,
381            'user_type'        => USER_NORMAL,
382            'user_id'        => 2,
383            'user_options'    => 230271,
384        ];
385
386        $json_response = $this->controller->notification();
387
388        $response_data = json_decode($json_response->getContent(), true);
389
390        $this->assertEquals([
391            'heading' => 'yourdomain.com',
392            'title' => 'Quoted by Guest in:',
393            'text' => '"Welcome to phpBB"',
394            'url' => 'phpBB/viewtopic.php?p=1#p1',
395            'avatar' => [],
396        ], $response_data);
397    }
398
399    public function test_worker()
400    {
401        $this->template->method('render')->willReturn('rendered_content');
402        $this->controller_helper->method('route')->willReturn('test_route');
403        $this->config['assets_version'] = '1.0';
404
405        $response = $this->controller->worker();
406
407        $this->assertInstanceOf(Response::class, $response);
408        $this->assertEquals('text/javascript; charset=UTF-8', $response->headers->get('Content-Type'));
409        $this->assertEquals('rendered_content', $response->getContent());
410        $this->assertNull($response->headers->get('X-PHPBB-IS-BOT'));
411    }
412
413    public function test_worker_bot()
414    {
415        $this->template->method('render')->willReturn('rendered_content');
416        $this->controller_helper->method('route')->willReturn('test_route');
417        $this->config['assets_version'] = '1.0';
418        $this->user->data['is_bot'] = true;
419
420        $response = $this->controller->worker();
421
422        $this->assertEquals('yes', $response->headers->get('X-PHPBB-IS-BOT'));
423    }
424
425    public function test_check_subscribe_requests_invalid_form_token()
426    {
427        $this->form_helper->method('check_form_tokens')->willReturn(false);
428
429        $this->expectException(http_exception::class);
430        $this->expectExceptionMessage('FORM_INVALID');
431
432        $check_subscribe_reflection = new ReflectionMethod($this->controller, 'check_subscribe_requests');
433        $check_subscribe_reflection->invoke($this->controller);
434    }
435
436    public function test_check_subscribe_requests_anonymous_user()
437    {
438        $this->form_helper->method('check_form_tokens')->willReturn(true);
439        $this->request->method('is_ajax')->willReturn(true);
440        $this->user->data['user_id'] = ANONYMOUS;
441
442        $this->expectException(http_exception::class);
443        $this->expectExceptionMessage('NO_AUTH_OPERATION');
444
445        $check_subscribe_reflection = new ReflectionMethod($this->controller, 'check_subscribe_requests');
446        $check_subscribe_reflection->invoke($this->controller);
447    }
448
449    public function test_subscribe_success()
450    {
451        $this->form_helper->method('check_form_tokens')->willReturn(true);
452        $this->request->method('is_ajax')->willReturn(true);
453        $this->user->data['user_id'] = 2;
454        $this->user->data['is_bot'] = false;
455        $this->user->data['user_type'] = USER_NORMAL;
456
457        $symfony_request = $this->createMock(\phpbb\symfony_request::class);
458        $symfony_request->method('get')->willReturn(json_encode([
459            'endpoint' => 'test_endpoint',
460            'expiration_time' => 0,
461            'keys' => ['p256dh' => 'test_p256dh', 'auth' => 'test_auth']
462        ]));
463
464        $response = $this->controller->subscribe($symfony_request);
465
466        $this->assertInstanceOf(JsonResponse::class, $response);
467        $this->assertEquals(['success' => true, 'form_tokens' => $this->form_helper->get_form_tokens(webpush::FORM_TOKEN_UCP)], json_decode($response->getContent(), true));
468
469        // Get subscription data from database
470        $sql = 'SELECT *
471                FROM phpbb_push_subscriptions
472                WHERE user_id = 2';
473        $result = $this->db->sql_query($sql);
474        $row = $this->db->sql_fetchrow($result);
475        $this->db->sql_freeresult($result);
476
477        $this->assertEquals([
478            'user_id' => '2',
479            'endpoint' => 'test_endpoint',
480            'p256dh' => 'test_p256dh',
481            'auth' => 'test_auth',
482            'expiration_time' => 0,
483            'subscription_id' => '1',
484        ], $row);
485    }
486
487    public function test_unsubscribe_success()
488    {
489        $this->form_helper->method('check_form_tokens')->willReturn(true);
490        $this->request->method('is_ajax')->willReturn(true);
491        $this->user->data['user_id'] = 2;
492        $this->user->data['is_bot'] = false;
493        $this->user->data['user_type'] = USER_NORMAL;
494
495        $symfony_request = $this->createMock(\phpbb\symfony_request::class);
496        $symfony_request->method('get')->willReturn(json_encode([
497            'endpoint' => 'test_endpoint',
498            'expiration_time' => 0,
499            'keys' => ['p256dh' => 'test_p256dh', 'auth' => 'test_auth']
500        ]));
501
502        $response = $this->controller->subscribe($symfony_request);
503
504        $this->assertInstanceOf(JsonResponse::class, $response);
505        $this->assertEquals(['success' => true, 'form_tokens' => $this->form_helper->get_form_tokens(webpush::FORM_TOKEN_UCP)], json_decode($response->getContent(), true));
506
507        // Get subscription data from database
508        $sql = 'SELECT *
509                FROM phpbb_push_subscriptions
510                WHERE user_id = 2';
511        $result = $this->db->sql_query($sql);
512        $row = $this->db->sql_fetchrow($result);
513        $this->db->sql_freeresult($result);
514
515        $this->assertEquals([
516            'user_id' => '2',
517            'endpoint' => 'test_endpoint',
518            'p256dh' => 'test_p256dh',
519            'auth' => 'test_auth',
520            'expiration_time' => 0,
521            'subscription_id' => '1',
522        ], $row);
523
524        // Now unsubscribe
525        $response = $this->controller->unsubscribe($symfony_request);
526
527        $this->assertInstanceOf(JsonResponse::class, $response);
528        $this->assertEquals(['success' => true, 'form_tokens' => $this->form_helper->get_form_tokens(webpush::FORM_TOKEN_UCP)], json_decode($response->getContent(), true));
529
530        // Get subscription data from database
531        $sql = 'SELECT *
532                FROM phpbb_push_subscriptions
533                WHERE user_id = 2';
534        $result = $this->db->sql_query($sql);
535        $row = $this->db->sql_fetchrow($result);
536        $this->db->sql_freeresult($result);
537
538        $this->assertEmpty($row);
539    }
540}