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\Channels;
9
10use App\Models\Chat\Channel;
11use App\Models\Chat\UserChannel;
12use App\Models\ChatFilter;
13use App\Models\User;
14use App\Models\UserRelation;
15use Faker;
16use Tests\TestCase;
17
18class MessagesControllerTest extends TestCase
19{
20 protected static $faker;
21
22 private User $anotherUser;
23 private Channel $privateChannel;
24 private Channel $publicChannel;
25 private User $restrictedUser;
26 private User $silencedUser;
27 private Channel $tourneyChannel;
28 private User $user;
29
30 public static function setUpBeforeClass(): void
31 {
32 self::$faker = Faker\Factory::create();
33 }
34
35 //region GET /chat/channels/[channel_id]/messages - Get Channel Messages (public)
36 public function testChannelShowPublicWhenGuest() // fail
37 {
38 $this->json('GET', route('api.chat.channels.messages.index', ['channel' => $this->publicChannel->channel_id]))
39 ->assertStatus(401);
40 }
41
42 public function testChannelShowPublicWhenUnjoined() // fail
43 {
44 $this->actAsScopedUser($this->user, ['*']);
45 $this->json('GET', route('api.chat.channels.messages.index', ['channel' => $this->publicChannel->channel_id]))
46 ->assertStatus(404);
47 }
48
49 public function testChannelShowPublicWhenJoined() // success
50 {
51 $this->actAsScopedUser($this->user, ['*']);
52 $this->json('PUT', route('api.chat.channels.join', [
53 'channel' => $this->publicChannel->channel_id,
54 'user' => $this->user->user_id,
55 ]));
56
57 $this->actAsScopedUser($this->user, ['*']);
58 $this->json('GET', route('api.chat.channels.messages.index', ['channel' => $this->publicChannel->channel_id]))
59 ->assertStatus(200);
60 // TODO: Add check for messages being present?
61 }
62
63 //endregion
64
65 //region GET /chat/channels/[channel_id]/messages - Get Channel Messages (tourney)
66 public function testChannelShowTourneyWhenGuest() // fail
67 {
68 $this->json('GET', route('api.chat.channels.messages.index', ['channel' => $this->tourneyChannel->channel_id]))
69 ->assertStatus(401);
70 }
71
72 public function testChannelShowTourneyWhenUnjoined() // fail
73 {
74 $this->actAsScopedUser($this->user, ['*']);
75 $this->json('GET', route('api.chat.channels.messages.index', ['channel' => $this->tourneyChannel->channel_id]))
76 ->assertStatus(404);
77 }
78
79 public function testChannelShowTourneyWhenJoined() // success
80 {
81 $this->actAsScopedUser($this->user, ['*']);
82 $this->json('PUT', route('api.chat.channels.join', [
83 'channel' => $this->tourneyChannel->channel_id,
84 'user' => $this->user->user_id,
85 ]))->assertSuccessful();
86
87 $this->actAsScopedUser($this->user, ['*']);
88 $this->json('GET', route('api.chat.channels.messages.index', ['channel' => $this->tourneyChannel->channel_id]))
89 ->assertStatus(200);
90 // TODO: Add check for messages being present?
91 }
92
93 //endregion
94
95 //region GET /chat/channels/[channel_id]/messages - Get Channel Messages (private)
96 public function testChannelShowPrivateWhenGuest() // fail
97 {
98 $this->json('GET', route('api.chat.channels.messages.index', ['channel' => $this->privateChannel->channel_id]))
99 ->assertStatus(401);
100 }
101
102 public function testChannelShowPrivateWhenNotJoined() // fail
103 {
104 $this->actAsScopedUser($this->user, ['*']);
105 $this->json('GET', route('api.chat.channels.messages.index', ['channel' => $this->privateChannel->channel_id]))
106 ->assertStatus(404);
107 }
108
109 public function testChannelShowPrivateWhenJoined() // success
110 {
111 $this->userChannel = UserChannel::factory()->create([
112 'user_id' => $this->user->user_id,
113 'channel_id' => $this->privateChannel->channel_id,
114 ]);
115
116 $this->actAsScopedUser($this->user, ['*']);
117 $this->json('GET', route('api.chat.channels.messages.index', ['channel' => $this->privateChannel->channel_id]))
118 ->assertStatus(200);
119 }
120
121 //endregion
122
123 //region GET /chat/channels/[channel_id]/messages - Get Channel Messages (pm)
124 public function testChannelShowPMWhenGuest() // fail
125 {
126 $pmChannel = Channel::factory()->type('pm')->create();
127 $this->json('GET', route('api.chat.channels.messages.index', ['channel' => $pmChannel->channel_id]))
128 ->assertStatus(401);
129 }
130
131 public function testChannelShowPMWhenNotJoined() // fail
132 {
133 $pmChannel = Channel::factory()->type('pm')->create();
134 $this->actAsScopedUser($this->user, ['*']);
135 $this->json('GET', route('api.chat.channels.messages.index', ['channel' => $pmChannel->channel_id]))
136 ->assertStatus(404);
137 }
138
139 public function testChannelShowPMWhenTargetRestricted() // fail
140 {
141 $pmChannel = Channel::factory()->type('pm', [$this->user, $this->restrictedUser])->create();
142
143 $this->actAsScopedUser($this->user, ['*']);
144 $this->json('GET', route('api.chat.channels.messages.index', ['channel' => $pmChannel->channel_id]))
145 ->assertStatus(404);
146 }
147
148 public function testChannelShowPM() // success
149 {
150 $pmChannel = Channel::factory()->type('pm', [$this->user, $this->anotherUser])->create();
151
152 $this->actAsScopedUser($this->user, ['*']);
153 $this->json('GET', route('api.chat.channels.messages.index', ['channel' => $pmChannel->channel_id]))
154 ->assertStatus(200);
155 // TODO: Add check for messages being present?
156 }
157
158 //endregion
159
160 //region POST /chat/channels/[channel_id]/messages - Send Message to Channel
161 public function testChannelSendFiltered()
162 {
163 $this->actAsScopedUser($this->user, ['*']);
164 $this->json('PUT', route('api.chat.channels.join', [
165 'channel' => $this->publicChannel->getKey(),
166 'user' => $this->user->getKey(),
167 ]));
168
169 $filter = ChatFilter::factory()->create();
170
171 $this->json(
172 'POST',
173 route('api.chat.channels.messages.store', ['channel' => $this->publicChannel->getKey()]),
174 ['message' => $filter->match],
175 )
176 ->assertJsonFragment(['content' => $filter->replacement]);
177 }
178
179 public function testChannelSendWhenGuest() // fail
180 {
181 $this->json(
182 'POST',
183 route('api.chat.channels.messages.store', ['channel' => $this->publicChannel->channel_id]),
184 ['message' => self::$faker->sentence()]
185 )->assertStatus(401);
186 }
187
188 public function testChannelSendWhenUnjoined() // fail
189 {
190 $this->actAsScopedUser($this->user, ['*']);
191 $this->json(
192 'POST',
193 route('api.chat.channels.messages.store', ['channel' => $this->publicChannel->channel_id]),
194 ['message' => self::$faker->sentence()]
195 )
196 ->assertStatus(403);
197 }
198
199 public function testChannelSendWhenJoined() // success
200 {
201 $message = self::$faker->sentence();
202
203 $this->actAsScopedUser($this->user, ['*']);
204 $this->json('PUT', route('api.chat.channels.join', [
205 'channel' => $this->publicChannel->channel_id,
206 'user' => $this->user->user_id,
207 ]));
208
209 $this->actAsScopedUser($this->user, ['*']);
210
211 $this->json(
212 'POST',
213 route('api.chat.channels.messages.store', ['channel' => $this->publicChannel->channel_id]),
214 ['message' => $message]
215 )
216 ->assertStatus(200)
217 ->assertJsonFragment(['content' => $message]);
218 }
219
220 public function testChannelSendWhenModerated() // fail
221 {
222 $moderatedChannel = Channel::factory()->type('public')->create(['moderated' => true]);
223
224 $this->actAsScopedUser($this->user, ['*']);
225 $this->json(
226 'POST',
227 route('api.chat.channels.messages.store', ['channel' => $moderatedChannel->channel_id]),
228 ['message' => self::$faker->sentence()]
229 )
230 ->assertStatus(403);
231 }
232
233 public function testChannelSendWhenBlocking() // fail
234 {
235 $pmChannel = Channel::factory()->type('pm', [$this->user, $this->anotherUser])->create();
236 UserRelation::factory()->block()->create([
237 'user_id' => $this->user,
238 'zebra_id' => $this->anotherUser,
239 ]);
240
241 $this->actAsScopedUser($this->user, ['*']);
242 $this->json(
243 'POST',
244 route('api.chat.channels.messages.store', ['channel' => $pmChannel->channel_id]),
245 ['message' => self::$faker->sentence()]
246 )
247 ->assertStatus(403);
248 }
249
250 public function testChannelSendWhenBlocked() // fail
251 {
252 $pmChannel = Channel::factory()->type('pm', [$this->user, $this->anotherUser])->create();
253 UserRelation::factory()->block()->create([
254 'user_id' => $this->anotherUser,
255 'zebra_id' => $this->user,
256 ]);
257
258 $this->actAsScopedUser($this->user, ['*']);
259 $this->json(
260 'POST',
261 route('api.chat.channels.messages.store', ['channel' => $pmChannel->channel_id]),
262 ['message' => self::$faker->sentence()]
263 )
264 ->assertStatus(403);
265 }
266
267 public function testChannelSendWhenRestrictedToPM() // fail
268 {
269 $pmChannel = Channel::factory()->type('pm', [$this->restrictedUser, $this->anotherUser])->create();
270
271 $this->actAsScopedUser($this->restrictedUser, ['*']);
272 $this->json(
273 'POST',
274 route('api.chat.channels.messages.store', ['channel' => $pmChannel->channel_id]),
275 ['message' => self::$faker->sentence()]
276 )
277 ->assertStatus(403);
278 }
279
280 public function testChannelSendWhenRestrictedToPublic() // fail
281 {
282 $this->actAsScopedUser($this->restrictedUser, ['*']);
283 $this->json('PUT', route('api.chat.channels.join', [
284 'channel' => $this->publicChannel->channel_id,
285 'user' => $this->restrictedUser->user_id,
286 ]));
287
288 $this->actAsScopedUser($this->restrictedUser, ['*']);
289 $this->json(
290 'POST',
291 route('api.chat.channels.messages.store', ['channel' => $this->publicChannel->channel_id]),
292 ['message' => self::$faker->sentence()]
293 )
294 ->assertStatus(403);
295 }
296
297 public function testChannelSendWhenTargetRestricted() // fail
298 {
299 $pmChannel = Channel::factory()->type('pm', [$this->user, $this->restrictedUser])->create();
300
301 $this->actAsScopedUser($this->user, ['*']);
302 $this->json(
303 'POST',
304 route('api.chat.channels.messages.store', ['channel' => $pmChannel->channel_id]),
305 ['message' => self::$faker->sentence()]
306 )
307 ->assertStatus(404);
308 }
309
310 public function testChannelSendWhenTourney() // fail
311 {
312 $message = self::$faker->sentence();
313
314 $this->actAsScopedUser($this->user, ['*']);
315 $this->json('PUT', route('api.chat.channels.join', [
316 'channel' => $this->tourneyChannel->channel_id,
317 'user' => $this->user->user_id,
318 ]));
319
320 $this->actAsScopedUser($this->user, ['*']);
321
322 $this->json(
323 'POST',
324 route('api.chat.channels.messages.store', ['channel' => $this->tourneyChannel->channel_id]),
325 ['message' => $message]
326 )
327 ->assertStatus(403);
328 }
329
330 public function testChannelSendWhenSilencedToPM() // fail
331 {
332 $pmChannel = Channel::factory()->type('pm', [$this->silencedUser, $this->anotherUser])->create();
333
334 $this->actAsScopedUser($this->silencedUser, ['*']);
335 $this->json(
336 'POST',
337 route('api.chat.channels.messages.store', ['channel' => $pmChannel->channel_id]),
338 ['message' => self::$faker->sentence()]
339 )
340 ->assertStatus(403);
341 }
342
343 public function testChannelSendWhenSilencedToPublic() // fail
344 {
345 $this->actAsScopedUser($this->silencedUser, ['*']);
346 $this->json('PUT', route('api.chat.channels.join', [
347 'channel' => $this->publicChannel->channel_id,
348 'user' => $this->silencedUser->user_id,
349 ]));
350
351 $this->actAsScopedUser($this->silencedUser, ['*']);
352 $this->json(
353 'POST',
354 route('api.chat.channels.messages.store', ['channel' => $this->publicChannel->channel_id]),
355 ['message' => self::$faker->sentence()]
356 )
357 ->assertStatus(403);
358 }
359
360 //endregion
361
362 protected function setUp(): void
363 {
364 parent::setUp();
365
366 $this->user = User::factory()->withPlays()->create();
367 $this->anotherUser = User::factory()->create();
368 $this->restrictedUser = User::factory()->restricted()->create();
369 $this->silencedUser = User::factory()->silenced()->create();
370 $this->publicChannel = Channel::factory()->type('public')->create();
371 $this->privateChannel = Channel::factory()->type('private')->create();
372 $this->tourneyChannel = Channel::factory()->tourney()->create();
373 }
374}