the browser-facing portion of osu!
at master 7.2 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\Beatmapset; 12use App\Models\User; 13use Illuminate\Testing\Fluent\AssertableJson; 14use Tests\TestCase; 15 16class BeatmapsControllerTest extends TestCase 17{ 18 private $user; 19 private $beatmap; 20 21 /** 22 * @group RequiresBeatmapDifficultyLookupCache 23 */ 24 public function testAttributes(): void 25 { 26 $beatmap = $this->createExistingOsuBeatmap(); 27 28 $this->actAsScopedUser(User::factory()->create(), ['public']); 29 30 $this->post(route('api.beatmaps.attributes', ['beatmap' => $beatmap->getKey()]), [ 31 'mods' => 1, 32 ]) 33 ->assertSuccessful() 34 ->assertJson(fn (AssertableJson $json) => 35 $json 36 ->has('attributes.star_rating') 37 ->has('attributes.max_combo') 38 ->etc()); 39 } 40 41 public function testAttributesInvalidRuleset(): void 42 { 43 $beatmap = $this->createExistingOsuBeatmap(); 44 45 $this->actAsScopedUser(User::factory()->create(), ['public']); 46 47 $this->post(route('api.beatmaps.attributes', ['beatmap' => $beatmap->getKey(), 'ruleset' => 'invalid'])) 48 ->assertStatus(422); 49 } 50 51 public function testAttributesInvalidRulesetId(): void 52 { 53 $beatmap = $this->createExistingOsuBeatmap(); 54 55 $this->actAsScopedUser(User::factory()->create(), ['public']); 56 57 $this->post(route('api.beatmaps.attributes', ['beatmap' => $beatmap->getKey(), 'ruleset_id' => 1000])) 58 ->assertStatus(422); 59 } 60 61 public function testAttributesInvalidConversion(): void 62 { 63 $beatmap = $this->createExistingFruitsBeatmap(); 64 65 $this->actAsScopedUser(User::factory()->create(), ['public']); 66 67 $this->post(route('api.beatmaps.attributes', ['beatmap' => $beatmap->getKey(), 'ruleset' => 'mania'])) 68 ->assertStatus(422); 69 } 70 71 public function testIndexForApi(): void 72 { 73 $beatmap = Beatmap::factory()->create(); 74 $beatmapB = Beatmap::factory()->create(); 75 $beatmapC = Beatmap::factory()->create(); 76 $beatmapC->beatmapset->update(['active' => false]); 77 78 $this->actAsScopedUser(User::factory()->create(), ['*']); 79 80 $this 81 ->get(route('api.beatmaps.index', ['ids' => [$beatmap->getKey(), $beatmapB->getKey(), $beatmapC->getKey()]])) 82 ->assertSuccessful() 83 ->assertJson(fn (AssertableJson $json) => 84 $json 85 ->where('beatmaps.0.id', $beatmap->getKey()) 86 ->where('beatmaps.1.id', $beatmapB->getKey()) 87 ->missing('beatmaps.2') 88 ->etc()); 89 } 90 91 public function testIndexForApiMissingParameter(): void 92 { 93 $this->actAsScopedUser(User::factory()->create(), ['*']); 94 95 $this 96 ->get(route('api.beatmaps.index')) 97 ->assertSuccessful(); 98 } 99 100 public function testInvalidMode() 101 { 102 $this->json('GET', route('beatmaps.scores', $this->beatmap), [ 103 'mode' => 'nope', 104 ])->assertStatus(422); 105 } 106 107 /** 108 * @dataProvider dataProviderForTestLookupForApi 109 */ 110 public function testLookupForApi(string $key, callable $valueFn): void 111 { 112 $beatmap = Beatmap::factory()->create(); 113 114 $this->actAsScopedUser(User::factory()->create(), ['*']); 115 116 $this 117 ->get(route('api.beatmaps.lookup', [$key => $valueFn($beatmap)])) 118 ->assertSuccessful() 119 ->assertJsonPath('id', $beatmap->getKey()); 120 } 121 122 /** 123 * Make sure the lookup stops when finding beatmap from one of the parameters 124 */ 125 public function testLookupMultipleParamsForApi(): void 126 { 127 $beatmap = Beatmap::factory()->create(); 128 129 $this->actAsScopedUser(User::factory()->create(), ['*']); 130 131 $this 132 ->get(route('api.beatmaps.lookup', [ 133 'checksum' => '', 134 'id' => (string) $beatmap->getKey(), 135 'filename' => '', 136 ])) 137 ->assertSuccessful() 138 ->assertJsonPath('id', $beatmap->getKey()); 139 } 140 141 /** 142 * Checks whether HTTP 403 is thrown when a logged out 143 * user tries to access the non-general (country or friend ranking) 144 * scoreboards. 145 */ 146 public function testScoresNonGeneralLoggedOut() 147 { 148 $this->json('GET', route('beatmaps.scores', $this->beatmap), [ 149 'type' => 'country', 150 ])->assertStatus(422) 151 ->assertJson(['error' => osu_trans('errors.supporter_only')]); 152 } 153 154 /** 155 * Checks whether an error is thrown when an user without supporter 156 * tries to access supporter-only scoreboards. 157 */ 158 public function testScoresNonGeneralSupporter() 159 { 160 $this->actingAs($this->user) 161 ->json('GET', route('beatmaps.scores', $this->beatmap), [ 162 'type' => 'country', 163 ])->assertStatus(422) 164 ->assertJson(['error' => osu_trans('errors.supporter_only')]); 165 166 $this->user->osu_subscriber = true; 167 $this->user->save(); 168 169 $this->actingAs($this->user) 170 ->json('GET', route('beatmaps.scores', $this->beatmap), [ 171 'type' => 'country', 172 ])->assertStatus(200); 173 } 174 175 public function testShowForApi() 176 { 177 $beatmap = Beatmap::factory()->create(); 178 179 $this->actAsScopedUser(User::factory()->create(), ['*']); 180 181 $this 182 ->get(route('api.beatmaps.show', ['beatmap' => $beatmap->getKey()])) 183 ->assertSuccessful() 184 ->assertJsonPath('id', $beatmap->getKey()); 185 } 186 187 public static function dataProviderForTestLookupForApi(): array 188 { 189 return [ 190 'checksum' => ['checksum', fn (Beatmap $b) => $b->checksum], 191 'filename' => ['filename', fn (Beatmap $b) => $b->filename], 192 'id' => ['id', fn (Beatmap $b) => $b->getKey()], 193 ]; 194 } 195 196 public static function dataProviderForTestUpdateOwnerLoved(): array 197 { 198 return [ 199 [Beatmapset::STATES['graveyard'], true], 200 [Beatmapset::STATES['loved'], true], 201 [Beatmapset::STATES['ranked'], false], 202 [Beatmapset::STATES['wip'], false], 203 ]; 204 } 205 206 protected function setUp(): void 207 { 208 parent::setUp(); 209 210 $this->user = User::factory()->create(); 211 $this->beatmap = Beatmap::factory()->qualified()->create(); 212 } 213 214 private function createExistingFruitsBeatmap() 215 { 216 return Beatmap::factory()->create([ 217 'beatmap_id' => 2177697, 218 'beatmapset_id' => Beatmapset::factory(['beatmapset_id' => 918591]), 219 'playmode' => Beatmap::MODES['fruits'], 220 ]); 221 } 222 223 private function createExistingOsuBeatmap() 224 { 225 return Beatmap::factory()->create([ 226 'beatmap_id' => 567606, 227 'beatmapset_id' => Beatmapset::factory(['beatmapset_id' => 246416]), 228 ]); 229 } 230}