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\Chat;
9
10use App\Models\Chat\Channel;
11use App\Models\OAuth\Client;
12use App\Models\User;
13use App\Models\UserRelation;
14use Faker;
15use Illuminate\Testing\Fluent\AssertableJson;
16use Tests\TestCase;
17
18class ChatControllerTest extends TestCase
19{
20 protected static $faker;
21
22 private User $anotherUser;
23 private User $user;
24
25 public static function setUpBeforeClass(): void
26 {
27 self::$faker = Faker\Factory::create();
28 }
29
30 //region POST /chat/new - Create New PM
31
32 /**
33 * @dataProvider createPmWithAuthorizedGrantDataProvider
34 */
35 public function testCreatePmWithAuthorizedGrant($scopes, $expectedStatus)
36 {
37 $this->actAsScopedUser($this->user, $scopes);
38 $this->json(
39 'POST',
40 route('api.chat.new'),
41 [
42 'target_id' => $this->anotherUser->user_id,
43 'message' => self::$faker->sentence(),
44 ]
45 )->assertStatus($expectedStatus);
46 }
47
48 /**
49 * @dataProvider createPmWithClientCredentialsDataProvider
50 */
51 public function testCreatePmWithClientCredentials($scopes, $expectedStatus)
52 {
53 $client = Client::factory()->create(['user_id' => $this->user]);
54 $this->actAsScopedUser(null, $scopes, $client);
55 $this->json(
56 'POST',
57 route('api.chat.new'),
58 [
59 'target_id' => $this->anotherUser->user_id,
60 'message' => self::$faker->sentence(),
61 ]
62 )->assertStatus($expectedStatus);
63 }
64
65 /**
66 * @dataProvider createPmWithClientCredentialsBotGroupDataProvider
67 */
68 public function testCreatePmWithClientCredentialsBotGroup($scopes, $expectedStatus)
69 {
70 $client = Client::factory()->create(['user_id' => $this->user]);
71 $this->user->update(['group_id' => app('groups')->byIdentifier('bot')->getKey()]);
72 $this->actAsScopedUser(null, $scopes, $client);
73 $this->json(
74 'POST',
75 route('api.chat.new'),
76 [
77 'target_id' => $this->anotherUser->user_id,
78 'message' => self::$faker->sentence(),
79 ]
80 )->assertStatus($expectedStatus);
81 }
82
83 public function testCreatePMWhenAlreadyExists() // success
84 {
85 $this->actAsScopedUser($this->user, ['*']);
86 $this->json(
87 'POST',
88 route('api.chat.new'),
89 [
90 'target_id' => $this->anotherUser->user_id,
91 'message' => self::$faker->sentence(),
92 ]
93 )->assertStatus(200);
94
95 // should return existing conversation and not error
96 $this->json(
97 'POST',
98 route('api.chat.new'),
99 [
100 'target_id' => $this->anotherUser->user_id,
101 'message' => self::$faker->sentence(),
102 ]
103 )->assertStatus(200);
104 }
105
106 public function testCreatePMWhenLeftChannel() // success
107 {
108 $this->actAsScopedUser($this->user, ['*']);
109 $request = $this->json(
110 'POST',
111 route('api.chat.new'),
112 [
113 'target_id' => $this->anotherUser->user_id,
114 'message' => self::$faker->sentence(),
115 ]
116 );
117
118 $channelId = $request->json('new_channel_id');
119 $request->assertSuccessful();
120
121 $this->json(
122 'DELETE',
123 route('api.chat.channels.part', [
124 'channel' => $channelId,
125 'user' => $this->user->user_id,
126 ])
127 )->assertSuccessful();
128
129 $this->json(
130 'POST',
131 route('api.chat.new'),
132 [
133 'target_id' => $this->anotherUser->user_id,
134 'message' => self::$faker->sentence(),
135 ]
136 )->assertSuccessful();
137 }
138
139 public function testCreatePMWhenGuest() // fail
140 {
141 $this->json(
142 'POST',
143 route('api.chat.new'),
144 [
145 'target_id' => $this->anotherUser->user_id,
146 'message' => self::$faker->sentence(),
147 ]
148 )->assertStatus(401);
149 }
150
151 public function testCreatePMWhenBlocked() // fail
152 {
153 UserRelation::factory()->block()->create([
154 'user_id' => $this->anotherUser,
155 'zebra_id' => $this->user,
156 ]);
157
158 $this->actAsScopedUser($this->user, ['*']);
159 $this->json(
160 'POST',
161 route('api.chat.new'),
162 [
163 'target_id' => $this->anotherUser->user_id,
164 'message' => self::$faker->sentence(),
165 ]
166 )->assertStatus(403);
167 }
168
169 public function testCreatePMWhenRestricted() // fail
170 {
171 $restrictedUser = User::factory()->restricted()->create();
172
173 $this->actAsScopedUser($restrictedUser, ['*']);
174 $this->json(
175 'POST',
176 route('api.chat.new'),
177 [
178 'target_id' => $this->anotherUser->user_id,
179 'message' => self::$faker->sentence(),
180 ]
181 )->assertStatus(403);
182 }
183
184 public function testCreatePMWhenSilenced() // fail
185 {
186 $silencedUser = User::factory()->silenced()->create();
187
188 $this->actAsScopedUser($silencedUser, ['*']);
189 $this->json(
190 'POST',
191 route('api.chat.new'),
192 [
193 'target_id' => $this->anotherUser->user_id,
194 'message' => self::$faker->sentence(),
195 ]
196 )->assertStatus(403);
197 }
198
199 public function testCreatePMWhenTargetRestricted() // fail
200 {
201 $restrictedUser = User::factory()->restricted()->create();
202
203 $this->actAsScopedUser($this->user, ['*']);
204 $this->json(
205 'POST',
206 route('api.chat.new'),
207 [
208 'target_id' => $restrictedUser->user_id,
209 'message' => self::$faker->sentence(),
210 ]
211 )->assertStatus(422);
212 }
213
214 public function testCreatePMWithSelf() // fail
215 {
216 $this->actAsScopedUser($this->user, ['*']);
217 $this->json(
218 'POST',
219 route('api.chat.new'),
220 [
221 'target_id' => $this->user->user_id,
222 'message' => self::$faker->sentence(),
223 ]
224 )->assertStatus(422);
225 }
226
227 public function testCreatePMWhenFriendsOnlyAndNotFriended() // fail
228 {
229 $privateUser = User::factory()->create(['pm_friends_only' => true]);
230
231 $this->actAsScopedUser($this->user, ['*']);
232 $this->json(
233 'POST',
234 route('api.chat.new'),
235 [
236 'target_id' => $privateUser->user_id,
237 'message' => self::$faker->sentence(),
238 ]
239 )->assertStatus(403);
240 }
241
242 public function testCreatePMWhenFriendsOnlyAndFriended() // success
243 {
244 $privateUser = User::factory()->create(['pm_friends_only' => true]);
245 UserRelation::factory()->friend()->create([
246 'user_id' => $privateUser,
247 'zebra_id' => $this->user,
248 ]);
249
250 $this->actAsScopedUser($this->user, ['*']);
251 $this->json(
252 'POST',
253 route('api.chat.new'),
254 [
255 'target_id' => $privateUser->user_id,
256 'message' => self::$faker->sentence(),
257 ]
258 )->assertStatus(200);
259 }
260
261 //endregion
262
263 //region GET /chat/updates
264 public function testChatUpdatesWhenGuest()
265 {
266 $this->json('GET', route('api.chat.updates'))
267 ->assertStatus(401);
268 }
269
270 public function testChatUpdatesJoinChannel()
271 {
272 $publicChannel = Channel::factory()->type('public')->create();
273
274 // join channel
275 $this->actAsScopedUser($this->user, ['*']);
276 $this->json('PUT', route('api.chat.channels.join', [
277 'channel' => $publicChannel->channel_id,
278 'user' => $this->user->user_id,
279 ]))
280 ->assertSuccessful();
281
282 $this->json('GET', route('api.chat.updates'), ['since' => 0])
283 ->assertStatus(200)
284 ->assertJson(fn (AssertableJson $json) =>
285 $json->where('presence.0.channel_id', $publicChannel->getKey())
286 ->etc());
287 }
288
289 //endregion
290
291 public static function createPmWithAuthorizedGrantDataProvider()
292 {
293 return [
294 [['*'], 200],
295 // there's no test for bot because the test setup itself is expected to fail when setting the token.
296 [['public'], 403],
297 ];
298 }
299
300 public static function createPmWithClientCredentialsDataProvider()
301 {
302 return [
303 // TODO: need to add test that validates auth guard calls Token::validate
304 [['public'], 403],
305 ];
306 }
307
308 public static function createPmWithClientCredentialsBotGroupDataProvider()
309 {
310 return [
311 [['chat.write', 'delegate'], 200],
312 [['public'], 403],
313 ];
314 }
315
316 protected function setUp(): void
317 {
318 parent::setUp();
319
320 $this->user = User::factory()->withPlays()->create();
321 $this->anotherUser = User::factory()->create();
322 }
323}