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\Build;
12use App\Models\ScoreToken;
13use App\Models\User;
14use Tests\TestCase;
15
16class ScoreTokensControllerTest extends TestCase
17{
18 private Build $build;
19 private User $user;
20
21 /**
22 * @dataProvider dataProviderForTestStore
23 */
24 public function testStore(string $beatmapState, int $status): void
25 {
26 $beatmap = Beatmap::factory()->$beatmapState()->create();
27
28 $routeParams = [
29 'beatmap' => $beatmap->getKey(),
30 'ruleset_id' => $beatmap->playmode,
31 ];
32 $bodyParams = ['beatmap_hash' => $beatmap->checksum];
33 $this->withHeaders(['x-token' => static::createClientToken($this->build)]);
34
35 $this->expectCountChange(fn () => ScoreToken::count(), $status >= 200 && $status < 300 ? 1 : 0);
36
37 $this->actAsScopedUser($this->user, ['*']);
38 $this->json(
39 'POST',
40 route('api.beatmaps.solo.score-tokens.store', $routeParams),
41 $bodyParams
42 )->assertStatus($status);
43 }
44
45 /**
46 * @dataProvider dataProviderForTestStoreInvalidParameter
47 */
48 public function testStoreInvalidParameter(string $paramKey, ?string $paramValue, int $status, string $errorMessage): void
49 {
50 $origClientCheckVersion = $GLOBALS['cfg']['osu']['client']['check_version'];
51 config_set('osu.client.check_version', true);
52 $beatmap = Beatmap::factory()->ranked()->create();
53
54 $this->actAsScopedUser($this->user, ['*']);
55
56 $params = [
57 'beatmap' => $beatmap->getKey(),
58 'ruleset_id' => $beatmap->playmode,
59 'beatmap_hash' => $beatmap->checksum,
60 ];
61 $this->withHeaders([
62 'x-token' => $paramKey === 'client_token'
63 ? $paramValue
64 : static::createClientToken($this->build),
65 ]);
66
67 if ($paramKey !== 'client_token') {
68 $params[$paramKey] = $paramValue;
69 }
70
71 $routeParams = [
72 'beatmap' => $params['beatmap'],
73 'ruleset_id' => $params['ruleset_id'],
74 ];
75 $bodyParams = [
76 'beatmap_hash' => $params['beatmap_hash'],
77 ];
78
79 $this->expectCountChange(fn () => ScoreToken::count(), 0);
80
81 $this->json(
82 'POST',
83 route('api.beatmaps.solo.score-tokens.store', $routeParams),
84 $bodyParams
85 )->assertStatus($status)
86 ->assertJson([
87 'error' => $errorMessage,
88 ]);
89
90 config_set('osu.client.check_version', $origClientCheckVersion);
91 }
92
93 public function testStoreValidRulesetConversion(): void
94 {
95 $beatmap = Beatmap::factory()->create([
96 'playmode' => 0,
97 ]);
98
99 $routeParams = [
100 'beatmap' => $beatmap->getKey(),
101 'ruleset_id' => 1,
102 ];
103 $bodyParams = ['beatmap_hash' => $beatmap->checksum];
104 $this->withHeaders(['x-token' => static::createClientToken($this->build)]);
105
106 $this->expectCountChange(fn () => ScoreToken::count(), 1);
107
108 $this->actAsScopedUser($this->user, ['*']);
109 $this->json(
110 'POST',
111 route('api.beatmaps.solo.score-tokens.store', $routeParams),
112 $bodyParams
113 )->assertStatus(200);
114 }
115
116 public function testStoreInvalidRulesetConversion(): void
117 {
118 $beatmap = Beatmap::factory()->create([
119 'playmode' => 2,
120 ]);
121
122 $routeParams = [
123 'beatmap' => $beatmap->getKey(),
124 'ruleset_id' => 1,
125 ];
126 $bodyParams = ['beatmap_hash' => $beatmap->checksum];
127 $this->withHeaders(['x-token' => static::createClientToken($this->build)]);
128
129 $this->expectCountChange(fn () => ScoreToken::count(), 0);
130
131 $this->actAsScopedUser($this->user, ['*']);
132 $this->json(
133 'POST',
134 route('api.beatmaps.solo.score-tokens.store', $routeParams),
135 $bodyParams
136 )->assertStatus(422)
137 ->assertJson([
138 'error' => 'invalid ruleset_id',
139 ]);
140 }
141
142 public static function dataProviderForTestStore(): array
143 {
144 return [
145 ['deleted', 404],
146 ['deletedBeatmapset', 404],
147 ['inactive', 404],
148 ['ranked', 200],
149 ['wip', 200],
150 ];
151 }
152
153 public static function dataProviderForTestStoreInvalidParameter(): array
154 {
155 return [
156 'invalid client token' => ['client_token', md5('invalid_'), 422, 'invalid client hash'],
157 'missing client token' => ['client_token', null, 422, 'missing token header'],
158
159 'invalid ruleset id' => ['ruleset_id', '5', 422, 'invalid ruleset_id'],
160 'missing ruleset id' => ['ruleset_id', null, 422, 'missing ruleset_id'],
161
162 'invalid beatmap hash' => ['beatmap_hash', 'xxx', 422, 'invalid or missing beatmap_hash'],
163 'missing beatmap hash' => ['beatmap_hash', null, 422, 'invalid or missing beatmap_hash'],
164 ];
165 }
166
167 protected function setUp(): void
168 {
169 parent::setUp();
170
171 $this->user = User::factory()->create();
172 $this->build = Build::factory()->create(['allow_ranking' => true]);
173 }
174}