the browser-facing portion of osu!
at master 8.5 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\Models\Multiplayer; 7 8use App\Exceptions\InvariantException; 9use App\Models\Beatmap; 10use App\Models\ChatFilter; 11use App\Models\Multiplayer\PlaylistItem; 12use App\Models\Multiplayer\Room; 13use App\Models\User; 14use Exception; 15use Tests\TestCase; 16 17class RoomTest extends TestCase 18{ 19 /** 20 * @dataProvider startGameDurationDataProvider 21 */ 22 public function testStartGameDuration(int $duration, bool $isSupporter, bool $expectException) 23 { 24 $beatmap = Beatmap::factory()->create(); 25 $user = User::factory(); 26 if ($isSupporter) { 27 $user = $user->supporter(); 28 } 29 30 $user = $user->create(); 31 32 $params = [ 33 'duration' => $duration, 34 'name' => 'test', 35 'playlist' => [ 36 [ 37 'beatmap_id' => $beatmap->getKey(), 38 'ruleset_id' => $beatmap->playmode, 39 ], 40 ], 41 ]; 42 43 if ($expectException) { 44 $this->expectException(InvariantException::class); 45 $this->expectExceptionMessage(osu_trans('multiplayer.room.errors.duration_too_long')); 46 $this->expectCountChange(fn () => Room::count(), 0); 47 } else { 48 $this->expectCountChange(fn () => Room::count(), 1); 49 } 50 51 $room = (new Room())->startGame($user, $params); 52 $this->assertTrue($room->exists); 53 } 54 55 public function testStartGameWithBeatmap() 56 { 57 $beatmap = Beatmap::factory()->create(); 58 $user = User::factory()->create(); 59 60 $params = [ 61 'duration' => 60, 62 'name' => 'test', 63 'playlist' => [ 64 [ 65 'beatmap_id' => $beatmap->getKey(), 66 'ruleset_id' => $beatmap->playmode, 67 ], 68 ], 69 ]; 70 71 $room = (new Room())->startGame($user, $params); 72 $this->assertTrue($room->exists); 73 } 74 75 public function testStartGameWithDeletedBeatmap() 76 { 77 $beatmap = Beatmap::factory()->create(['deleted_at' => now()]); 78 $user = User::factory()->create(); 79 80 $params = [ 81 'duration' => 60, 82 'name' => 'test', 83 'playlist' => [ 84 [ 85 'beatmap_id' => $beatmap->getKey(), 86 'ruleset_id' => $beatmap->playmode, 87 ], 88 ], 89 ]; 90 91 $this->expectException(InvariantException::class); 92 (new Room())->startGame($user, $params); 93 } 94 95 public function testStartGameWithInvalidRuleset() 96 { 97 $beatmap = Beatmap::factory()->create([ 98 'playmode' => 2, 99 ]); 100 $user = User::factory()->create(); 101 102 $params = [ 103 'duration' => 60, 104 'name' => 'test', 105 'playlist' => [ 106 [ 107 'beatmap_id' => $beatmap->getKey(), 108 'ruleset_id' => 0, 109 ], 110 ], 111 ]; 112 113 $this->expectException(InvariantException::class); 114 $this->expectExceptionMessageMatches('/^invalid ruleset_id for beatmap \d+$/'); 115 (new Room())->startGame($user, $params); 116 } 117 118 public function testRoomHasEnded() 119 { 120 $user = User::factory()->create(); 121 $room = Room::factory()->ended()->create(); 122 $playlistItem = PlaylistItem::factory()->create([ 123 'room_id' => $room, 124 ]); 125 126 $this->expectException(InvariantException::class); 127 static::roomStartPlay($user, $playlistItem); 128 } 129 130 public function testStartPlay(): void 131 { 132 $user = User::factory()->create(); 133 $room = Room::factory()->create(); 134 $playlistItem = PlaylistItem::factory()->create(['room_id' => $room]); 135 136 $this->expectCountChange(fn () => $room->participant_count, 1); 137 $this->expectCountChange(fn () => $room->userHighScores()->count(), 1); 138 $this->expectCountChange(fn () => $playlistItem->scoreTokens()->count(), 1); 139 140 static::roomStartPlay($user, $playlistItem); 141 $room->refresh(); 142 143 $this->assertSame($user->getKey(), $playlistItem->scoreTokens()->last()->user_id); 144 } 145 146 public function testMaxAttemptsReached() 147 { 148 $user = User::factory()->create(); 149 $room = Room::factory()->create(['max_attempts' => 2]); 150 $playlistItem1 = PlaylistItem::factory()->create(['room_id' => $room]); 151 $playlistItem2 = PlaylistItem::factory()->create(['room_id' => $room]); 152 153 static::roomStartPlay($user, $playlistItem1); 154 $this->assertTrue(true); 155 156 static::roomStartPlay($user, $playlistItem2); 157 $this->assertTrue(true); 158 159 $this->expectException(InvariantException::class); 160 static::roomStartPlay($user, $playlistItem1); 161 } 162 163 public function testMaxAttemptsForItemReached() 164 { 165 $user = User::factory()->create(); 166 $room = Room::factory()->create(); 167 $playlistItem1 = PlaylistItem::factory()->create([ 168 'room_id' => $room, 169 'max_attempts' => 1, 170 ]); 171 $playlistItem2 = PlaylistItem::factory()->create([ 172 'room_id' => $room, 173 'max_attempts' => 1, 174 ]); 175 176 $initialCount = $playlistItem1->scoreTokens()->count(); 177 static::roomStartPlay($user, $playlistItem1); 178 $this->assertSame($initialCount + 1, $playlistItem1->scoreTokens()->count()); 179 180 $initialCount = $playlistItem1->scoreTokens()->count(); 181 try { 182 static::roomStartPlay($user, $playlistItem1); 183 } catch (Exception $ex) { 184 $this->assertTrue($ex instanceof InvariantException); 185 } 186 $this->assertSame($initialCount, $playlistItem1->scoreTokens()->count()); 187 188 $initialCount = $playlistItem2->scoreTokens()->count(); 189 static::roomStartPlay($user, $playlistItem2); 190 $this->assertSame($initialCount + 1, $playlistItem2->scoreTokens()->count()); 191 } 192 193 public function testCannotStartPlayedItem() 194 { 195 $beatmap = Beatmap::factory()->create(); 196 $user = User::factory()->create(); 197 198 $params = [ 199 'name' => 'test', 200 'playlist' => [ 201 [ 202 'beatmap_id' => $beatmap->getKey(), 203 'ruleset_id' => $beatmap->playmode, 204 'played_at' => time(), 205 ], 206 ], 207 ]; 208 209 $this->expectException(InvariantException::class); 210 (new Room())->startGame($user, $params); 211 } 212 213 public function testNameFiltering() 214 { 215 ChatFilter::factory()->create([ 216 'match' => 'bad', 217 'replacement' => 'good', 218 ]); 219 $beatmap = Beatmap::factory()->create(); 220 $user = User::factory()->create(); 221 222 $params = [ 223 'name' => 'bad word', 224 'playlist' => [ 225 [ 226 'beatmap_id' => $beatmap->getKey(), 227 'ruleset_id' => $beatmap->playmode, 228 'played_at' => time(), 229 ], 230 ], 231 'type' => 'head_to_head', 232 ]; 233 234 $room = new Room(); 235 $room->startGame($user, $params); 236 $this->assertSame('good word', $room->name); 237 } 238 239 public static function startGameDurationDataProvider() 240 { 241 static $dayMinutes = 1440; 242 static::createApp(); 243 244 $maxDuration = $GLOBALS['cfg']['osu']['user']['max_multiplayer_duration']; 245 $maxDurationSupporter = $GLOBALS['cfg']['osu']['user']['max_multiplayer_duration_supporter']; 246 247 return [ 248 '2 weeks' => [$dayMinutes * $maxDuration, false, false], 249 '2 weeks (with supporter)' => [$dayMinutes * $maxDuration, true, false], 250 'more than 2 weeks' => [$dayMinutes * $maxDuration + 1, false, true], 251 'more than 2 weeks (with supporter)' => [$dayMinutes * $maxDuration + 1, true, false], 252 '3 months' => [$dayMinutes * $maxDurationSupporter, false, true], 253 '3 months (with supporter)' => [$dayMinutes * $maxDurationSupporter, true, false], 254 'more than 3 months' => [$dayMinutes * $maxDurationSupporter + 1, false, true], 255 'more than 3 months (with supporter)' => [$dayMinutes * $maxDurationSupporter + 1, true, true], 256 ]; 257 } 258}