the browser-facing portion of osu!
at master 250 lines 7.6 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\Models\Solo; 9 10use App\Exceptions\InvariantException; 11use App\Models\Solo\Score; 12use stdClass; 13use Tests\TestCase; 14 15class ScoreTest extends TestCase 16{ 17 public function testStatisticsStoredInCorrectCasing() 18 { 19 $score = Score::createFromJsonOrExplode([ 20 'accuracy' => 1, 21 'beatmap_id' => 1, 22 'ended_at' => json_time(now()), 23 'max_combo' => 100, 24 'mods' => [], 25 'passed' => true, 26 'rank' => 'S', 27 'ruleset_id' => 1, 28 'statistics' => ['Great' => 10, 'SmallTickHit' => 1], 29 'total_score' => 1000, 30 'user_id' => 1, 31 ]); 32 33 $score = $score->fresh(); 34 $this->assertSame(1, json_decode($score->getAttributes()['data'], true)['statistics']['small_tick_hit']); 35 $this->assertSame(1, $score->data->statistics->small_tick_hit); 36 } 37 38 public function testLegacyPassScoreSetsRank() 39 { 40 $score = Score::createFromJsonOrExplode([ 41 'accuracy' => 1, 42 'beatmap_id' => 1, 43 'ended_at' => json_time(now()), 44 'max_combo' => 100, 45 'mods' => [], 46 'passed' => true, 47 'rank' => 'S', 48 'ruleset_id' => 1, 49 'statistics' => ['great' => 10], 50 'total_score' => 1000, 51 'user_id' => 1, 52 ]); 53 54 $this->assertTrue($score->passed); 55 $this->assertSame($score->rank, 'S'); 56 57 $legacy = $score->makeLegacyEntry(); 58 59 $this->assertSame($legacy->rank, 'X'); 60 } 61 62 public function testLegacyFailScoreIsRankF() 63 { 64 $score = Score::createFromJsonOrExplode([ 65 'accuracy' => 1, 66 'beatmap_id' => 1, 67 'ended_at' => json_time(now()), 68 'max_combo' => 100, 69 'mods' => [], 70 'passed' => false, 71 'rank' => 'S', // lazer may send an incorrect rank for a failed score. 72 'ruleset_id' => 1, 73 'statistics' => ['great' => 10], 74 'total_score' => 1000, 75 'user_id' => 1, 76 ]); 77 78 $this->assertFalse($score->passed); 79 $this->assertSame($score->rank, 'F'); 80 81 $legacy = $score->makeLegacyEntry(); 82 83 $this->assertSame($legacy->rank, 'F'); 84 } 85 86 public function testLegacyScoreHitCounts() 87 { 88 $legacy = Score::createFromJsonOrExplode([ 89 'accuracy' => 1, 90 'beatmap_id' => 1, 91 'ended_at' => json_time(now()), 92 'max_combo' => 100, 93 'mods' => [], 94 'passed' => true, 95 'rank' => 'S', 96 'ruleset_id' => 0, 97 'statistics' => ['great' => 10, 'ok' => 20, 'meh' => 30, 'miss' => 40], 98 'total_score' => 1000, 99 'user_id' => 1, 100 ])->makeLegacyEntry(); 101 102 $this->assertSame($legacy->count300, 10); 103 $this->assertSame($legacy->count100, 20); 104 $this->assertSame($legacy->count50, 30); 105 $this->assertSame($legacy->countmiss, 40); 106 } 107 108 public function testLegacyScoreHitCountsFromStudlyCaseStatistics() 109 { 110 $legacy = Score::createFromJsonOrExplode([ 111 'accuracy' => 1, 112 'beatmap_id' => 1, 113 'ended_at' => json_time(now()), 114 'max_combo' => 100, 115 'mods' => [], 116 'passed' => true, 117 'rank' => 'S', 118 'ruleset_id' => 0, 119 'statistics' => ['Great' => 10, 'Ok' => 20, 'Meh' => 30, 'Miss' => 40], 120 'total_score' => 1000, 121 'user_id' => 1, 122 ])->makeLegacyEntry(); 123 124 $this->assertSame($legacy->count300, 10); 125 $this->assertSame($legacy->count100, 20); 126 $this->assertSame($legacy->count50, 30); 127 $this->assertSame($legacy->countmiss, 40); 128 } 129 130 public function testModsPropertyType() 131 { 132 $score = new Score([ 133 'beatmap_id' => 0, 134 'data' => [ 135 'mods' => [['acronym' => 'DT']], 136 ], 137 'ended_at' => json_time(now()), 138 'ruleset_id' => 0, 139 'user_id' => 0, 140 ]); 141 142 $this->assertTrue($score->data->mods[0] instanceof stdClass, 'mods entry should be of type stdClass'); 143 } 144 145 public function testWeightedPp(): void 146 { 147 $pp = 10; 148 $weight = 0.5; 149 $score = Score::factory()->create(['pp' => $pp]); 150 $score->weight = $weight; 151 152 $this->assertSame($score->weightedPp(), $pp * $weight); 153 } 154 155 public function testWeightedPpWithoutPerformance(): void 156 { 157 $score = Score::factory()->create(['pp' => null]); 158 $score->weight = 0.5; 159 160 $this->assertNull($score->weightedPp()); 161 } 162 163 public function testWeightedPpWithoutWeight(): void 164 { 165 $score = Score::factory()->create(['pp' => 10]); 166 167 $this->assertNull($score->weightedPp()); 168 } 169 170 public function testNegativeTotalScoreIsNotAccepted() 171 { 172 $this->expectException(InvariantException::class); 173 $this->expectExceptionMessage('Invalid total_score.'); 174 175 Score::createFromJsonOrExplode([ 176 'accuracy' => 1, 177 'beatmap_id' => 1, 178 'ended_at' => json_time(now()), 179 'max_combo' => 100, 180 'mods' => [], 181 'passed' => true, 182 'rank' => 'S', 183 'ruleset_id' => 1, 184 'statistics' => ['Great' => 10, 'SmallTickHit' => 1], 185 'total_score' => -1000, 186 'user_id' => 1, 187 ]); 188 } 189 190 public function testNegativeAccuracyIsNotAccepted() 191 { 192 $this->expectException(InvariantException::class); 193 $this->expectExceptionMessage('Invalid accuracy.'); 194 195 Score::createFromJsonOrExplode([ 196 'accuracy' => -1, 197 'beatmap_id' => 1, 198 'ended_at' => json_time(now()), 199 'max_combo' => 100, 200 'mods' => [], 201 'passed' => true, 202 'rank' => 'S', 203 'ruleset_id' => 1, 204 'statistics' => ['Great' => 10, 'SmallTickHit' => 1], 205 'total_score' => 1000, 206 'user_id' => 1, 207 ]); 208 } 209 210 public function testAccuracyAboveOneIsNotAccepted() 211 { 212 $this->expectException(InvariantException::class); 213 $this->expectExceptionMessage('Invalid accuracy.'); 214 215 Score::createFromJsonOrExplode([ 216 'accuracy' => 2, 217 'beatmap_id' => 1, 218 'ended_at' => json_time(now()), 219 'max_combo' => 100, 220 'mods' => [], 221 'passed' => true, 222 'rank' => 'S', 223 'ruleset_id' => 1, 224 'statistics' => ['Great' => 10, 'SmallTickHit' => 1], 225 'total_score' => 1000, 226 'user_id' => 1, 227 ]); 228 } 229 230 public function testNegativeTotalScoreWithoutModsIsNotAccepted() 231 { 232 $this->expectException(InvariantException::class); 233 $this->expectExceptionMessage('Invalid total_score_without_mods.'); 234 235 Score::createFromJsonOrExplode([ 236 'accuracy' => 1, 237 'beatmap_id' => 1, 238 'ended_at' => json_time(now()), 239 'max_combo' => 100, 240 'mods' => [], 241 'passed' => true, 242 'rank' => 'S', 243 'ruleset_id' => 1, 244 'statistics' => ['Great' => 10, 'SmallTickHit' => 1], 245 'total_score' => 1000, 246 'total_score_without_mods' => -1000, 247 'user_id' => 1, 248 ]); 249 } 250}