CommonAPI: Linkify chat messages.
[akkoma] / test / web / common_api / common_api_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.CommonAPITest do
6 use Pleroma.DataCase
7 alias Pleroma.Activity
8 alias Pleroma.Chat
9 alias Pleroma.Conversation.Participation
10 alias Pleroma.Object
11 alias Pleroma.User
12 alias Pleroma.Web.ActivityPub.ActivityPub
13 alias Pleroma.Web.ActivityPub.Transmogrifier
14 alias Pleroma.Web.ActivityPub.Visibility
15 alias Pleroma.Web.AdminAPI.AccountView
16 alias Pleroma.Web.CommonAPI
17
18 import Pleroma.Factory
19 import Mock
20
21 require Pleroma.Constants
22
23 setup do: clear_config([:instance, :safe_dm_mentions])
24 setup do: clear_config([:instance, :limit])
25 setup do: clear_config([:instance, :max_pinned_statuses])
26
27 describe "posting chat messages" do
28 setup do: clear_config([:instance, :chat_limit])
29
30 test "it posts a chat message without content but with an attachment" do
31 author = insert(:user)
32 recipient = insert(:user)
33
34 file = %Plug.Upload{
35 content_type: "image/jpg",
36 path: Path.absname("test/fixtures/image.jpg"),
37 filename: "an_image.jpg"
38 }
39
40 {:ok, upload} = ActivityPub.upload(file, actor: author.ap_id)
41
42 {:ok, activity} =
43 CommonAPI.post_chat_message(
44 author,
45 recipient,
46 nil,
47 media_id: upload.id
48 )
49
50 assert activity
51 end
52
53 test "it linkifies" do
54 author = insert(:user)
55 recipient = insert(:user)
56
57 other_user = insert(:user)
58
59 {:ok, activity} =
60 CommonAPI.post_chat_message(
61 author,
62 recipient,
63 "https://example.org is the site of @#{other_user.nickname} #2hu"
64 )
65
66 assert other_user.ap_id not in activity.recipients
67
68 object = Object.normalize(activity, false)
69
70 assert object.data["content"] ==
71 "<a href=\"https://example.org\" rel=\"ugc\">https://example.org</a> is the site of <span class=\"h-card\"><a class=\"u-url mention\" data-user=\"#{
72 other_user.id
73 }\" href=\"#{other_user.ap_id}\" rel=\"ugc\">@<span>#{other_user.nickname}</span></a></span> <a class=\"hashtag\" data-tag=\"2hu\" href=\"http://localhost:4001/tag/2hu\">#2hu</a>"
74 end
75
76 test "it posts a chat message" do
77 author = insert(:user)
78 recipient = insert(:user)
79
80 {:ok, activity} =
81 CommonAPI.post_chat_message(
82 author,
83 recipient,
84 "a test message <script>alert('uuu')</script> :firefox:"
85 )
86
87 assert activity.data["type"] == "Create"
88 assert activity.local
89 object = Object.normalize(activity)
90
91 assert object.data["type"] == "ChatMessage"
92 assert object.data["to"] == [recipient.ap_id]
93
94 assert object.data["content"] ==
95 "a test message &lt;script&gt;alert(&#39;uuu&#39;)&lt;/script&gt; :firefox:"
96
97 assert object.data["emoji"] == %{
98 "firefox" => "http://localhost:4001/emoji/Firefox.gif"
99 }
100
101 assert Chat.get(author.id, recipient.ap_id)
102 assert Chat.get(recipient.id, author.ap_id)
103
104 assert :ok == Pleroma.Web.Federator.perform(:publish, activity)
105 end
106
107 test "it reject messages over the local limit" do
108 Pleroma.Config.put([:instance, :chat_limit], 2)
109
110 author = insert(:user)
111 recipient = insert(:user)
112
113 {:error, message} =
114 CommonAPI.post_chat_message(
115 author,
116 recipient,
117 "123"
118 )
119
120 assert message == :content_too_long
121 end
122 end
123
124 describe "unblocking" do
125 test "it works even without an existing block activity" do
126 blocked = insert(:user)
127 blocker = insert(:user)
128 User.block(blocker, blocked)
129
130 assert User.blocks?(blocker, blocked)
131 assert {:ok, :no_activity} == CommonAPI.unblock(blocker, blocked)
132 refute User.blocks?(blocker, blocked)
133 end
134 end
135
136 describe "deletion" do
137 test "it works with pruned objects" do
138 user = insert(:user)
139
140 {:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
141
142 clear_config([:instance, :federating], true)
143
144 Object.normalize(post, false)
145 |> Object.prune()
146
147 with_mock Pleroma.Web.Federator,
148 publish: fn _ -> nil end do
149 assert {:ok, delete} = CommonAPI.delete(post.id, user)
150 assert delete.local
151 assert called(Pleroma.Web.Federator.publish(delete))
152 end
153
154 refute Activity.get_by_id(post.id)
155 end
156
157 test "it allows users to delete their posts" do
158 user = insert(:user)
159
160 {:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
161
162 clear_config([:instance, :federating], true)
163
164 with_mock Pleroma.Web.Federator,
165 publish: fn _ -> nil end do
166 assert {:ok, delete} = CommonAPI.delete(post.id, user)
167 assert delete.local
168 assert called(Pleroma.Web.Federator.publish(delete))
169 end
170
171 refute Activity.get_by_id(post.id)
172 end
173
174 test "it does not allow a user to delete their posts" do
175 user = insert(:user)
176 other_user = insert(:user)
177
178 {:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
179
180 assert {:error, "Could not delete"} = CommonAPI.delete(post.id, other_user)
181 assert Activity.get_by_id(post.id)
182 end
183
184 test "it allows moderators to delete other user's posts" do
185 user = insert(:user)
186 moderator = insert(:user, is_moderator: true)
187
188 {:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
189
190 assert {:ok, delete} = CommonAPI.delete(post.id, moderator)
191 assert delete.local
192
193 refute Activity.get_by_id(post.id)
194 end
195
196 test "it allows admins to delete other user's posts" do
197 user = insert(:user)
198 moderator = insert(:user, is_admin: true)
199
200 {:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
201
202 assert {:ok, delete} = CommonAPI.delete(post.id, moderator)
203 assert delete.local
204
205 refute Activity.get_by_id(post.id)
206 end
207
208 test "superusers deleting non-local posts won't federate the delete" do
209 # This is the user of the ingested activity
210 _user =
211 insert(:user,
212 local: false,
213 ap_id: "http://mastodon.example.org/users/admin",
214 last_refreshed_at: NaiveDateTime.utc_now()
215 )
216
217 moderator = insert(:user, is_admin: true)
218
219 data =
220 File.read!("test/fixtures/mastodon-post-activity.json")
221 |> Jason.decode!()
222
223 {:ok, post} = Transmogrifier.handle_incoming(data)
224
225 with_mock Pleroma.Web.Federator,
226 publish: fn _ -> nil end do
227 assert {:ok, delete} = CommonAPI.delete(post.id, moderator)
228 assert delete.local
229 refute called(Pleroma.Web.Federator.publish(:_))
230 end
231
232 refute Activity.get_by_id(post.id)
233 end
234 end
235
236 test "favoriting race condition" do
237 user = insert(:user)
238 users_serial = insert_list(10, :user)
239 users = insert_list(10, :user)
240
241 {:ok, activity} = CommonAPI.post(user, %{status: "."})
242
243 users_serial
244 |> Enum.map(fn user ->
245 CommonAPI.favorite(user, activity.id)
246 end)
247
248 object = Object.get_by_ap_id(activity.data["object"])
249 assert object.data["like_count"] == 10
250
251 users
252 |> Enum.map(fn user ->
253 Task.async(fn ->
254 CommonAPI.favorite(user, activity.id)
255 end)
256 end)
257 |> Enum.map(&Task.await/1)
258
259 object = Object.get_by_ap_id(activity.data["object"])
260 assert object.data["like_count"] == 20
261 end
262
263 test "repeating race condition" do
264 user = insert(:user)
265 users_serial = insert_list(10, :user)
266 users = insert_list(10, :user)
267
268 {:ok, activity} = CommonAPI.post(user, %{status: "."})
269
270 users_serial
271 |> Enum.map(fn user ->
272 CommonAPI.repeat(activity.id, user)
273 end)
274
275 object = Object.get_by_ap_id(activity.data["object"])
276 assert object.data["announcement_count"] == 10
277
278 users
279 |> Enum.map(fn user ->
280 Task.async(fn ->
281 CommonAPI.repeat(activity.id, user)
282 end)
283 end)
284 |> Enum.map(&Task.await/1)
285
286 object = Object.get_by_ap_id(activity.data["object"])
287 assert object.data["announcement_count"] == 20
288 end
289
290 test "when replying to a conversation / participation, it will set the correct context id even if no explicit reply_to is given" do
291 user = insert(:user)
292 {:ok, activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"})
293
294 [participation] = Participation.for_user(user)
295
296 {:ok, convo_reply} =
297 CommonAPI.post(user, %{status: ".", in_reply_to_conversation_id: participation.id})
298
299 assert Visibility.is_direct?(convo_reply)
300
301 assert activity.data["context"] == convo_reply.data["context"]
302 end
303
304 test "when replying to a conversation / participation, it only mentions the recipients explicitly declared in the participation" do
305 har = insert(:user)
306 jafnhar = insert(:user)
307 tridi = insert(:user)
308
309 {:ok, activity} =
310 CommonAPI.post(har, %{
311 status: "@#{jafnhar.nickname} hey",
312 visibility: "direct"
313 })
314
315 assert har.ap_id in activity.recipients
316 assert jafnhar.ap_id in activity.recipients
317
318 [participation] = Participation.for_user(har)
319
320 {:ok, activity} =
321 CommonAPI.post(har, %{
322 status: "I don't really like @#{tridi.nickname}",
323 visibility: "direct",
324 in_reply_to_status_id: activity.id,
325 in_reply_to_conversation_id: participation.id
326 })
327
328 assert har.ap_id in activity.recipients
329 assert jafnhar.ap_id in activity.recipients
330 refute tridi.ap_id in activity.recipients
331 end
332
333 test "with the safe_dm_mention option set, it does not mention people beyond the initial tags" do
334 har = insert(:user)
335 jafnhar = insert(:user)
336 tridi = insert(:user)
337
338 Pleroma.Config.put([:instance, :safe_dm_mentions], true)
339
340 {:ok, activity} =
341 CommonAPI.post(har, %{
342 status: "@#{jafnhar.nickname} hey, i never want to see @#{tridi.nickname} again",
343 visibility: "direct"
344 })
345
346 refute tridi.ap_id in activity.recipients
347 assert jafnhar.ap_id in activity.recipients
348 end
349
350 test "it de-duplicates tags" do
351 user = insert(:user)
352 {:ok, activity} = CommonAPI.post(user, %{status: "#2hu #2HU"})
353
354 object = Object.normalize(activity)
355
356 assert object.data["tag"] == ["2hu"]
357 end
358
359 test "it adds emoji in the object" do
360 user = insert(:user)
361 {:ok, activity} = CommonAPI.post(user, %{status: ":firefox:"})
362
363 assert Object.normalize(activity).data["emoji"]["firefox"]
364 end
365
366 describe "posting" do
367 test "it supports explicit addressing" do
368 user = insert(:user)
369 user_two = insert(:user)
370 user_three = insert(:user)
371 user_four = insert(:user)
372
373 {:ok, activity} =
374 CommonAPI.post(user, %{
375 status:
376 "Hey, I think @#{user_three.nickname} is ugly. @#{user_four.nickname} is alright though.",
377 to: [user_two.nickname, user_four.nickname, "nonexistent"]
378 })
379
380 assert user.ap_id in activity.recipients
381 assert user_two.ap_id in activity.recipients
382 assert user_four.ap_id in activity.recipients
383 refute user_three.ap_id in activity.recipients
384 end
385
386 test "it filters out obviously bad tags when accepting a post as HTML" do
387 user = insert(:user)
388
389 post = "<p><b>2hu</b></p><script>alert('xss')</script>"
390
391 {:ok, activity} =
392 CommonAPI.post(user, %{
393 status: post,
394 content_type: "text/html"
395 })
396
397 object = Object.normalize(activity)
398
399 assert object.data["content"] == "<p><b>2hu</b></p>alert(&#39;xss&#39;)"
400 end
401
402 test "it filters out obviously bad tags when accepting a post as Markdown" do
403 user = insert(:user)
404
405 post = "<p><b>2hu</b></p><script>alert('xss')</script>"
406
407 {:ok, activity} =
408 CommonAPI.post(user, %{
409 status: post,
410 content_type: "text/markdown"
411 })
412
413 object = Object.normalize(activity)
414
415 assert object.data["content"] == "<p><b>2hu</b></p>alert(&#39;xss&#39;)"
416 end
417
418 test "it does not allow replies to direct messages that are not direct messages themselves" do
419 user = insert(:user)
420
421 {:ok, activity} = CommonAPI.post(user, %{status: "suya..", visibility: "direct"})
422
423 assert {:ok, _} =
424 CommonAPI.post(user, %{
425 status: "suya..",
426 visibility: "direct",
427 in_reply_to_status_id: activity.id
428 })
429
430 Enum.each(["public", "private", "unlisted"], fn visibility ->
431 assert {:error, "The message visibility must be direct"} =
432 CommonAPI.post(user, %{
433 status: "suya..",
434 visibility: visibility,
435 in_reply_to_status_id: activity.id
436 })
437 end)
438 end
439
440 test "replying with a direct message will NOT auto-add the author of the reply to the recipient list" do
441 user = insert(:user)
442 other_user = insert(:user)
443 third_user = insert(:user)
444
445 {:ok, post} = CommonAPI.post(user, %{status: "I'm stupid"})
446
447 {:ok, open_answer} =
448 CommonAPI.post(other_user, %{status: "No ur smart", in_reply_to_status_id: post.id})
449
450 # The OP is implicitly added
451 assert user.ap_id in open_answer.recipients
452
453 {:ok, secret_answer} =
454 CommonAPI.post(other_user, %{
455 status: "lol, that guy really is stupid, right, @#{third_user.nickname}?",
456 in_reply_to_status_id: post.id,
457 visibility: "direct"
458 })
459
460 assert third_user.ap_id in secret_answer.recipients
461
462 # The OP is not added
463 refute user.ap_id in secret_answer.recipients
464 end
465
466 test "it allows to address a list" do
467 user = insert(:user)
468 {:ok, list} = Pleroma.List.create("foo", user)
469
470 {:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})
471
472 assert activity.data["bcc"] == [list.ap_id]
473 assert activity.recipients == [list.ap_id, user.ap_id]
474 assert activity.data["listMessage"] == list.ap_id
475 end
476
477 test "it returns error when status is empty and no attachments" do
478 user = insert(:user)
479
480 assert {:error, "Cannot post an empty status without attachments"} =
481 CommonAPI.post(user, %{status: ""})
482 end
483
484 test "it validates character limits are correctly enforced" do
485 Pleroma.Config.put([:instance, :limit], 5)
486
487 user = insert(:user)
488
489 assert {:error, "The status is over the character limit"} =
490 CommonAPI.post(user, %{status: "foobar"})
491
492 assert {:ok, activity} = CommonAPI.post(user, %{status: "12345"})
493 end
494
495 test "it can handle activities that expire" do
496 user = insert(:user)
497
498 expires_at =
499 NaiveDateTime.utc_now()
500 |> NaiveDateTime.truncate(:second)
501 |> NaiveDateTime.add(1_000_000, :second)
502
503 assert {:ok, activity} = CommonAPI.post(user, %{status: "chai", expires_in: 1_000_000})
504
505 assert expiration = Pleroma.ActivityExpiration.get_by_activity_id(activity.id)
506 assert expiration.scheduled_at == expires_at
507 end
508 end
509
510 describe "reactions" do
511 test "reacting to a status with an emoji" do
512 user = insert(:user)
513 other_user = insert(:user)
514
515 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
516
517 {:ok, reaction} = CommonAPI.react_with_emoji(activity.id, user, "👍")
518
519 assert reaction.data["actor"] == user.ap_id
520 assert reaction.data["content"] == "👍"
521
522 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
523
524 {:error, _} = CommonAPI.react_with_emoji(activity.id, user, ".")
525 end
526
527 test "unreacting to a status with an emoji" do
528 user = insert(:user)
529 other_user = insert(:user)
530
531 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
532 {:ok, reaction} = CommonAPI.react_with_emoji(activity.id, user, "👍")
533
534 {:ok, unreaction} = CommonAPI.unreact_with_emoji(activity.id, user, "👍")
535
536 assert unreaction.data["type"] == "Undo"
537 assert unreaction.data["object"] == reaction.data["id"]
538 assert unreaction.local
539 end
540
541 test "repeating a status" do
542 user = insert(:user)
543 other_user = insert(:user)
544
545 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
546
547 {:ok, %Activity{} = announce_activity} = CommonAPI.repeat(activity.id, user)
548 assert Visibility.is_public?(announce_activity)
549 end
550
551 test "can't repeat a repeat" do
552 user = insert(:user)
553 other_user = insert(:user)
554 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
555
556 {:ok, %Activity{} = announce} = CommonAPI.repeat(activity.id, other_user)
557
558 refute match?({:ok, %Activity{}}, CommonAPI.repeat(announce.id, user))
559 end
560
561 test "repeating a status privately" do
562 user = insert(:user)
563 other_user = insert(:user)
564
565 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
566
567 {:ok, %Activity{} = announce_activity} =
568 CommonAPI.repeat(activity.id, user, %{visibility: "private"})
569
570 assert Visibility.is_private?(announce_activity)
571 refute Visibility.visible_for_user?(announce_activity, nil)
572 end
573
574 test "favoriting a status" do
575 user = insert(:user)
576 other_user = insert(:user)
577
578 {:ok, post_activity} = CommonAPI.post(other_user, %{status: "cofe"})
579
580 {:ok, %Activity{data: data}} = CommonAPI.favorite(user, post_activity.id)
581 assert data["type"] == "Like"
582 assert data["actor"] == user.ap_id
583 assert data["object"] == post_activity.data["object"]
584 end
585
586 test "retweeting a status twice returns the status" do
587 user = insert(:user)
588 other_user = insert(:user)
589
590 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
591 {:ok, %Activity{} = announce} = CommonAPI.repeat(activity.id, user)
592 {:ok, ^announce} = CommonAPI.repeat(activity.id, user)
593 end
594
595 test "favoriting a status twice returns ok, but without the like activity" do
596 user = insert(:user)
597 other_user = insert(:user)
598
599 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
600 {:ok, %Activity{}} = CommonAPI.favorite(user, activity.id)
601 assert {:ok, :already_liked} = CommonAPI.favorite(user, activity.id)
602 end
603 end
604
605 describe "pinned statuses" do
606 setup do
607 Pleroma.Config.put([:instance, :max_pinned_statuses], 1)
608
609 user = insert(:user)
610 {:ok, activity} = CommonAPI.post(user, %{status: "HI!!!"})
611
612 [user: user, activity: activity]
613 end
614
615 test "pin status", %{user: user, activity: activity} do
616 assert {:ok, ^activity} = CommonAPI.pin(activity.id, user)
617
618 id = activity.id
619 user = refresh_record(user)
620
621 assert %User{pinned_activities: [^id]} = user
622 end
623
624 test "pin poll", %{user: user} do
625 {:ok, activity} =
626 CommonAPI.post(user, %{
627 status: "How is fediverse today?",
628 poll: %{options: ["Absolutely outstanding", "Not good"], expires_in: 20}
629 })
630
631 assert {:ok, ^activity} = CommonAPI.pin(activity.id, user)
632
633 id = activity.id
634 user = refresh_record(user)
635
636 assert %User{pinned_activities: [^id]} = user
637 end
638
639 test "unlisted statuses can be pinned", %{user: user} do
640 {:ok, activity} = CommonAPI.post(user, %{status: "HI!!!", visibility: "unlisted"})
641 assert {:ok, ^activity} = CommonAPI.pin(activity.id, user)
642 end
643
644 test "only self-authored can be pinned", %{activity: activity} do
645 user = insert(:user)
646
647 assert {:error, "Could not pin"} = CommonAPI.pin(activity.id, user)
648 end
649
650 test "max pinned statuses", %{user: user, activity: activity_one} do
651 {:ok, activity_two} = CommonAPI.post(user, %{status: "HI!!!"})
652
653 assert {:ok, ^activity_one} = CommonAPI.pin(activity_one.id, user)
654
655 user = refresh_record(user)
656
657 assert {:error, "You have already pinned the maximum number of statuses"} =
658 CommonAPI.pin(activity_two.id, user)
659 end
660
661 test "unpin status", %{user: user, activity: activity} do
662 {:ok, activity} = CommonAPI.pin(activity.id, user)
663
664 user = refresh_record(user)
665
666 id = activity.id
667
668 assert match?({:ok, %{id: ^id}}, CommonAPI.unpin(activity.id, user))
669
670 user = refresh_record(user)
671
672 assert %User{pinned_activities: []} = user
673 end
674
675 test "should unpin when deleting a status", %{user: user, activity: activity} do
676 {:ok, activity} = CommonAPI.pin(activity.id, user)
677
678 user = refresh_record(user)
679
680 assert {:ok, _} = CommonAPI.delete(activity.id, user)
681
682 user = refresh_record(user)
683
684 assert %User{pinned_activities: []} = user
685 end
686 end
687
688 describe "mute tests" do
689 setup do
690 user = insert(:user)
691
692 activity = insert(:note_activity)
693
694 [user: user, activity: activity]
695 end
696
697 test "add mute", %{user: user, activity: activity} do
698 {:ok, _} = CommonAPI.add_mute(user, activity)
699 assert CommonAPI.thread_muted?(user, activity)
700 end
701
702 test "remove mute", %{user: user, activity: activity} do
703 CommonAPI.add_mute(user, activity)
704 {:ok, _} = CommonAPI.remove_mute(user, activity)
705 refute CommonAPI.thread_muted?(user, activity)
706 end
707
708 test "check that mutes can't be duplicate", %{user: user, activity: activity} do
709 CommonAPI.add_mute(user, activity)
710 {:error, _} = CommonAPI.add_mute(user, activity)
711 end
712 end
713
714 describe "reports" do
715 test "creates a report" do
716 reporter = insert(:user)
717 target_user = insert(:user)
718
719 {:ok, activity} = CommonAPI.post(target_user, %{status: "foobar"})
720
721 reporter_ap_id = reporter.ap_id
722 target_ap_id = target_user.ap_id
723 activity_ap_id = activity.data["id"]
724 comment = "foobar"
725
726 report_data = %{
727 account_id: target_user.id,
728 comment: comment,
729 status_ids: [activity.id]
730 }
731
732 note_obj = %{
733 "type" => "Note",
734 "id" => activity_ap_id,
735 "content" => "foobar",
736 "published" => activity.object.data["published"],
737 "actor" => AccountView.render("show.json", %{user: target_user})
738 }
739
740 assert {:ok, flag_activity} = CommonAPI.report(reporter, report_data)
741
742 assert %Activity{
743 actor: ^reporter_ap_id,
744 data: %{
745 "type" => "Flag",
746 "content" => ^comment,
747 "object" => [^target_ap_id, ^note_obj],
748 "state" => "open"
749 }
750 } = flag_activity
751 end
752
753 test "updates report state" do
754 [reporter, target_user] = insert_pair(:user)
755 activity = insert(:note_activity, user: target_user)
756
757 {:ok, %Activity{id: report_id}} =
758 CommonAPI.report(reporter, %{
759 account_id: target_user.id,
760 comment: "I feel offended",
761 status_ids: [activity.id]
762 })
763
764 {:ok, report} = CommonAPI.update_report_state(report_id, "resolved")
765
766 assert report.data["state"] == "resolved"
767
768 [reported_user, activity_id] = report.data["object"]
769
770 assert reported_user == target_user.ap_id
771 assert activity_id == activity.data["id"]
772 end
773
774 test "does not update report state when state is unsupported" do
775 [reporter, target_user] = insert_pair(:user)
776 activity = insert(:note_activity, user: target_user)
777
778 {:ok, %Activity{id: report_id}} =
779 CommonAPI.report(reporter, %{
780 account_id: target_user.id,
781 comment: "I feel offended",
782 status_ids: [activity.id]
783 })
784
785 assert CommonAPI.update_report_state(report_id, "test") == {:error, "Unsupported state"}
786 end
787
788 test "updates state of multiple reports" do
789 [reporter, target_user] = insert_pair(:user)
790 activity = insert(:note_activity, user: target_user)
791
792 {:ok, %Activity{id: first_report_id}} =
793 CommonAPI.report(reporter, %{
794 account_id: target_user.id,
795 comment: "I feel offended",
796 status_ids: [activity.id]
797 })
798
799 {:ok, %Activity{id: second_report_id}} =
800 CommonAPI.report(reporter, %{
801 account_id: target_user.id,
802 comment: "I feel very offended!",
803 status_ids: [activity.id]
804 })
805
806 {:ok, report_ids} =
807 CommonAPI.update_report_state([first_report_id, second_report_id], "resolved")
808
809 first_report = Activity.get_by_id(first_report_id)
810 second_report = Activity.get_by_id(second_report_id)
811
812 assert report_ids -- [first_report_id, second_report_id] == []
813 assert first_report.data["state"] == "resolved"
814 assert second_report.data["state"] == "resolved"
815 end
816 end
817
818 describe "reblog muting" do
819 setup do
820 muter = insert(:user)
821
822 muted = insert(:user)
823
824 [muter: muter, muted: muted]
825 end
826
827 test "add a reblog mute", %{muter: muter, muted: muted} do
828 {:ok, _reblog_mute} = CommonAPI.hide_reblogs(muter, muted)
829
830 assert User.showing_reblogs?(muter, muted) == false
831 end
832
833 test "remove a reblog mute", %{muter: muter, muted: muted} do
834 {:ok, _reblog_mute} = CommonAPI.hide_reblogs(muter, muted)
835 {:ok, _reblog_mute} = CommonAPI.show_reblogs(muter, muted)
836
837 assert User.showing_reblogs?(muter, muted) == true
838 end
839 end
840
841 describe "unfollow/2" do
842 test "also unsubscribes a user" do
843 [follower, followed] = insert_pair(:user)
844 {:ok, follower, followed, _} = CommonAPI.follow(follower, followed)
845 {:ok, _subscription} = User.subscribe(follower, followed)
846
847 assert User.subscribed_to?(follower, followed)
848
849 {:ok, follower} = CommonAPI.unfollow(follower, followed)
850
851 refute User.subscribed_to?(follower, followed)
852 end
853
854 test "cancels a pending follow for a local user" do
855 follower = insert(:user)
856 followed = insert(:user, locked: true)
857
858 assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} =
859 CommonAPI.follow(follower, followed)
860
861 assert User.get_follow_state(follower, followed) == :follow_pending
862 assert {:ok, follower} = CommonAPI.unfollow(follower, followed)
863 assert User.get_follow_state(follower, followed) == nil
864
865 assert %{id: ^activity_id, data: %{"state" => "cancelled"}} =
866 Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(follower, followed)
867
868 assert %{
869 data: %{
870 "type" => "Undo",
871 "object" => %{"type" => "Follow", "state" => "cancelled"}
872 }
873 } = Pleroma.Web.ActivityPub.Utils.fetch_latest_undo(follower)
874 end
875
876 test "cancels a pending follow for a remote user" do
877 follower = insert(:user)
878 followed = insert(:user, locked: true, local: false, ap_enabled: true)
879
880 assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} =
881 CommonAPI.follow(follower, followed)
882
883 assert User.get_follow_state(follower, followed) == :follow_pending
884 assert {:ok, follower} = CommonAPI.unfollow(follower, followed)
885 assert User.get_follow_state(follower, followed) == nil
886
887 assert %{id: ^activity_id, data: %{"state" => "cancelled"}} =
888 Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(follower, followed)
889
890 assert %{
891 data: %{
892 "type" => "Undo",
893 "object" => %{"type" => "Follow", "state" => "cancelled"}
894 }
895 } = Pleroma.Web.ActivityPub.Utils.fetch_latest_undo(follower)
896 end
897 end
898
899 describe "accept_follow_request/2" do
900 test "after acceptance, it sets all existing pending follow request states to 'accept'" do
901 user = insert(:user, locked: true)
902 follower = insert(:user)
903 follower_two = insert(:user)
904
905 {:ok, follow_activity} = ActivityPub.follow(follower, user)
906 {:ok, follow_activity_two} = ActivityPub.follow(follower, user)
907 {:ok, follow_activity_three} = ActivityPub.follow(follower_two, user)
908
909 assert follow_activity.data["state"] == "pending"
910 assert follow_activity_two.data["state"] == "pending"
911 assert follow_activity_three.data["state"] == "pending"
912
913 {:ok, _follower} = CommonAPI.accept_follow_request(follower, user)
914
915 assert Repo.get(Activity, follow_activity.id).data["state"] == "accept"
916 assert Repo.get(Activity, follow_activity_two.id).data["state"] == "accept"
917 assert Repo.get(Activity, follow_activity_three.id).data["state"] == "pending"
918 end
919
920 test "after rejection, it sets all existing pending follow request states to 'reject'" do
921 user = insert(:user, locked: true)
922 follower = insert(:user)
923 follower_two = insert(:user)
924
925 {:ok, follow_activity} = ActivityPub.follow(follower, user)
926 {:ok, follow_activity_two} = ActivityPub.follow(follower, user)
927 {:ok, follow_activity_three} = ActivityPub.follow(follower_two, user)
928
929 assert follow_activity.data["state"] == "pending"
930 assert follow_activity_two.data["state"] == "pending"
931 assert follow_activity_three.data["state"] == "pending"
932
933 {:ok, _follower} = CommonAPI.reject_follow_request(follower, user)
934
935 assert Repo.get(Activity, follow_activity.id).data["state"] == "reject"
936 assert Repo.get(Activity, follow_activity_two.id).data["state"] == "reject"
937 assert Repo.get(Activity, follow_activity_three.id).data["state"] == "pending"
938 end
939
940 test "doesn't create a following relationship if the corresponding follow request doesn't exist" do
941 user = insert(:user, locked: true)
942 not_follower = insert(:user)
943 CommonAPI.accept_follow_request(not_follower, user)
944
945 assert Pleroma.FollowingRelationship.following?(not_follower, user) == false
946 end
947 end
948
949 describe "vote/3" do
950 test "does not allow to vote twice" do
951 user = insert(:user)
952 other_user = insert(:user)
953
954 {:ok, activity} =
955 CommonAPI.post(user, %{
956 status: "Am I cute?",
957 poll: %{options: ["Yes", "No"], expires_in: 20}
958 })
959
960 object = Object.normalize(activity)
961
962 {:ok, _, object} = CommonAPI.vote(other_user, object, [0])
963
964 assert {:error, "Already voted"} == CommonAPI.vote(other_user, object, [1])
965 end
966 end
967
968 describe "listen/2" do
969 test "returns a valid activity" do
970 user = insert(:user)
971
972 {:ok, activity} =
973 CommonAPI.listen(user, %{
974 title: "lain radio episode 1",
975 album: "lain radio",
976 artist: "lain",
977 length: 180_000
978 })
979
980 object = Object.normalize(activity)
981
982 assert object.data["title"] == "lain radio episode 1"
983
984 assert Visibility.get_visibility(activity) == "public"
985 end
986
987 test "respects visibility=private" do
988 user = insert(:user)
989
990 {:ok, activity} =
991 CommonAPI.listen(user, %{
992 title: "lain radio episode 1",
993 album: "lain radio",
994 artist: "lain",
995 length: 180_000,
996 visibility: "private"
997 })
998
999 object = Object.normalize(activity)
1000
1001 assert object.data["title"] == "lain radio episode 1"
1002
1003 assert Visibility.get_visibility(activity) == "private"
1004 end
1005 end
1006 end