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