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;
9
10use App\Models\BeatmapDifficulty;
11use App\Models\BeatmapDifficultyAttrib;
12use App\Models\BeatmapFailtimes;
13use App\Models\BeatmapTag;
14use App\Models\Chat;
15use App\Models\FavouriteBeatmapset;
16use App\Models\Forum;
17use App\Models\LegacyMatch;
18use App\Models\UserAchievement;
19use App\Models\UserClient;
20use App\Models\UserCountryHistory;
21use App\Models\UserDonation;
22use App\Models\UserGroup;
23use App\Models\UserRelation;
24use App\Models\UserReplaysWatchedCount;
25use Carbon\Carbon;
26use Tests\TestCase;
27
28class ModelCompositePrimaryKeysTest extends TestCase
29{
30 /**
31 * @dataProvider dataProviderBase
32 */
33 public function testDelete(string $class, array $baseParams, array $item2Params, array $check)
34 {
35 [$item1, $item2] = $this->createModels($class, $baseParams, $item2Params, $check);
36
37 $this->expectCountChange(fn () => $class::count(), -1);
38 $item1->delete();
39 $this->assertNull($item1->fresh());
40 $this->assertNotNull($item2->fresh());
41 }
42
43 /**
44 * @dataProvider dataProviderBase
45 */
46 public function testFresh(string $class, array $baseParams, array $item2Params, array $check)
47 {
48 [$item1, $item2] = $this->createModels($class, $baseParams, $item2Params, $check);
49
50 $this->assertSame(':composite', $item1->getKeyName());
51 $this->assertFalse($item1->incrementing);
52
53 $key = $check[0];
54 $cast = $this->getCast($check[2]);
55 $this->assertSame($cast($check[1][0]), $cast($item1->fresh()->$key));
56 $this->assertSame($cast($check[2]), $cast($item2->fresh()->$key));
57 }
58
59 /**
60 * @dataProvider dataProviderBase
61 */
62 public function testUpdate(string $class, array $baseParams, array $item2Params, array $check)
63 {
64 [$item1, $item2] = $this->createModels($class, $baseParams, $item2Params, $check);
65
66 $key = $check[0];
67 $newValue = $check[1][1];
68 $item1->update([$key => $newValue]);
69 $cast = $this->getCast($check[2]);
70 $this->assertSame($cast($newValue), $cast($item1->fresh()->$key));
71 $this->assertSame($cast($check[2]), $cast($item2->fresh()->$key));
72 }
73
74 public static function dataProviderBase()
75 {
76 // 0: class name
77 // 1: base params
78 // 2: base params for item 2
79 // 3:
80 // 0: check column
81 // 1: item 1 value
82 // 0: initial value
83 // 1: update value
84 // 2: item 2 value
85 return [
86 [
87 BeatmapDifficulty::class,
88 [
89 'beatmap_id' => 0,
90 'mode' => 0,
91 'mods' => 0,
92 ],
93 ['beatmap_id' => 1],
94 ['diff_unified', [0.0, 10.0], 11.0],
95 ],
96 [
97 BeatmapDifficultyAttrib::class,
98 [
99 'attrib_id' => 0,
100 'beatmap_id' => 0,
101 'mode' => 0,
102 'mods' => 0,
103 ],
104 ['beatmap_id' => 1],
105 ['value', [0.0, 10.0], 11.0],
106 ],
107 [
108 BeatmapFailtimes::class,
109 [
110 'beatmap_id' => 0,
111 'type' => 'fail',
112 ],
113 ['type' => 'exit'],
114 ['p1', [0, 10], 11],
115 ],
116 [
117 BeatmapTag::class,
118 [
119 'beatmap_id' => 0,
120 'tag_id' => 0,
121 'user_id' => 0,
122 ],
123 ['tag_id' => 1],
124 ['updated_at', [Carbon::now()->subDays(5), Carbon::now()->subDays(1)], Carbon::now()],
125 ],
126 [
127 Chat\UserChannel::class,
128 [
129 'channel_id' => 0,
130 'user_id' => 0,
131 ],
132 ['channel_id' => 1],
133 ['last_read_id', [1, 2], 3],
134 ],
135 [
136 FavouriteBeatmapset::class,
137 [
138 'beatmapset_id' => 0,
139 'user_id' => 0,
140 ],
141 ['beatmapset_id' => 2],
142 ['dateadded', [Carbon::now()->subDays(5), Carbon::now()->subDays(1)], Carbon::now()],
143 ],
144 [
145 Forum\AuthRole::class,
146 [
147 'auth_option_id' => 0,
148 'role_id' => 0,
149 ],
150 ['role_id' => 1],
151 ['auth_setting', [0, 1], 2],
152 ],
153 [
154 Forum\Authorize::class,
155 [
156 'auth_option_id' => 0,
157 'auth_role_id' => 0,
158 'forum_id' => 0,
159 'group_id' => 0,
160 ],
161 [],
162 ['auth_setting', [0, 1], 2],
163 ],
164 [
165 Forum\ForumTrack::class,
166 [
167 'forum_id' => 0,
168 ],
169 [],
170 ['user_id', [0, 1], 2],
171 ],
172 [
173 Forum\TopicTrack::class,
174 [
175 'topic_id' => 0,
176 ],
177 [],
178 ['user_id', [0, 1], 2],
179 ],
180 [
181 Forum\TopicWatch::class,
182 [
183 'topic_id' => 0,
184 ],
185 [],
186 ['user_id', [0, 1], 2],
187 ],
188 [
189 LegacyMatch\Score::class,
190 [
191 'game_id' => 0,
192 'slot' => 0,
193 'user_id' => 0,
194 ],
195 ['slot' => 1],
196 ['score', [10, 20], 30],
197 ],
198 [
199 UserAchievement::class,
200 [
201 'user_id' => 0,
202 'achievement_id' => 0,
203 ],
204 ['user_id' => 1],
205 ['beatmap_id', [1, 2], 3],
206 ],
207 [
208 UserClient::class,
209 [
210 'osu_md5' => md5('', true),
211 'unique_md5' => md5('', true),
212 'disk_md5' => md5('', true),
213 ],
214 [],
215 ['user_id', [0, 1], 2],
216 ],
217 [
218 UserCountryHistory::class,
219 [
220 'user_id' => 0,
221 'year_month' => '2301',
222 'country_acronym' => 'JP',
223 ],
224 ['user_id' => 1],
225 ['count', [1, 2], 3],
226 ],
227 [
228 UserDonation::class,
229 [
230 'user_id' => 0,
231 'transaction_id' => '',
232 'amount' => 1,
233 ],
234 ['user_id' => 1],
235 ['length', [1, 2], 3],
236 ],
237 [
238 UserGroup::class,
239 [
240 'user_id' => 0,
241 'group_id' => 0,
242 ],
243 ['user_id' => 1],
244 ['group_leader', [true, false], true],
245 ],
246 [
247 UserRelation::class,
248 [
249 'user_id' => 0,
250 ],
251 [],
252 ['zebra_id', [1, 2], 3],
253 ],
254 [
255 UserReplaysWatchedCount::class,
256 [
257 'user_id' => 0,
258 'year_month' => '0101',
259 ],
260 ['user_id' => 1],
261 ['count', [0, 1], 2],
262 ],
263 ];
264 }
265
266 private function createModels(string $class, array $baseParams, array $item2Params, array $check)
267 {
268 return [
269 $class::create(array_merge($baseParams, [$check[0] => $check[1][0]])),
270 $class::create(array_merge($baseParams, $item2Params, [$check[0] => $check[2]])),
271 ];
272 }
273
274 private function getCast(mixed $value): callable
275 {
276 return $value instanceof Carbon
277 ? fn (Carbon $value): string => json_time($value)
278 : fn (mixed $value): mixed => $value;
279 }
280}