the browser-facing portion of osu!
at master 8.9 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\Controllers; 9 10use App\Models\Beatmap; 11use App\Models\ScorePin; 12use App\Models\Solo\Score; 13use App\Models\User; 14use Tests\TestCase; 15 16class ScorePinsControllerTest extends TestCase 17{ 18 private static function createScore(?User $user = null, ?int $rulesetId = null, ?bool $passed = null): Score 19 { 20 if ($rulesetId !== null) { 21 $params['ruleset_id'] = $rulesetId; 22 } 23 if ($user !== null) { 24 $params['user_id'] = $user; 25 } 26 $params['passed'] = $passed ?? true; 27 28 return Score::factory()->create($params); 29 } 30 31 private static function makeParams(Score $score): array 32 { 33 return [ 34 'score_id' => $score->getKey(), 35 ]; 36 } 37 38 public function testDestroy() 39 { 40 $pin = ScorePin::factory()->withScore(static::createScore())->create(); 41 42 $this->expectCountChange(fn () => ScorePin::count(), -1); 43 44 $this->actAsUser($pin->user, true); 45 46 $this 47 ->delete(route('score-pins.destroy', static::makeParams($pin->score))) 48 ->assertSuccessful(); 49 } 50 51 public function testDestroyAsDifferentUser() 52 { 53 $pin = ScorePin::factory()->withScore(static::createScore())->create(); 54 $otherUser = User::factory()->create(); 55 56 $this->expectCountChange(fn () => ScorePin::count(), 0); 57 58 $this->actAsUser($otherUser, true); 59 60 $this 61 ->delete(route('score-pins.destroy', static::makeParams($pin->score))) 62 ->assertSuccessful(); 63 } 64 65 public function testDestroyAsGuest() 66 { 67 $pin = ScorePin::factory()->withScore(static::createScore())->create(); 68 69 $this->expectCountChange(fn () => ScorePin::count(), 0); 70 71 $this 72 ->delete(route('score-pins.destroy', static::makeParams($pin->score))) 73 ->assertStatus(401); 74 } 75 76 // moving: [0]. expected order: [1] < [0] 77 public function testReorderMoveBottom() 78 { 79 $user = User::factory()->create(); 80 $pins = collect([0, 1])->map(fn ($order) => ScorePin 81 ::factory(['display_order' => $order]) 82 ->withScore(static::createScore($user, Beatmap::MODES['osu'])) 83 ->create()); 84 85 $this->actAsUser($user, true); 86 $this 87 ->put(route('score-pins.reorder'), [ 88 ...static::makeParams($pins[0]->score), 89 'order1' => static::makeParams($pins[1]->score), 90 ])->assertSuccessful(); 91 92 $pins->map->refresh(); 93 $this->assertTrue($pins[1]->display_order < $pins[0]->display_order); 94 } 95 96 // moving: [0]. expected order: [1] < [0] < [2] 97 public function testReorderMoveDown() 98 { 99 $user = User::factory()->create(); 100 $pins = collect([0, 1, 2])->map(fn ($order) => ScorePin 101 ::factory(['display_order' => $order]) 102 ->withScore(static::createScore($user, Beatmap::MODES['osu'])) 103 ->create()); 104 105 $this->actAsUser($user, true); 106 $this 107 ->put(route('score-pins.reorder'), [ 108 ...static::makeParams($pins[0]->score), 109 'order1' => static::makeParams($pins[1]->score), 110 ])->assertSuccessful(); 111 112 $pins->map->refresh(); 113 $this->assertTrue($pins[1]->display_order < $pins[0]->display_order); 114 $this->assertTrue($pins[0]->display_order < $pins[2]->display_order); 115 } 116 117 // moving: [1]. expected order: [1] < [0] 118 public function testReorderMoveTop() 119 { 120 $user = User::factory()->create(); 121 $pins = collect([0, 1])->map(fn ($order) => ScorePin 122 ::factory(['display_order' => $order]) 123 ->withScore(static::createScore($user, Beatmap::MODES['osu'])) 124 ->create()); 125 126 $this->actAsUser($user, true); 127 $this 128 ->put(route('score-pins.reorder'), [ 129 ...static::makeParams($pins[1]->score), 130 'order3' => static::makeParams($pins[0]->score), 131 ])->assertSuccessful(); 132 133 $pins->map->refresh(); 134 $this->assertTrue($pins[1]->display_order < $pins[0]->display_order); 135 } 136 137 // moving: [2]. expected order: [0] < [2] < [1] 138 public function testReorderMoveUp() 139 { 140 $user = User::factory()->create(); 141 $pins = collect([0, 1, 2])->map(fn ($order) => ScorePin 142 ::factory(['display_order' => $order]) 143 ->withScore(static::createScore($user, Beatmap::MODES['osu'])) 144 ->create()); 145 146 $this->actAsUser($user, true); 147 $this 148 ->put(route('score-pins.reorder'), [ 149 ...static::makeParams($pins[2]->score), 150 'order1' => static::makeParams($pins[0]->score), 151 ])->assertSuccessful(); 152 153 $pins->map->refresh(); 154 $this->assertTrue($pins[0]->display_order < $pins[2]->display_order); 155 $this->assertTrue($pins[2]->display_order < $pins[1]->display_order); 156 } 157 158 public function testStore() 159 { 160 $score = static::createScore(); 161 162 $this->expectCountChange(fn () => $score->user->scorePins()->count(), 1); 163 164 $this->actAsUser($score->user, true); 165 166 $this 167 ->post(route('score-pins.store'), static::makeParams($score)) 168 ->assertSuccessful(); 169 } 170 171 public function testStoreAsGuest() 172 { 173 $score = static::createScore(); 174 175 $this->expectCountChange(fn () => ScorePin::count(), 0); 176 177 $this 178 ->post(route('score-pins.store'), static::makeParams($score)) 179 ->assertStatus(401); 180 } 181 182 public function testStoreAsNonOwner() 183 { 184 $score = static::createScore(); 185 $otherUser = User::factory()->create(); 186 187 $this->expectCountChange(fn () => ScorePin::count(), 0); 188 189 $this->actAsUser($otherUser, true); 190 191 $this 192 ->post(route('score-pins.store'), static::makeParams($score)) 193 ->assertStatus(403); 194 } 195 196 // new score pin should always be above existing ones 197 public function testStoreDisplayOrder() 198 { 199 $user = User::factory()->create([ 200 'osu_subscriber' => false, 201 ]); 202 $score1 = static::createScore($user, Beatmap::MODES['osu']); 203 $score2 = static::createScore($user, Beatmap::MODES['osu']); 204 $pin1 = ScorePin::factory()->withScore($score1)->create(); 205 206 $this->actAsUser($user, true); 207 208 $this 209 ->post(route('score-pins.store'), static::makeParams($score2)) 210 ->assertSuccessful(); 211 212 $pin2 = $user->scorePins()->find($score2->getKey()); 213 $this->assertTrue($pin1->display_order > $pin2->display_order); 214 } 215 216 public function testStoreDuplicate() 217 { 218 $score = static::createScore(); 219 $pin = ScorePin::factory()->withScore($score)->create(); 220 221 $this->expectCountChange(fn () => ScorePin::count(), 0); 222 223 $this->actAsUser($score->user, true); 224 225 $this 226 ->post(route('score-pins.store'), static::makeParams($score)) 227 ->assertSuccessful(); 228 } 229 230 public function testStoreInvalidScoreId() 231 { 232 Score::whereKey(1)->delete(); 233 234 $this->expectCountChange(fn () => ScorePin::count(), 0); 235 236 $this->actAsUser(User::factory()->create(), true); 237 238 $this 239 ->post(route('score-pins.store'), ['score_id' => 1]) 240 ->assertStatus(422); 241 } 242 243 public function testStoreLimit() 244 { 245 config_set('osu.user.max_score_pins', 1); 246 247 $user = User::factory()->create([ 248 'osu_subscriber' => false, 249 ]); 250 $score1 = static::createScore($user, Beatmap::MODES['osu']); 251 $score2 = static::createScore($user, Beatmap::MODES['osu']); 252 $pin1 = ScorePin::factory()->withScore($score1)->create(); 253 254 $this->expectCountChange(fn () => ScorePin::count(), 0); 255 256 $this->actAsUser($user, true); 257 258 $this 259 ->post(route('score-pins.store'), static::makeParams($score2)) 260 ->assertStatus(403); 261 } 262 263 public function testStoreLimitDifferentMode() 264 { 265 config_set('osu.user.max_score_pins', 1); 266 267 $user = User::factory()->create([ 268 'osu_subscriber' => false, 269 ]); 270 $score1 = static::createScore($user, Beatmap::MODES['osu']); 271 $score2 = static::createScore($user, Beatmap::MODES['taiko']); 272 $pin1 = ScorePin::factory()->withScore($score1)->create(); 273 274 $this->expectCountChange(fn () => ScorePin::count(), 1); 275 276 $this->actAsUser($user, true); 277 278 $this 279 ->post(route('score-pins.store'), static::makeParams($score2)) 280 ->assertSuccessful(); 281 } 282}