the browser-facing portion of osu!
at master 5.3 kB view raw
1<?php 2 3// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0. 4// See the LICENCE file in the repository root for full licence text. 5 6namespace Tests; 7 8use App\Events\NewPrivateNotificationEvent; 9use App\Jobs\Notifications\BeatmapsetDiscussionPostNew; 10use App\Jobs\Notifications\BroadcastNotificationBase; 11use App\Libraries\Chat; 12use App\Mail\UserNotificationDigest; 13use App\Models\Beatmapset; 14use App\Models\Notification; 15use App\Models\User; 16use App\Models\UserNotificationOption; 17use Event; 18use Mail; 19use Queue; 20use ReflectionClass; 21use ReflectionClassConstant; 22use Symfony\Component\Finder\Finder; 23 24class BroadcastNotificationTest extends TestCase 25{ 26 private const IGNORED_CONST_NAMES = ['NAME_TO_CATEGORY', 'NOTIFIABLE_CLASSES', 'SUBTYPES']; 27 28 protected $sender; 29 30 public function testNoNotificationForBotUser() 31 { 32 $bot = User::factory()->withGroup('bot')->create(['user_allow_pm' => true]); 33 $this->sender->markSessionVerified(); 34 $notificationsCount = Notification::count(); 35 36 Chat::sendPrivateMessage($this->sender, $bot, 'hello', false); 37 $this->runFakeQueue(); 38 39 $this->assertSame($notificationsCount, Notification::count()); 40 Event::assertNotDispatched(NewPrivateNotificationEvent::class); 41 } 42 43 /** 44 * @dataProvider notificationNamesDataProvider 45 */ 46 public function testAllNotificationNamesHaveNotificationClasses($name) 47 { 48 $this->assertNotNull(BroadcastNotificationBase::getNotificationClass($name)); 49 } 50 51 /** 52 * @dataProvider notificationJobClassesDataProvider 53 */ 54 public function testNotificationOptionNameHasDeliveryModes($class) 55 { 56 $predicate = $class::NOTIFICATION_OPTION_NAME === null 57 || in_array($class::NOTIFICATION_OPTION_NAME, UserNotificationOption::HAS_DELIVERY_MODES, true); 58 59 $this->assertTrue($predicate, "NOTIFICATION_OPTION_NAME for {$class} must be null or in UserNotificationOption::HAS_DELIVERY_MODES"); 60 } 61 62 /** 63 * @dataProvider userNotificationDetailsDataProvider 64 */ 65 public function testSendNotificationWithOptions($details) 66 { 67 $user = User::factory()->create(); 68 $user->notificationOptions()->create([ 69 'name' => UserNotificationOption::BEATMAPSET_MODDING, 70 'details' => $details, 71 ]); 72 73 $beatmapset = Beatmapset::factory()->owner()->withDiscussion()->create(); 74 $beatmapset->watches()->create([ 75 'last_read' => now()->subSeconds(), // make sure last_read isn't the same second the test runs. 76 'user_id' => $user->getKey(), 77 ]); 78 79 $this 80 ->actingAsVerified($this->sender) 81 ->post(route('beatmapsets.discussions.posts.store'), $this->makeBeatmapsetDiscussionPostParams($beatmapset, 'praise')) 82 ->assertStatus(200); 83 84 Queue::assertPushed(BeatmapsetDiscussionPostNew::class); 85 $this->runFakeQueue(); 86 87 if ($details['push'] ?? BeatmapsetDiscussionPostNew::DELIVERY_MODE_DEFAULTS['push']) { 88 Event::assertDispatched(NewPrivateNotificationEvent::class); 89 } else { 90 Event::assertNotDispatched(NewPrivateNotificationEvent::class); 91 } 92 93 // make sure the mailer we want to check wasn't done by something else... 94 Mail::assertNotSent(UserNotificationDigest::class); 95 $this->artisan('notifications:send-mail'); 96 $this->runFakeQueue(); 97 98 if ($details['mail'] ?? BeatmapsetDiscussionPostNew::DELIVERY_MODE_DEFAULTS['mail']) { 99 Mail::assertSent(UserNotificationDigest::class); 100 } else { 101 Mail::assertNotSent(UserNotificationDigest::class); 102 } 103 } 104 105 public static function notificationJobClassesDataProvider() 106 { 107 $files = Finder::create()->files()->in(__DIR__.'/../app/Jobs/Notifications')->sortByName(); 108 foreach ($files as $file) { 109 $baseName = $file->getBasename(".{$file->getExtension()}"); 110 $classes[] = ["\\App\\Jobs\\Notifications\\{$baseName}"]; 111 } 112 113 return $classes; 114 } 115 116 public static function notificationNamesDataProvider() 117 { 118 // TODO: move notification names to different class instead of filtering 119 $constants = collect((new ReflectionClass(Notification::class))->getReflectionConstants()) 120 ->filter(fn (ReflectionClassConstant $constant) => ( 121 $constant->getDeclaringClass()->name === Notification::class 122 && !in_array($constant->name, static::IGNORED_CONST_NAMES, true) 123 )) 124 ->values(); 125 126 return $constants->map(fn (ReflectionClassConstant $constant) => [$constant->getValue()])->all(); 127 } 128 129 public static function userNotificationDetailsDataProvider() 130 { 131 return [ 132 [null], // for testing defaults. 133 [['mail' => false, 'push' => false]], 134 [['mail' => false, 'push' => true]], 135 [['mail' => true, 'push' => true]], 136 ]; 137 } 138 139 protected function setUp(): void 140 { 141 parent::setUp(); 142 143 // mocking the queue so we can run the job manually to get the created notification. 144 Queue::fake(); 145 Event::fake(); 146 Mail::fake(); 147 148 $this->sender = User::factory()->create(); 149 } 150}