the browser-facing portion of osu!
at master 313 lines 11 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 6declare(strict_types=1); 7 8namespace Tests\Libraries\Beatmapset; 9 10use App\Exceptions\AuthorizationException; 11use App\Exceptions\InvariantException; 12use App\Jobs\Notifications\BeatmapOwnerChange; 13use App\Libraries\Beatmapset\ChangeBeatmapOwners; 14use App\Models\Beatmap; 15use App\Models\BeatmapOwner; 16use App\Models\Beatmapset; 17use App\Models\BeatmapsetEvent; 18use App\Models\DeletedUser; 19use App\Models\User; 20use Arr; 21use Bus; 22use Tests\TestCase; 23 24class ChangeBeatmapOwnersTest extends TestCase 25{ 26 public static function dataProviderForInvalidUser(): array 27 { 28 return [ 29 ['bot'], 30 ['no_profile'], 31 ]; 32 } 33 34 public static function dataProviderForUpdateOwner(): array 35 { 36 return [ 37 'existing restricted user' => [['restricted', 'default'], true], 38 'new restricted user' => [['default', 'restricted'], false], 39 'new user' => [['default', 'default'], true], 40 ]; 41 } 42 43 public static function dataProviderForUpdateOwnerLoved(): array 44 { 45 return [ 46 [Beatmapset::STATES['graveyard'], true], 47 [Beatmapset::STATES['loved'], true], 48 [Beatmapset::STATES['ranked'], false], 49 [Beatmapset::STATES['wip'], false], 50 ]; 51 } 52 53 public function testMissingUser(): void 54 { 55 $moderator = User::factory()->withGroup('nat')->create(); 56 $otherUser = User::factory()->create(); 57 $missingUserId = User::max('user_id') + 1; 58 $userIds = [$missingUserId, $otherUser->getKey()]; 59 60 $beatmap = Beatmap::factory() 61 ->state(['user_id' => $missingUserId]) 62 ->has(BeatmapOwner::factory()->state(['user_id' => $missingUserId])) 63 ->for(Beatmapset::factory()->pending()->state(['user_id' => $missingUserId])) 64 ->create(); 65 66 $this->expectCountChange(fn () => BeatmapsetEvent::count(), 1); 67 68 (new ChangeBeatmapOwners($beatmap, $userIds, $moderator))->handle(); 69 70 $beatmap = $beatmap->fresh(); 71 $newOwners = $beatmap->getOwners(); 72 $this->assertEqualsCanonicalizing($userIds, $newOwners->pluck('user_id')->toArray()); 73 $this->assertTrue($newOwners->find($missingUserId) instanceof DeletedUser); 74 $this->assertSame($userIds[0], $beatmap->user_id); 75 76 Bus::assertDispatched(BeatmapOwnerChange::class); 77 } 78 79 /** 80 * @dataProvider dataProviderForInvalidUser 81 */ 82 public function testInvalidUser(string $group): void 83 { 84 $moderator = User::factory()->withGroup('nat')->create(); 85 $owner = User::factory()->create(); 86 $invalidUser = User::factory()->withGroup($group)->create(); 87 88 $beatmap = Beatmap::factory() 89 ->for(Beatmapset::factory()->pending()->owner($owner)) 90 ->owner($owner) 91 ->create(); 92 93 $this->expectCountChange(fn () => BeatmapsetEvent::count(), 0); 94 95 $this->expectExceptionCallable( 96 fn () => (new ChangeBeatmapOwners($beatmap, [$invalidUser->getKey()], $moderator))->handle(), 97 InvariantException::class, 98 ); 99 100 $beatmap = $beatmap->fresh(); 101 $this->assertEqualsCanonicalizing([$owner->getKey()], $beatmap->getOwners()->pluck('user_id')->toArray()); 102 103 Bus::assertNotDispatched(BeatmapOwnerChange::class); 104 } 105 106 /** 107 * @dataProvider dataProviderForUpdateOwner 108 */ 109 public function testUpdateOwner(array $states, bool $success): void 110 { 111 $factory = User::factory(); 112 $moderator = $factory->withGroup('nat')->create(); 113 $users = array_map(fn ($state) => $factory->$state()->create(), $states); 114 $owner = $users[0]; 115 116 $beatmap = Beatmap::factory() 117 ->for(Beatmapset::factory()->pending()->owner($owner)) 118 ->owner($owner) 119 ->create(); 120 121 $this->expectCountChange(fn () => BeatmapsetEvent::count(), $success ? 1 : 0); 122 123 $this->expectExceptionCallable( 124 fn () => (new ChangeBeatmapOwners($beatmap, Arr::pluck($users, 'user_id'), $moderator))->handle(), 125 $success ? null : InvariantException::class, 126 ); 127 128 $beatmap = $beatmap->fresh(); 129 130 if ($success) { 131 $this->assertEqualsCanonicalizing(Arr::pluck($users, 'user_id'), $beatmap->getOwners()->pluck('user_id')->toArray()); 132 $this->assertSame($users[0]->getKey(), $beatmap->user_id); 133 Bus::assertDispatched(BeatmapOwnerChange::class); 134 } else { 135 $this->assertEqualsCanonicalizing([$owner->getKey()], $beatmap->getOwners()->pluck('user_id')->toArray()); 136 Bus::assertNotDispatched(BeatmapOwnerChange::class); 137 } 138 } 139 140 public function testUpdateOwnerExistingRestrictedUser(): void 141 { 142 $source = User::factory()->withGroup('gmt')->create(); 143 $owner = User::factory()->restricted()->create(); 144 $users = [User::factory()->create(), $owner]; 145 $ownerId = $owner->getKey(); 146 147 $beatmap = Beatmap::factory() 148 ->for(Beatmapset::factory()->pending()->owner($owner)) 149 ->owner($owner) 150 ->create(); 151 152 $this->expectCountChange(fn () => BeatmapsetEvent::count(), 1); 153 154 (new ChangeBeatmapOwners($beatmap, Arr::pluck($users, 'user_id'), $source))->handle(); 155 156 $beatmap = $beatmap->fresh(); 157 $newOwners = $beatmap->getOwners(); 158 $this->assertCount(count($users), $newOwners); 159 $this->assertEqualsCanonicalizing(Arr::pluck($users, 'user_id'), $newOwners->pluck('user_id')->toArray()); 160 161 Bus::assertDispatched(BeatmapOwnerChange::class); 162 } 163 164 public function testUpdateOwnerInvalidState(): void 165 { 166 $user = User::factory()->create(); 167 $owner = User::factory()->create(); 168 $beatmap = Beatmap::factory() 169 ->for(Beatmapset::factory()->qualified()->owner($owner)) 170 ->owner($owner) 171 ->create(); 172 173 $this->expectCountChange(fn () => BeatmapsetEvent::count(), 0); 174 $this->expectExceptionCallable( 175 fn () => (new ChangeBeatmapOwners($beatmap, [$user->getKey()], $owner))->handle(), 176 AuthorizationException::class 177 ); 178 179 $beatmap = $beatmap->fresh(); 180 $this->assertEqualsCanonicalizing([$owner->getKey()], $beatmap->getOwners()->pluck('user_id')->toArray()); 181 $this->assertSame($owner->getKey(), $beatmap->user_id); 182 183 Bus::assertNotDispatched(BeatmapOwnerChange::class); 184 } 185 186 public function testUpdateOwnerInvalidUser(): void 187 { 188 $owner = User::factory()->create(); 189 $beatmap = Beatmap::factory() 190 ->for(Beatmapset::factory()->pending()->owner($owner)) 191 ->owner($owner) 192 ->create(); 193 194 $this->expectCountChange(fn () => BeatmapsetEvent::count(), 0); 195 $this->expectExceptionCallable( 196 fn () => (new ChangeBeatmapOwners($beatmap, [User::max('user_id') + 1], $owner))->handle(), 197 InvariantException::class 198 ); 199 200 $beatmap = $beatmap->fresh(); 201 $this->assertEqualsCanonicalizing([$owner->getKey()], $beatmap->getOwners()->pluck('user_id')->toArray()); 202 $this->assertSame($owner->getKey(), $beatmap->user_id); 203 204 Bus::assertNotDispatched(BeatmapOwnerChange::class); 205 } 206 207 /** 208 * @dataProvider dataProviderForUpdateOwnerLoved 209 */ 210 public function testUpdateOwnerLoved(int $approved, bool $ok): void 211 { 212 $moderator = User::factory()->withGroup('loved')->create(); 213 $user = User::factory()->create(); 214 $owner = User::factory()->create(); 215 $beatmap = Beatmap::factory() 216 ->for(Beatmapset::factory()->state([ 217 'approved' => $approved, 218 'approved_date' => now(), 219 ])->owner($owner)) 220 ->owner($owner) 221 ->create(); 222 223 $this->expectCountChange(fn () => BeatmapsetEvent::count(), $ok ? 1 : 0); 224 225 $this->expectExceptionCallable( 226 fn () => (new ChangeBeatmapOwners($beatmap, [$user->getKey()], $moderator))->handle(), 227 $ok ? null : AuthorizationException::class, 228 ); 229 230 $beatmap = $beatmap->fresh(); 231 $expectedUser = $ok ? $user : $owner; 232 $this->assertEqualsCanonicalizing([$expectedUser->getKey()], $beatmap->getOwners()->pluck('user_id')->toArray()); 233 $this->assertSame($expectedUser->getKey(), $beatmap->user_id); 234 235 if ($ok) { 236 Bus::assertDispatched(BeatmapOwnerChange::class); 237 } else { 238 Bus::assertNotDispatched(BeatmapOwnerChange::class); 239 } 240 } 241 242 public function testUpdateOwnerModerator(): void 243 { 244 $moderator = User::factory()->withGroup('nat')->create(); 245 $user = User::factory()->create(); 246 $owner = User::factory()->create(); 247 $beatmap = Beatmap::factory() 248 ->for(Beatmapset::factory()->state([ 249 'approved' => Beatmapset::STATES['ranked'], 250 'approved_date' => now(), 251 ])->owner($owner)) 252 ->owner($owner) 253 ->create(); 254 255 $this->expectCountChange(fn () => BeatmapsetEvent::count(), 1); 256 257 (new ChangeBeatmapOwners($beatmap, [$user->getKey()], $moderator))->handle(); 258 259 $beatmap = $beatmap->fresh(); 260 $this->assertEqualsCanonicalizing([$user->getKey()], $beatmap->getOwners()->pluck('user_id')->toArray()); 261 $this->assertSame($user->getKey(), $beatmap->user_id); 262 } 263 264 public function testUpdateOwnerNotOwner(): void 265 { 266 $user = User::factory()->create(); 267 $owner = User::factory()->create(); 268 $beatmap = Beatmap::factory() 269 ->for(Beatmapset::factory()->state([ 270 'approved' => Beatmapset::STATES['ranked'], 271 'approved_date' => now(), 272 ])->owner($owner)) 273 ->owner($owner) 274 ->create(); 275 276 $this->expectCountChange(fn () => BeatmapsetEvent::count(), 0); 277 $this->expectExceptionCallable( 278 fn () => (new ChangeBeatmapOwners($beatmap, [$user->getKey()], $user))->handle(), 279 AuthorizationException::class, 280 ); 281 282 $beatmap = $beatmap->fresh(); 283 $this->assertEqualsCanonicalizing([$owner->getKey()], $beatmap->getOwners()->pluck('user_id')->toArray()); 284 $this->assertSame($owner->getKey(), $beatmap->user_id); 285 286 Bus::assertNotDispatched(BeatmapOwnerChange::class); 287 } 288 289 public function testUpdateOwnerSameOwner(): void 290 { 291 $owner = User::factory()->create(); 292 $beatmap = Beatmap::factory() 293 ->for(Beatmapset::factory()->pending()->owner($owner)) 294 ->owner($owner) 295 ->create(); 296 297 $this->expectCountChange(fn () => BeatmapsetEvent::count(), 0); 298 (new ChangeBeatmapOwners($beatmap, [$owner->getKey()], $owner))->handle(); 299 300 $beatmap = $beatmap->fresh(); 301 $this->assertEqualsCanonicalizing([$owner->getKey()], $beatmap->getOwners()->pluck('user_id')->toArray()); 302 $this->assertSame($owner->getKey(), $beatmap->user_id); 303 304 Bus::assertNotDispatched(BeatmapOwnerChange::class); 305 } 306 307 protected function setUp(): void 308 { 309 parent::setUp(); 310 311 Bus::fake([BeatmapOwnerChange::class]); 312 } 313}