Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 478
0.00% covered (danger)
0.00%
0 / 21
CRAP
0.00% covered (danger)
0.00%
0 / 1
notification_method_webpush_test
0.00% covered (danger)
0.00%
0 / 478
0.00% covered (danger)
0.00%
0 / 21
2162
0.00% covered (danger)
0.00%
0 / 1
 getDataSet
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 get_notification_methods
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 setUpBeforeClass
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 tearDownAfterClass
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 start_webpush_testing
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 stop_webpush_testing
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setUp
0.00% covered (danger)
0.00%
0 / 129
0.00% covered (danger)
0.00%
0 / 1
12
 data_notification_webpush
0.00% covered (danger)
0.00%
0 / 74
0.00% covered (danger)
0.00%
0 / 1
2
 test_notification_webpush
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 1
2
 test_get_subscription
0.00% covered (danger)
0.00%
0 / 30
0.00% covered (danger)
0.00%
0 / 1
30
 test_notify_empty_queue
0.00% covered (danger)
0.00%
0 / 26
0.00% covered (danger)
0.00%
0 / 1
6
 test_notify_invalid_endpoint
0.00% covered (danger)
0.00%
0 / 38
0.00% covered (danger)
0.00%
0 / 1
42
 test_notify_expired
0.00% covered (danger)
0.00%
0 / 33
0.00% covered (danger)
0.00%
0 / 1
30
 test_get_type
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 test_prune_notifications
0.00% covered (danger)
0.00%
0 / 39
0.00% covered (danger)
0.00%
0 / 1
30
 data_set_endpoint_padding
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
2
 test_set_endpoint_padding
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
2
 create_subscription_for_user
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 1
12
 expire_subscription
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 get_messages_for_subscription
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
6
 get_notifications
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
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\notification\method\webpush;
15use Symfony\Component\Config\FileLocator;
16use Symfony\Component\DependencyInjection\ContainerBuilder;
17use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
18
19require_once __DIR__ . '/base.php';
20
21/**
22 * @group slow
23 */
24class notification_method_webpush_test extends phpbb_tests_notification_base
25{
26    /** @var string[] VAPID keys for testing purposes */
27    public const VAPID_KEYS = [
28        'publicKey'        => 'BIcGkq1Ncj3a2-J0UW-1A0NETLjvxZzNLiYBiPVMKNjgwmwPi5jyK87VfS4FZn9n7S9pLMQzjV3LmFuOnRSOvmI',
29        'privateKey'    => 'SrlbBEVgibWmKHYbDPu4Y2XvDWPjeGcc9fC16jq01xU',
30    ];
31
32    /** @var webpush */
33    protected $notification_method_webpush;
34
35    /** @var \phpbb\language\language */
36    protected $language;
37
38    /** @var \phpbb\log\log_interface */
39    protected $log;
40
41    public function getDataSet()
42    {
43        return $this->createXMLDataSet(__DIR__ . '/fixtures/webpush_notification.type.post.xml');
44    }
45
46    protected function get_notification_methods()
47    {
48        return [
49            'notification.method.webpush',
50        ];
51    }
52
53    public static function setUpBeforeClass(): void
54    {
55        self::start_webpush_testing();
56    }
57
58    public static function tearDownAfterClass(): void
59    {
60        self::stop_webpush_testing();
61    }
62
63    protected static function start_webpush_testing(): void
64    {
65        // Stop first to ensure port is available
66        self::stop_webpush_testing();
67
68        $process = new \Symfony\Component\Process\Process(['node_modules/.bin/web-push-testing', '--port', '9012', 'start']);
69        $process->run();
70        if (!$process->isSuccessful())
71        {
72            self::fail('Starting web push testing service failed: ' . $process->getErrorOutput());
73        }
74    }
75
76    protected static function stop_webpush_testing(): void
77    {
78        $process = new \Symfony\Component\Process\Process(['node_modules/.bin/web-push-testing', '--port', '9012', 'stop']);
79        $process->run();
80    }
81
82    protected function setUp(): void
83    {
84        phpbb_database_test_case::setUp();
85
86        global $phpbb_root_path, $phpEx;
87
88        include_once(__DIR__ . '/ext/test/notification/type/test.' . $phpEx);
89
90        global $db, $config, $user, $auth, $cache, $phpbb_container, $phpbb_dispatcher;
91
92        $avatar_helper = $this->getMockBuilder('\phpbb\avatar\helper')
93                              ->disableOriginalConstructor()
94                              ->getMock();
95        $controller_helper = $this->getMockBuilder('\phpbb\controller\helper')
96                                  ->disableOriginalConstructor()
97                                  ->getMock();
98        $db = $this->db = $this->new_dbal();
99        $config = $this->config = new \phpbb\config\config([
100            'allow_privmsg'            => true,
101            'allow_bookmarks'        => true,
102            'allow_topic_notify'    => true,
103            'allow_forum_notify'    => true,
104            'allow_board_notifications'    => true,
105            'webpush_vapid_public'    => self::VAPID_KEYS['publicKey'],
106            'webpush_vapid_private'    => self::VAPID_KEYS['privateKey'],
107        ]);
108        $lang_loader = new \phpbb\language\language_file_loader($phpbb_root_path, $phpEx);
109        $this->language = new \phpbb\language\language($lang_loader);
110        $this->language->add_lang('acp/common');
111        $user = new \phpbb\user($this->language, '\phpbb\datetime');
112        $this->user = $user;
113        $this->user->data['user_options'] = 230271;
114        $this->user_loader = new \phpbb\user_loader($avatar_helper, $this->db, $phpbb_root_path, $phpEx, 'phpbb_users');
115        $auth = $this->auth = new phpbb_mock_notifications_auth();
116        $this->phpbb_dispatcher = new phpbb_mock_event_dispatcher();
117        $phpbb_dispatcher = $this->phpbb_dispatcher;
118        $cache_driver = new \phpbb\cache\driver\dummy();
119        $cache = $this->cache = new \phpbb\cache\service(
120            $cache_driver,
121            $this->config,
122            $this->db,
123            $this->phpbb_dispatcher,
124            $phpbb_root_path,
125            $phpEx
126        );
127
128        $log_table = 'phpbb_log';
129        $this->log = new \phpbb\log\log($this->db, $user, $auth, $this->phpbb_dispatcher, $phpbb_root_path, 'adm/', $phpEx, $log_table);
130
131        $phpbb_container = $this->container = new ContainerBuilder();
132        $loader     = new YamlFileLoader($phpbb_container, new FileLocator(__DIR__ . '/fixtures'));
133        $loader->load('services_notification.yml');
134        $phpbb_container->set('avatar.helper', $avatar_helper);
135        $phpbb_container->set('user_loader', $this->user_loader);
136        $phpbb_container->set('user', $user);
137        $phpbb_container->set('language', $this->language);
138        $phpbb_container->set('config', $this->config);
139        $phpbb_container->set('dbal.conn', $this->db);
140        $phpbb_container->set('controller.helper', $this->createMock('\phpbb\controller\helper'));
141        $phpbb_container->set('auth', $auth);
142        $phpbb_container->set('cache.driver', $cache_driver);
143        $phpbb_container->set('cache', $cache);
144        $phpbb_container->set('log', $this->log);
145        $phpbb_container->set('text_formatter.utils', new \phpbb\textformatter\s9e\utils());
146        $phpbb_container->set('dispatcher', $this->phpbb_dispatcher);
147        $phpbb_container->set('event_dispatcher', $this->phpbb_dispatcher);
148        $phpbb_container->setParameter('core.root_path', $phpbb_root_path);
149        $phpbb_container->setParameter('core.php_ext', $phpEx);
150        $phpbb_container->setParameter('tables.notifications', 'phpbb_notifications');
151        $phpbb_container->setParameter('tables.user_notifications', 'phpbb_user_notifications');
152        $phpbb_container->setParameter('tables.notification_types', 'phpbb_notification_types');
153        $phpbb_container->setParameter('tables.notification_emails', 'phpbb_notification_emails');
154        $phpbb_container->setParameter('tables.notification_push', 'phpbb_notification_push');
155        $phpbb_container->setParameter('tables.push_subscriptions', 'phpbb_push_subscriptions');
156        $phpbb_container->set(
157            'text_formatter.s9e.mention_helper',
158            new \phpbb\textformatter\s9e\mention_helper(
159                $this->db,
160                $auth,
161                $this->user,
162                $phpbb_root_path,
163                $phpEx
164            )
165        );
166
167        $ban_type_email = new \phpbb\ban\type\email($this->db, 'phpbb_bans', 'phpbb_users', 'phpbb_sessions', 'phpbb_sessions_keys');
168        $ban_type_user = new \phpbb\ban\type\user($this->db, 'phpbb_bans', 'phpbb_users', 'phpbb_sessions', 'phpbb_sessions_keys');
169        $ban_type_ip = new \phpbb\ban\type\ip($this->db, 'phpbb_bans', 'phpbb_users', 'phpbb_sessions', 'phpbb_sessions_keys');
170        $phpbb_container->set('ban.type.email', $ban_type_email);
171        $phpbb_container->set('ban.type.user', $ban_type_user);
172        $phpbb_container->set('ban.type.ip', $ban_type_ip);
173        $collection = new \phpbb\di\service_collection($phpbb_container);
174        $collection->add('ban.type.email');
175        $collection->add('ban.type.user');
176        $collection->add('ban.type.ip');
177        $ban_manager = new \phpbb\ban\manager($collection, new \phpbb\cache\driver\dummy(), $this->db, $this->language, $this->log, $user, 'phpbb_bans', 'phpbb_users');
178        $phpbb_container->set('ban.manager', $ban_manager);
179
180        $messenger_method_collection = new \phpbb\di\service_collection($phpbb_container);
181        $phpbb_container->set('messenger.method_collection', $messenger_method_collection);
182
183        $this->notification_method_webpush = new \phpbb\notification\method\webpush(
184            $phpbb_container->get('config'),
185            $phpbb_container->get('dbal.conn'),
186            $phpbb_container->get('log'),
187            $phpbb_container->get('user_loader'),
188            $phpbb_container->get('user'),
189            $phpbb_root_path,
190            $phpEx,
191            $phpbb_container->getParameter('tables.notification_push'),
192            $phpbb_container->getParameter('tables.push_subscriptions')
193        );
194
195        $phpbb_container->set('notification.method.webpush', $this->notification_method_webpush);
196
197        $this->notifications = new phpbb_notification_manager_helper(
198            array(),
199            array(),
200            $this->container,
201            $this->user_loader,
202            $this->phpbb_dispatcher,
203            $this->db,
204            $this->cache,
205            $this->language,
206            $this->user,
207            'phpbb_notification_types',
208            'phpbb_user_notifications'
209        );
210
211        $phpbb_container->set('notification_manager', $this->notifications);
212
213        $phpbb_container->addCompilerPass(new phpbb\di\pass\markpublic_pass());
214
215        $phpbb_container->compile();
216
217        $this->notifications->setDependencies($this->auth, $this->config);
218
219        $types = array();
220        foreach ($this->get_notification_types() as $type)
221        {
222            $class = $this->build_type($type);
223
224            $types[$type] = $class;
225        }
226
227        $this->notifications->set_var('notification_types', $types);
228
229        $methods = array();
230        foreach ($this->get_notification_methods() as $method)
231        {
232            $class = $this->container->get($method);
233
234            $methods[$method] = $class;
235        }
236
237        $this->notifications->set_var('notification_methods', $methods);
238    }
239
240    public static function data_notification_webpush()
241    {
242        return [
243            /**
244            * Normal post
245            *
246            * User => State description
247            *    2    => Topic id=1 and id=2 subscribed, should receive a new topics post notification
248            *    3    => Topic id=1 subscribed, should receive a new topic post notification
249            *    4    => Topic id=1 subscribed, should receive a new topic post notification
250            *    5    => Topic id=1 subscribed, post id=1 already notified, should receive a new topic post notification
251            *    6    => Topic id=1 and forum id=1 subscribed, should receive a new topic/forum post notification
252            *    7    => Forum id=1 subscribed, should NOT receive a new topic post but a forum post notification
253            *    8    => Forum id=1 subscribed, post id=1 already notified, should NOT receive a new topic post but a forum post notification
254            */
255            [
256                'notification.type.post',
257                [
258                    'forum_id'        => '1',
259                    'post_id'        => '2',
260                    'topic_id'        => '1',
261                ],
262                [
263                    2 => ['user_id' => '2'],
264                    3 => ['user_id' => '3'],
265                    4 => ['user_id' => '4'],
266                    5 => ['user_id' => '5'],
267                    6 => ['user_id' => '6'],
268                ],
269            ],
270            [
271                'notification.type.forum',
272                [
273                    'forum_id'        => '1',
274                    'post_id'        => '3',
275                    'topic_id'        => '1',
276                ],
277                [
278                    6 => ['user_id' => '6'],
279                    7 => ['user_id' => '7'],
280                    8 => ['user_id' => '8']
281                ],
282            ],
283            [
284                'notification.type.post',
285                [
286                    'forum_id'        => '1',
287                    'post_id'        => '4',
288                    'topic_id'        => '2',
289                ],
290                [
291                    2 => ['user_id' => '2'],
292                ],
293            ],
294            [
295                'notification.type.forum',
296                [
297                    'forum_id'        => '1',
298                    'post_id'        => '5',
299                    'topic_id'        => '2',
300                ],
301                [
302                    6 => ['user_id' => '6'],
303                    7 => ['user_id' => '7'],
304                    8 => ['user_id' => '8'],
305                ],
306            ],
307            [
308                'notification.type.post',
309                [
310                    'forum_id'        => '2',
311                    'post_id'        => '6',
312                    'topic_id'        => '3',
313                ],
314                [
315                ],
316            ],
317            [
318                'notification.type.forum',
319                [
320                    'forum_id'        => '2',
321                    'post_id'        => '6',
322                    'topic_id'        => '3',
323                ],
324                [
325                ],
326            ],
327        ];
328    }
329
330    /**
331     * @dataProvider data_notification_webpush
332     */
333    public function test_notification_webpush($notification_type, $post_data, $expected_users)
334    {
335        $post_data = array_merge([
336                'post_time' => 1349413322,
337                'poster_id' => 1,
338                'topic_title' => '',
339                'post_subject' => '',
340                'post_username' => '',
341                'forum_name' => '',
342            ],
343
344            $post_data);
345        $notification_options = [
346            'item_id'            => $post_data['post_id'],
347            'item_parent_id'    => $post_data['topic_id'],
348        ];
349
350        $notified_users = $this->notification_method_webpush->get_notified_users($this->notifications->get_notification_type_id($notification_type), $notification_options);
351        $this->assertEquals(0, count($notified_users), 'Assert no user has been notified yet');
352
353        $this->notifications->add_notifications($notification_type, $post_data);
354
355        $notified_users = $this->notification_method_webpush->get_notified_users($this->notifications->get_notification_type_id($notification_type), $notification_options);
356        $this->assertEquals($expected_users, $notified_users, 'Assert that expected users have been notified');
357
358        $post_data['post_id']++;
359        $notification_options['item_id'] = $post_data['post_id'];
360        $post_data['post_time'] = 1349413323;
361
362        $this->notifications->add_notifications($notification_type, $post_data);
363
364        $notified_users2 = $this->notification_method_webpush->get_notified_users($this->notifications->get_notification_type_id($notification_type), $notification_options);
365        $this->assertEquals($expected_users, $notified_users2, 'Assert that expected users stay the same after replying to same topic');
366    }
367
368    /**
369     * @dataProvider data_notification_webpush
370     */
371    public function test_get_subscription($notification_type, $post_data, $expected_users): void
372    {
373        $subscription_info = [];
374        foreach ($expected_users as $user_id => $user_data)
375        {
376            $subscription_info[$user_id][] = $this->create_subscription_for_user($user_id);
377        }
378
379        // Create second subscription for first user ID passed
380        if (count($expected_users))
381        {
382            $first_user_id = array_key_first($expected_users);
383            $subscription_info[$first_user_id][] = $this->create_subscription_for_user($first_user_id);
384        }
385
386        $post_data = array_merge([
387            'post_time' => 1349413322,
388            'poster_id' => 1,
389            'topic_title' => '',
390            'post_subject' => '',
391            'post_username' => '',
392            'forum_name' => '',
393        ],
394
395            $post_data);
396        $notification_options = [
397            'item_id'            => $post_data['post_id'],
398            'item_parent_id'    => $post_data['topic_id'],
399        ];
400
401        $notified_users = $this->notification_method_webpush->get_notified_users($this->notifications->get_notification_type_id($notification_type), $notification_options);
402        $this->assertEquals(0, count($notified_users), 'Assert no user has been notified yet');
403
404        foreach ($expected_users as $user_id => $data)
405        {
406            $messages = $this->get_messages_for_subscription($subscription_info[$user_id][0]['clientHash']);
407            $this->assertEmpty($messages);
408        }
409
410        $this->notifications->add_notifications($notification_type, $post_data);
411
412        $notified_users = $this->notification_method_webpush->get_notified_users($this->notifications->get_notification_type_id($notification_type), $notification_options);
413        $this->assertEquals($expected_users, $notified_users, 'Assert that expected users have been notified');
414
415        foreach ($expected_users as $user_id => $data)
416        {
417            $messages = $this->get_messages_for_subscription($subscription_info[$user_id][0]['clientHash']);
418            $this->assertNotEmpty($messages, 'Failed asserting that user ' . $user_id . ' has received messages.');
419        }
420    }
421
422    /**
423     * @dataProvider data_notification_webpush
424     */
425    public function test_notify_empty_queue($notification_type, $post_data, $expected_users): void
426    {
427        foreach ($expected_users as $user_id => $user_data)
428        {
429            $this->create_subscription_for_user($user_id);
430        }
431
432        $post_data = array_merge([
433            'post_time' => 1349413322,
434            'poster_id' => 1,
435            'topic_title' => '',
436            'post_subject' => '',
437            'post_username' => '',
438            'forum_name' => '',
439        ],
440
441            $post_data);
442        $notification_options = [
443            'item_id'            => $post_data['post_id'],
444            'item_parent_id'    => $post_data['topic_id'],
445        ];
446
447        $notified_users = $this->notification_method_webpush->get_notified_users($this->notifications->get_notification_type_id($notification_type), $notification_options);
448        $this->assertEquals(0, count($notified_users), 'Assert no user has been notified yet');
449
450        $this->notification_method_webpush->notify(); // should have no effect
451
452        $notified_users = $this->notification_method_webpush->get_notified_users($this->notifications->get_notification_type_id($notification_type), $notification_options);
453        $this->assertEquals(0, count($notified_users), 'Assert no user has been notified yet');
454
455        $post_data['post_id']++;
456        $notification_options['item_id'] = $post_data['post_id'];
457        $post_data['post_time'] = 1349413323;
458
459        $this->notifications->add_notifications($notification_type, $post_data);
460
461        $notified_users2 = $this->notification_method_webpush->get_notified_users($this->notifications->get_notification_type_id($notification_type), $notification_options);
462        $this->assertEquals($expected_users, $notified_users2, 'Assert that expected users stay the same after replying to same topic');
463    }
464
465    /**
466     * @dataProvider data_notification_webpush
467     */
468    public function test_notify_invalid_endpoint($notification_type, $post_data, $expected_users): void
469    {
470        $subscription_info = [];
471        foreach ($expected_users as $user_id => $user_data)
472        {
473            $subscription_info[$user_id][] = $this->create_subscription_for_user($user_id);
474        }
475
476        // Create second subscription for first user ID passed
477        if (count($expected_users))
478        {
479            $first_user_id = array_key_first($expected_users);
480            $first_user_sub = $this->create_subscription_for_user($first_user_id, true);
481            $subscription_info[$first_user_id][] = $first_user_sub;
482        }
483
484        $post_data = array_merge([
485            'post_time' => 1349413322,
486            'poster_id' => 1,
487            'topic_title' => '',
488            'post_subject' => '',
489            'post_username' => '',
490            'forum_name' => '',
491        ],
492
493            $post_data);
494        $notification_options = [
495            'item_id'            => $post_data['post_id'],
496            'item_parent_id'    => $post_data['topic_id'],
497        ];
498
499        $notified_users = $this->notification_method_webpush->get_notified_users($this->notifications->get_notification_type_id($notification_type), $notification_options);
500        $this->assertEquals(0, count($notified_users), 'Assert no user has been notified yet');
501
502        foreach ($expected_users as $user_id => $data)
503        {
504            $messages = $this->get_messages_for_subscription($subscription_info[$user_id][0]['clientHash']);
505            $this->assertEmpty($messages);
506        }
507
508        $this->notifications->add_notifications($notification_type, $post_data);
509
510        $notified_users = $this->notification_method_webpush->get_notified_users($this->notifications->get_notification_type_id($notification_type), $notification_options);
511        $this->assertEquals($expected_users, $notified_users, 'Assert that expected users have been notified');
512
513        foreach ($expected_users as $user_id => $data)
514        {
515            $messages = $this->get_messages_for_subscription($subscription_info[$user_id][0]['clientHash']);
516            $this->assertNotEmpty($messages, 'Failed asserting that user ' . $user_id . ' has received messages.');
517        }
518
519        if (isset($first_user_sub))
520        {
521            $admin_logs = $this->log->get_logs('admin');
522            $this->db->sql_query('DELETE FROM phpbb_log'); // Clear logs
523            $this->assertCount(1, $admin_logs, 'Assert that an admin log was created for invalid endpoint');
524
525            $log_entry = $admin_logs[0];
526
527            $this->assertStringStartsWith('<strong>Web Push message could not be sent:</strong>', $log_entry['action']);
528            $this->assertStringContainsString('400', $log_entry['action']);
529        }
530    }
531
532    /**
533     * @dataProvider data_notification_webpush
534     */
535    public function test_notify_expired($notification_type, $post_data, $expected_users)
536    {
537        $subscription_info = [];
538        foreach ($expected_users as $user_id => $user_data)
539        {
540            $subscription_info[$user_id][] = $this->create_subscription_for_user($user_id);
541        }
542
543        $expected_delivered_users = $expected_users;
544
545        // Expire subscriptions for first user
546        if (count($expected_users))
547        {
548
549            $first_user_id = array_key_first($expected_users);
550            $first_user_subs = $subscription_info[$first_user_id];
551            unset($expected_delivered_users[$first_user_id]);
552            $this->expire_subscription($first_user_subs[0]['clientHash']);
553        }
554
555        $post_data = array_merge([
556            'post_time' => 1349413322,
557            'poster_id' => 1,
558            'topic_title' => '',
559            'post_subject' => '',
560            'post_username' => '',
561            'forum_name' => '',
562        ],
563
564            $post_data);
565        $notification_options = [
566            'item_id'            => $post_data['post_id'],
567            'item_parent_id'    => $post_data['topic_id'],
568        ];
569
570        $notified_users = $this->notification_method_webpush->get_notified_users($this->notifications->get_notification_type_id($notification_type), $notification_options);
571        $this->assertEquals(0, count($notified_users), 'Assert no user has been notified yet');
572
573        foreach ($expected_delivered_users as $user_id => $data)
574        {
575            $messages = $this->get_messages_for_subscription($subscription_info[$user_id][0]['clientHash']);
576            $this->assertEmpty($messages);
577        }
578
579        $this->notifications->add_notifications($notification_type, $post_data);
580
581        $notified_users = $this->notification_method_webpush->get_notified_users($this->notifications->get_notification_type_id($notification_type), $notification_options);
582        $this->assertEquals($expected_users, $notified_users, 'Assert that expected users have been notified');
583
584        foreach ($expected_delivered_users as $user_id => $data)
585        {
586            $messages = $this->get_messages_for_subscription($subscription_info[$user_id][0]['clientHash']);
587            $this->assertNotEmpty($messages, 'Failed asserting that user ' . $user_id . ' has received messages.');
588        }
589    }
590
591    public function test_get_type(): void
592    {
593        $this->assertEquals('notification.method.webpush', $this->notification_method_webpush->get_type());
594    }
595
596    /**
597     * @dataProvider data_notification_webpush
598     */
599    public function test_prune_notifications($notification_type, $post_data, $expected_users): void
600    {
601        $subscription_info = [];
602        foreach ($expected_users as $user_id => $user_data)
603        {
604            $subscription_info[$user_id][] = $this->create_subscription_for_user($user_id);
605        }
606
607        // Create second subscription for first user ID passed
608        if (count($expected_users))
609        {
610            $first_user_id = array_key_first($expected_users);
611            $subscription_info[$first_user_id][] = $this->create_subscription_for_user($first_user_id);
612        }
613
614        $post_data = array_merge([
615            'post_time' => 1349413322,
616            'poster_id' => 1,
617            'topic_title' => '',
618            'post_subject' => '',
619            'post_username' => '',
620            'forum_name' => '',
621        ],
622
623            $post_data);
624        $notification_options = [
625            'item_id'            => $post_data['post_id'],
626            'item_parent_id'    => $post_data['topic_id'],
627        ];
628
629        $notified_users = $this->notification_method_webpush->get_notified_users($this->notifications->get_notification_type_id($notification_type), $notification_options);
630        $this->assertEquals(0, count($notified_users), 'Assert no user has been notified yet');
631
632        foreach ($expected_users as $user_id => $data)
633        {
634            $messages = $this->get_messages_for_subscription($subscription_info[$user_id][0]['clientHash']);
635            $this->assertEmpty($messages);
636        }
637
638        $this->notifications->add_notifications($notification_type, $post_data);
639
640        $notified_users = $this->notification_method_webpush->get_notified_users($this->notifications->get_notification_type_id($notification_type), $notification_options);
641        $this->assertEquals($expected_users, $notified_users, 'Assert that expected users have been notified');
642
643        foreach ($expected_users as $user_id => $data)
644        {
645            $messages = $this->get_messages_for_subscription($subscription_info[$user_id][0]['clientHash']);
646            $this->assertNotEmpty($messages, 'Failed asserting that user ' . $user_id . ' has received messages.');
647        }
648
649        // Prune notifications with 0 time, shouldn't change anything
650        $prune_time = time();
651        $this->notification_method_webpush->prune_notifications(0);
652        $this->assertGreaterThanOrEqual($prune_time, $this->config->offsetGet('read_notification_last_gc'), 'Assert that prune time was set');
653
654        $cur_notifications = $this->get_notifications();
655        $this->assertSameSize($cur_notifications, $expected_users, 'Assert that no notifications have been pruned');
656
657        // Prune only read not supported, will prune all
658        $this->notification_method_webpush->prune_notifications($prune_time);
659        $this->assertGreaterThanOrEqual($prune_time, $this->config->offsetGet('read_notification_last_gc'), 'Assert that prune time was set');
660
661        $cur_notifications = $this->get_notifications();
662        $this->assertCount(0, $cur_notifications, 'Assert that no notifications have been pruned');
663    }
664
665    public static function data_set_endpoint_padding(): array
666    {
667        return [
668            [
669                'foo.mozilla.com',
670                webpush::MOZILLA_FALLBACK_PADDING
671            ],
672            [
673                'foo.mozaws.net',
674                webpush::MOZILLA_FALLBACK_PADDING
675            ],
676            [
677                'foo.android.googleapis.com',
678                \Minishlink\WebPush\Encryption::MAX_COMPATIBILITY_PAYLOAD_LENGTH,
679            ],
680        ];
681    }
682
683    /**
684     * @dataProvider data_set_endpoint_padding
685     */
686    public function test_set_endpoint_padding($endpoint, $expected_padding): void
687    {
688        $web_push_reflection = new \ReflectionMethod($this->notification_method_webpush, 'set_endpoint_padding');
689
690        $auth = [
691            'VAPID' => [
692                'subject' => generate_board_url(),
693                'publicKey' => $this->config['webpush_vapid_public'],
694                'privateKey' => $this->config['webpush_vapid_private'],
695            ],
696        ];
697
698        $web_push = new \Minishlink\WebPush\WebPush($auth);
699
700        $this->assertEquals(\Minishlink\WebPush\Encryption::MAX_COMPATIBILITY_PAYLOAD_LENGTH, $web_push->getAutomaticPadding());
701        $web_push_reflection->invoke($this->notification_method_webpush, $web_push, $endpoint);
702        $this->assertEquals($expected_padding, $web_push->getAutomaticPadding());
703    }
704
705    protected function create_subscription_for_user($user_id, bool $invalidate_endpoint = false): array
706    {
707        $client = new \GuzzleHttp\Client();
708        try
709        {
710            $response = $client->request('POST', 'http://localhost:9012/subscribe', ['form_params' => [
711                'applicationServerKey'    => self::VAPID_KEYS['publicKey'],
712            ]]);
713        }
714        catch (\GuzzleHttp\Exception\GuzzleException $exception)
715        {
716            $this->fail('Failed getting subscription from web-push-testing client: ' . $exception->getMessage());
717        }
718
719        $subscription_return = \phpbb\json\sanitizer::decode((string) $response->getBody());
720        $subscription_data = $subscription_return['data'];
721        $this->assertNotEmpty($subscription_data['endpoint']);
722        $this->assertStringStartsWith('http://localhost:9012/notify/', $subscription_data['endpoint']);
723        $this->assertIsArray($subscription_data['keys']);
724
725        if ($invalidate_endpoint)
726        {
727            $subscription_data['endpoint'] .= 'invalid';
728        }
729
730        $push_subscriptions_table = $this->container->getParameter('tables.push_subscriptions');
731
732        $sql = 'INSERT INTO ' . $push_subscriptions_table  . ' ' . $this->db->sql_build_array('INSERT', [
733            'user_id'        => $user_id,
734            'endpoint'        => $subscription_data['endpoint'],
735            'p256dh'        => $subscription_data['keys']['p256dh'],
736            'auth'            => $subscription_data['keys']['auth'],
737        ]);
738        $this->db->sql_query($sql);
739
740        return $subscription_data;
741    }
742
743    protected function expire_subscription(string $client_hash): void
744    {
745        $client = new \GuzzleHttp\Client();
746        try
747        {
748            $response = $client->request('POST', 'http://localhost:9012/expire-subscription/' . $client_hash);
749        }
750        catch (\GuzzleHttp\Exception\GuzzleException $exception)
751        {
752            $this->fail('Failed expiring subscription with web-push-testing client: ' . $exception->getMessage());
753        }
754
755        $subscription_return = \phpbb\json\sanitizer::decode((string) $response->getBody());
756        $this->assertEquals(200, $response->getStatusCode(), 'Expected response status to be 200');
757    }
758
759    protected function get_messages_for_subscription($client_hash): array
760    {
761        $client = new \GuzzleHttp\Client();
762        try
763        {
764            $response = $client->request('POST', 'http://localhost:9012/get-notifications', ['form_params' => [
765                'clientHash'    => $client_hash,
766            ]]);
767        }
768        catch (\GuzzleHttp\Exception\GuzzleException $exception)
769        {
770            $this->fail('Failed getting messages from web-push-testing client: ' . $exception->getMessage());
771        }
772
773        $response_data = json_decode($response->getBody()->getContents(), true);
774        $this->assertNotEmpty($response_data);
775        $this->assertArrayHasKey('data', $response_data);
776        $this->assertArrayHasKey('messages', $response_data['data']);
777
778        return $response_data['data']['messages'];
779    }
780
781    protected function get_notifications(): array
782    {
783        $webpush_table = $this->container->getParameter('tables.notification_push');
784        $sql = 'SELECT * FROM ' . $webpush_table;
785        $result = $this->db->sql_query($sql);
786        $sql_ary = $this->db->sql_fetchrowset($result);
787        $this->db->sql_freeresult($result);
788
789        return $sql_ary;
790    }
791}