Merge remote-tracking branch 'pleroma/develop' into staff-plug
[akkoma] / test / pleroma / web / common_api_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.CommonAPITest do
6 use Oban.Testing, repo: Pleroma.Repo
7 use Pleroma.DataCase
8
9 alias Pleroma.Activity
10 alias Pleroma.Chat
11 alias Pleroma.Conversation.Participation
12 alias Pleroma.Notification
13 alias Pleroma.Object
14 alias Pleroma.Repo
15 alias Pleroma.User
16 alias Pleroma.Web.ActivityPub.ActivityPub
17 alias Pleroma.Web.ActivityPub.Transmogrifier
18 alias Pleroma.Web.ActivityPub.Visibility
19 alias Pleroma.Web.AdminAPI.AccountView
20 alias Pleroma.Web.CommonAPI
21
22 import Pleroma.Factory
23 import Mock
24 import Ecto.Query, only: [from: 2]
25
26 require Pleroma.Constants
27
28 setup_all do
29 Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
30 :ok
31 end
32
33 setup do: clear_config([:instance, :safe_dm_mentions])
34 setup do: clear_config([:instance, :limit])
35 setup do: clear_config([:instance, :max_pinned_statuses])
36
37 describe "posting polls" do
38 test "it posts a poll" do
39 user = insert(:user)
40
41 {:ok, activity} =
42 CommonAPI.post(user, %{
43 status: "who is the best",
44 poll: %{expires_in: 600, options: ["reimu", "marisa"]}
45 })
46
47 object = Object.normalize(activity, fetch: false)
48
49 assert object.data["type"] == "Question"
50 assert object.data["oneOf"] |> length() == 2
51 end
52 end
53
54 describe "blocking" do
55 setup do
56 blocker = insert(:user)
57 blocked = insert(:user)
58 User.follow(blocker, blocked)
59 User.follow(blocked, blocker)
60 %{blocker: blocker, blocked: blocked}
61 end
62
63 test "it blocks and federates", %{blocker: blocker, blocked: blocked} do
64 clear_config([:instance, :federating], true)
65
66 with_mock Pleroma.Web.Federator,
67 publish: fn _ -> nil end do
68 assert {:ok, block} = CommonAPI.block(blocker, blocked)
69
70 assert block.local
71 assert User.blocks?(blocker, blocked)
72 refute User.following?(blocker, blocked)
73 refute User.following?(blocked, blocker)
74
75 assert called(Pleroma.Web.Federator.publish(block))
76 end
77 end
78
79 test "it blocks and does not federate if outgoing blocks are disabled", %{
80 blocker: blocker,
81 blocked: blocked
82 } do
83 clear_config([:instance, :federating], true)
84 clear_config([:activitypub, :outgoing_blocks], false)
85
86 with_mock Pleroma.Web.Federator,
87 publish: fn _ -> nil end do
88 assert {:ok, block} = CommonAPI.block(blocker, blocked)
89
90 assert block.local
91 assert User.blocks?(blocker, blocked)
92 refute User.following?(blocker, blocked)
93 refute User.following?(blocked, blocker)
94
95 refute called(Pleroma.Web.Federator.publish(block))
96 end
97 end
98 end
99
100 describe "posting chat messages" do
101 setup do: clear_config([:instance, :chat_limit])
102
103 test "it posts a self-chat" do
104 author = insert(:user)
105 recipient = author
106
107 {:ok, activity} =
108 CommonAPI.post_chat_message(
109 author,
110 recipient,
111 "remember to buy milk when milk truk arive"
112 )
113
114 assert activity.data["type"] == "Create"
115 end
116
117 test "it posts a chat message without content but with an attachment" do
118 author = insert(:user)
119 recipient = insert(:user)
120
121 file = %Plug.Upload{
122 content_type: "image/jpeg",
123 path: Path.absname("test/fixtures/image.jpg"),
124 filename: "an_image.jpg"
125 }
126
127 {:ok, upload} = ActivityPub.upload(file, actor: author.ap_id)
128
129 with_mocks([
130 {
131 Pleroma.Web.Streamer,
132 [],
133 [
134 stream: fn _, _ ->
135 nil
136 end
137 ]
138 },
139 {
140 Pleroma.Web.Push,
141 [],
142 [
143 send: fn _ -> nil end
144 ]
145 }
146 ]) do
147 {:ok, activity} =
148 CommonAPI.post_chat_message(
149 author,
150 recipient,
151 nil,
152 media_id: upload.id
153 )
154
155 notification =
156 Notification.for_user_and_activity(recipient, activity)
157 |> Repo.preload(:activity)
158
159 assert called(Pleroma.Web.Push.send(notification))
160 assert called(Pleroma.Web.Streamer.stream(["user", "user:notification"], notification))
161 assert called(Pleroma.Web.Streamer.stream(["user", "user:pleroma_chat"], :_))
162
163 assert activity
164 end
165 end
166
167 test "it adds html newlines" do
168 author = insert(:user)
169 recipient = insert(:user)
170
171 other_user = insert(:user)
172
173 {:ok, activity} =
174 CommonAPI.post_chat_message(
175 author,
176 recipient,
177 "uguu\nuguuu"
178 )
179
180 assert other_user.ap_id not in activity.recipients
181
182 object = Object.normalize(activity, fetch: false)
183
184 assert object.data["content"] == "uguu<br/>uguuu"
185 end
186
187 test "it linkifies" do
188 author = insert(:user)
189 recipient = insert(:user)
190
191 other_user = insert(:user)
192
193 {:ok, activity} =
194 CommonAPI.post_chat_message(
195 author,
196 recipient,
197 "https://example.org is the site of @#{other_user.nickname} #2hu"
198 )
199
200 assert other_user.ap_id not in activity.recipients
201
202 object = Object.normalize(activity, fetch: false)
203
204 assert object.data["content"] ==
205 "<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=\"#{
206 other_user.id
207 }\" 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>"
208 end
209
210 test "it posts a chat message" do
211 author = insert(:user)
212 recipient = insert(:user)
213
214 {:ok, activity} =
215 CommonAPI.post_chat_message(
216 author,
217 recipient,
218 "a test message <script>alert('uuu')</script> :firefox:"
219 )
220
221 assert activity.data["type"] == "Create"
222 assert activity.local
223 object = Object.normalize(activity, fetch: false)
224
225 assert object.data["type"] == "ChatMessage"
226 assert object.data["to"] == [recipient.ap_id]
227
228 assert object.data["content"] ==
229 "a test message &lt;script&gt;alert(&#39;uuu&#39;)&lt;/script&gt; :firefox:"
230
231 assert object.data["emoji"] == %{
232 "firefox" => "http://localhost:4001/emoji/Firefox.gif"
233 }
234
235 assert Chat.get(author.id, recipient.ap_id)
236 assert Chat.get(recipient.id, author.ap_id)
237
238 assert :ok == Pleroma.Web.Federator.perform(:publish, activity)
239 end
240
241 test "it reject messages over the local limit" do
242 clear_config([:instance, :chat_limit], 2)
243
244 author = insert(:user)
245 recipient = insert(:user)
246
247 {:error, message} =
248 CommonAPI.post_chat_message(
249 author,
250 recipient,
251 "123"
252 )
253
254 assert message == :content_too_long
255 end
256
257 test "it reject messages via MRF" do
258 clear_config([:mrf_keyword, :reject], ["GNO"])
259 clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.KeywordPolicy])
260
261 author = insert(:user)
262 recipient = insert(:user)
263
264 assert {:reject, "[KeywordPolicy] Matches with rejected keyword"} ==
265 CommonAPI.post_chat_message(author, recipient, "GNO/Linux")
266 end
267 end
268
269 describe "unblocking" do
270 test "it works even without an existing block activity" do
271 blocked = insert(:user)
272 blocker = insert(:user)
273 User.block(blocker, blocked)
274
275 assert User.blocks?(blocker, blocked)
276 assert {:ok, :no_activity} == CommonAPI.unblock(blocker, blocked)
277 refute User.blocks?(blocker, blocked)
278 end
279 end
280
281 describe "deletion" do
282 test "it works with pruned objects" do
283 user = insert(:user)
284
285 {:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
286
287 clear_config([:instance, :federating], true)
288
289 Object.normalize(post, fetch: false)
290 |> Object.prune()
291
292 with_mock Pleroma.Web.Federator,
293 publish: fn _ -> nil end do
294 assert {:ok, delete} = CommonAPI.delete(post.id, user)
295 assert delete.local
296 assert called(Pleroma.Web.Federator.publish(delete))
297 end
298
299 refute Activity.get_by_id(post.id)
300 end
301
302 test "it allows users to delete their posts" do
303 user = insert(:user)
304
305 {:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
306
307 clear_config([:instance, :federating], true)
308
309 with_mock Pleroma.Web.Federator,
310 publish: fn _ -> nil end do
311 assert {:ok, delete} = CommonAPI.delete(post.id, user)
312 assert delete.local
313 assert called(Pleroma.Web.Federator.publish(delete))
314 end
315
316 refute Activity.get_by_id(post.id)
317 end
318
319 test "it does not allow a user to delete their posts" do
320 user = insert(:user)
321 other_user = insert(:user)
322
323 {:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
324
325 assert {:error, "Could not delete"} = CommonAPI.delete(post.id, other_user)
326 assert Activity.get_by_id(post.id)
327 end
328
329 test "it allows moderators to delete other user's posts" do
330 user = insert(:user)
331 moderator = insert(:user, is_moderator: true)
332
333 {:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
334
335 assert {:ok, delete} = CommonAPI.delete(post.id, moderator)
336 assert delete.local
337
338 refute Activity.get_by_id(post.id)
339 end
340
341 test "it allows admins to delete other user's posts" do
342 user = insert(:user)
343 moderator = insert(:user, is_admin: true)
344
345 {:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
346
347 assert {:ok, delete} = CommonAPI.delete(post.id, moderator)
348 assert delete.local
349
350 refute Activity.get_by_id(post.id)
351 end
352
353 test "superusers deleting non-local posts won't federate the delete" do
354 # This is the user of the ingested activity
355 _user =
356 insert(:user,
357 local: false,
358 ap_id: "http://mastodon.example.org/users/admin",
359 last_refreshed_at: NaiveDateTime.utc_now()
360 )
361
362 moderator = insert(:user, is_admin: true)
363
364 data =
365 File.read!("test/fixtures/mastodon-post-activity.json")
366 |> Jason.decode!()
367
368 {:ok, post} = Transmogrifier.handle_incoming(data)
369
370 with_mock Pleroma.Web.Federator,
371 publish: fn _ -> nil end do
372 assert {:ok, delete} = CommonAPI.delete(post.id, moderator)
373 assert delete.local
374 refute called(Pleroma.Web.Federator.publish(:_))
375 end
376
377 refute Activity.get_by_id(post.id)
378 end
379 end
380
381 test "favoriting race condition" do
382 user = insert(:user)
383 users_serial = insert_list(10, :user)
384 users = insert_list(10, :user)
385
386 {:ok, activity} = CommonAPI.post(user, %{status: "."})
387
388 users_serial
389 |> Enum.map(fn user ->
390 CommonAPI.favorite(user, activity.id)
391 end)
392
393 object = Object.get_by_ap_id(activity.data["object"])
394 assert object.data["like_count"] == 10
395
396 users
397 |> Enum.map(fn user ->
398 Task.async(fn ->
399 CommonAPI.favorite(user, activity.id)
400 end)
401 end)
402 |> Enum.map(&Task.await/1)
403
404 object = Object.get_by_ap_id(activity.data["object"])
405 assert object.data["like_count"] == 20
406 end
407
408 test "repeating race condition" do
409 user = insert(:user)
410 users_serial = insert_list(10, :user)
411 users = insert_list(10, :user)
412
413 {:ok, activity} = CommonAPI.post(user, %{status: "."})
414
415 users_serial
416 |> Enum.map(fn user ->
417 CommonAPI.repeat(activity.id, user)
418 end)
419
420 object = Object.get_by_ap_id(activity.data["object"])
421 assert object.data["announcement_count"] == 10
422
423 users
424 |> Enum.map(fn user ->
425 Task.async(fn ->
426 CommonAPI.repeat(activity.id, user)
427 end)
428 end)
429 |> Enum.map(&Task.await/1)
430
431 object = Object.get_by_ap_id(activity.data["object"])
432 assert object.data["announcement_count"] == 20
433 end
434
435 test "when replying to a conversation / participation, it will set the correct context id even if no explicit reply_to is given" do
436 user = insert(:user)
437 {:ok, activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"})
438
439 [participation] = Participation.for_user(user)
440
441 {:ok, convo_reply} =
442 CommonAPI.post(user, %{status: ".", in_reply_to_conversation_id: participation.id})
443
444 assert Visibility.is_direct?(convo_reply)
445
446 assert activity.data["context"] == convo_reply.data["context"]
447 end
448
449 test "when replying to a conversation / participation, it only mentions the recipients explicitly declared in the participation" do
450 har = insert(:user)
451 jafnhar = insert(:user)
452 tridi = insert(:user)
453
454 {:ok, activity} =
455 CommonAPI.post(har, %{
456 status: "@#{jafnhar.nickname} hey",
457 visibility: "direct"
458 })
459
460 assert har.ap_id in activity.recipients
461 assert jafnhar.ap_id in activity.recipients
462
463 [participation] = Participation.for_user(har)
464
465 {:ok, activity} =
466 CommonAPI.post(har, %{
467 status: "I don't really like @#{tridi.nickname}",
468 visibility: "direct",
469 in_reply_to_status_id: activity.id,
470 in_reply_to_conversation_id: participation.id
471 })
472
473 assert har.ap_id in activity.recipients
474 assert jafnhar.ap_id in activity.recipients
475 refute tridi.ap_id in activity.recipients
476 end
477
478 test "with the safe_dm_mention option set, it does not mention people beyond the initial tags" do
479 har = insert(:user)
480 jafnhar = insert(:user)
481 tridi = insert(:user)
482
483 clear_config([:instance, :safe_dm_mentions], true)
484
485 {:ok, activity} =
486 CommonAPI.post(har, %{
487 status: "@#{jafnhar.nickname} hey, i never want to see @#{tridi.nickname} again",
488 visibility: "direct"
489 })
490
491 refute tridi.ap_id in activity.recipients
492 assert jafnhar.ap_id in activity.recipients
493 end
494
495 test "it de-duplicates tags" do
496 user = insert(:user)
497 {:ok, activity} = CommonAPI.post(user, %{status: "#2hu #2HU"})
498
499 object = Object.normalize(activity, fetch: false)
500
501 assert Object.tags(object) == ["2hu"]
502 end
503
504 test "it adds emoji in the object" do
505 user = insert(:user)
506 {:ok, activity} = CommonAPI.post(user, %{status: ":firefox:"})
507
508 assert Object.normalize(activity, fetch: false).data["emoji"]["firefox"]
509 end
510
511 describe "posting" do
512 test "it adds an emoji on an external site" do
513 user = insert(:user)
514 {:ok, activity} = CommonAPI.post(user, %{status: "hey :external_emoji:"})
515
516 assert %{"external_emoji" => url} = Object.normalize(activity).data["emoji"]
517 assert url == "https://example.com/emoji.png"
518
519 {:ok, activity} = CommonAPI.post(user, %{status: "hey :blank:"})
520
521 assert %{"blank" => url} = Object.normalize(activity).data["emoji"]
522 assert url == "#{Pleroma.Web.Endpoint.url()}/emoji/blank.png"
523 end
524
525 test "it copies emoji from the subject of the parent post" do
526 %Object{} =
527 object =
528 Object.normalize("https://patch.cx/objects/a399c28e-c821-4820-bc3e-4afeb044c16f",
529 fetch: true
530 )
531
532 activity = Activity.get_create_by_object_ap_id(object.data["id"])
533 user = insert(:user)
534
535 {:ok, reply_activity} =
536 CommonAPI.post(user, %{
537 in_reply_to_id: activity.id,
538 status: ":joker_disapprove:",
539 spoiler_text: ":joker_smile:"
540 })
541
542 assert Object.normalize(reply_activity).data["emoji"]["joker_smile"]
543 refute Object.normalize(reply_activity).data["emoji"]["joker_disapprove"]
544 end
545
546 test "deactivated users can't post" do
547 user = insert(:user, is_active: false)
548 assert {:error, _} = CommonAPI.post(user, %{status: "ye"})
549 end
550
551 test "it supports explicit addressing" do
552 user = insert(:user)
553 user_two = insert(:user)
554 user_three = insert(:user)
555 user_four = insert(:user)
556
557 {:ok, activity} =
558 CommonAPI.post(user, %{
559 status:
560 "Hey, I think @#{user_three.nickname} is ugly. @#{user_four.nickname} is alright though.",
561 to: [user_two.nickname, user_four.nickname, "nonexistent"]
562 })
563
564 assert user.ap_id in activity.recipients
565 assert user_two.ap_id in activity.recipients
566 assert user_four.ap_id in activity.recipients
567 refute user_three.ap_id in activity.recipients
568 end
569
570 test "it filters out obviously bad tags when accepting a post as HTML" do
571 user = insert(:user)
572
573 post = "<p><b>2hu</b></p><script>alert('xss')</script>"
574
575 {:ok, activity} =
576 CommonAPI.post(user, %{
577 status: post,
578 content_type: "text/html"
579 })
580
581 object = Object.normalize(activity, fetch: false)
582
583 assert object.data["content"] == "<p><b>2hu</b></p>alert(&#39;xss&#39;)"
584 assert object.data["source"] == post
585 end
586
587 test "it filters out obviously bad tags when accepting a post as Markdown" do
588 user = insert(:user)
589
590 post = "<p><b>2hu</b></p><script>alert('xss')</script>"
591
592 {:ok, activity} =
593 CommonAPI.post(user, %{
594 status: post,
595 content_type: "text/markdown"
596 })
597
598 object = Object.normalize(activity, fetch: false)
599
600 assert object.data["content"] == "<p><b>2hu</b></p>"
601 assert object.data["source"] == post
602 end
603
604 test "it does not allow replies to direct messages that are not direct messages themselves" do
605 user = insert(:user)
606
607 {:ok, activity} = CommonAPI.post(user, %{status: "suya..", visibility: "direct"})
608
609 assert {:ok, _} =
610 CommonAPI.post(user, %{
611 status: "suya..",
612 visibility: "direct",
613 in_reply_to_status_id: activity.id
614 })
615
616 Enum.each(["public", "private", "unlisted"], fn visibility ->
617 assert {:error, "The message visibility must be direct"} =
618 CommonAPI.post(user, %{
619 status: "suya..",
620 visibility: visibility,
621 in_reply_to_status_id: activity.id
622 })
623 end)
624 end
625
626 test "replying with a direct message will NOT auto-add the author of the reply to the recipient list" do
627 user = insert(:user)
628 other_user = insert(:user)
629 third_user = insert(:user)
630
631 {:ok, post} = CommonAPI.post(user, %{status: "I'm stupid"})
632
633 {:ok, open_answer} =
634 CommonAPI.post(other_user, %{status: "No ur smart", in_reply_to_status_id: post.id})
635
636 # The OP is implicitly added
637 assert user.ap_id in open_answer.recipients
638
639 {:ok, secret_answer} =
640 CommonAPI.post(other_user, %{
641 status: "lol, that guy really is stupid, right, @#{third_user.nickname}?",
642 in_reply_to_status_id: post.id,
643 visibility: "direct"
644 })
645
646 assert third_user.ap_id in secret_answer.recipients
647
648 # The OP is not added
649 refute user.ap_id in secret_answer.recipients
650 end
651
652 test "it allows to address a list" do
653 user = insert(:user)
654 {:ok, list} = Pleroma.List.create("foo", user)
655
656 {:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})
657
658 assert activity.data["bcc"] == [list.ap_id]
659 assert activity.recipients == [list.ap_id, user.ap_id]
660 assert activity.data["listMessage"] == list.ap_id
661 end
662
663 test "it returns error when status is empty and no attachments" do
664 user = insert(:user)
665
666 assert {:error, "Cannot post an empty status without attachments"} =
667 CommonAPI.post(user, %{status: ""})
668 end
669
670 test "it validates character limits are correctly enforced" do
671 clear_config([:instance, :limit], 5)
672
673 user = insert(:user)
674
675 assert {:error, "The status is over the character limit"} =
676 CommonAPI.post(user, %{status: "foobar"})
677
678 assert {:ok, _activity} = CommonAPI.post(user, %{status: "12345"})
679 end
680
681 test "it can handle activities that expire" do
682 user = insert(:user)
683
684 expires_at = DateTime.add(DateTime.utc_now(), 1_000_000)
685
686 assert {:ok, activity} = CommonAPI.post(user, %{status: "chai", expires_in: 1_000_000})
687
688 assert_enqueued(
689 worker: Pleroma.Workers.PurgeExpiredActivity,
690 args: %{activity_id: activity.id},
691 scheduled_at: expires_at
692 )
693 end
694 end
695
696 describe "reactions" do
697 test "reacting to a status with an emoji" do
698 user = insert(:user)
699 other_user = insert(:user)
700
701 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
702
703 {:ok, reaction} = CommonAPI.react_with_emoji(activity.id, user, "👍")
704
705 assert reaction.data["actor"] == user.ap_id
706 assert reaction.data["content"] == "👍"
707
708 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
709
710 {:error, _} = CommonAPI.react_with_emoji(activity.id, user, ".")
711 end
712
713 test "unreacting to a status with an emoji" do
714 user = insert(:user)
715 other_user = insert(:user)
716
717 clear_config([:instance, :federating], true)
718
719 with_mock Pleroma.Web.Federator,
720 publish: fn _ -> nil end do
721 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
722 {:ok, reaction} = CommonAPI.react_with_emoji(activity.id, user, "👍")
723
724 {:ok, unreaction} = CommonAPI.unreact_with_emoji(activity.id, user, "👍")
725
726 assert unreaction.data["type"] == "Undo"
727 assert unreaction.data["object"] == reaction.data["id"]
728 assert unreaction.local
729
730 # On federation, it contains the undone (and deleted) object
731 unreaction_with_object = %{
732 unreaction
733 | data: Map.put(unreaction.data, "object", reaction.data)
734 }
735
736 assert called(Pleroma.Web.Federator.publish(unreaction_with_object))
737 end
738 end
739
740 test "repeating a status" do
741 user = insert(:user)
742 other_user = insert(:user)
743
744 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
745
746 {:ok, %Activity{} = announce_activity} = CommonAPI.repeat(activity.id, user)
747 assert Visibility.is_public?(announce_activity)
748 end
749
750 test "can't repeat a repeat" do
751 user = insert(:user)
752 other_user = insert(:user)
753 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
754
755 {:ok, %Activity{} = announce} = CommonAPI.repeat(activity.id, other_user)
756
757 refute match?({:ok, %Activity{}}, CommonAPI.repeat(announce.id, user))
758 end
759
760 test "repeating a status privately" do
761 user = insert(:user)
762 other_user = insert(:user)
763
764 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
765
766 {:ok, %Activity{} = announce_activity} =
767 CommonAPI.repeat(activity.id, user, %{visibility: "private"})
768
769 assert Visibility.is_private?(announce_activity)
770 refute Visibility.visible_for_user?(announce_activity, nil)
771 end
772
773 test "author can repeat own private statuses" do
774 author = insert(:user)
775 follower = insert(:user)
776 CommonAPI.follow(follower, author)
777
778 {:ok, activity} = CommonAPI.post(author, %{status: "cofe", visibility: "private"})
779
780 {:ok, %Activity{} = announce_activity} = CommonAPI.repeat(activity.id, author)
781
782 assert Visibility.is_private?(announce_activity)
783 refute Visibility.visible_for_user?(announce_activity, nil)
784
785 assert Visibility.visible_for_user?(activity, follower)
786 assert {:error, :not_found} = CommonAPI.repeat(activity.id, follower)
787 end
788
789 test "favoriting a status" do
790 user = insert(:user)
791 other_user = insert(:user)
792
793 {:ok, post_activity} = CommonAPI.post(other_user, %{status: "cofe"})
794
795 {:ok, %Activity{data: data}} = CommonAPI.favorite(user, post_activity.id)
796 assert data["type"] == "Like"
797 assert data["actor"] == user.ap_id
798 assert data["object"] == post_activity.data["object"]
799 end
800
801 test "retweeting a status twice returns the status" do
802 user = insert(:user)
803 other_user = insert(:user)
804
805 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
806 {:ok, %Activity{} = announce} = CommonAPI.repeat(activity.id, user)
807 {:ok, ^announce} = CommonAPI.repeat(activity.id, user)
808 end
809
810 test "favoriting a status twice returns ok, but without the like activity" do
811 user = insert(:user)
812 other_user = insert(:user)
813
814 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
815 {:ok, %Activity{}} = CommonAPI.favorite(user, activity.id)
816 assert {:ok, :already_liked} = CommonAPI.favorite(user, activity.id)
817 end
818 end
819
820 describe "pinned statuses" do
821 setup do
822 clear_config([:instance, :max_pinned_statuses], 1)
823
824 user = insert(:user)
825 {:ok, activity} = CommonAPI.post(user, %{status: "HI!!!"})
826
827 [user: user, activity: activity]
828 end
829
830 test "activity not found error", %{user: user} do
831 assert {:error, :not_found} = CommonAPI.pin("id", user)
832 end
833
834 test "pin status", %{user: user, activity: activity} do
835 assert {:ok, ^activity} = CommonAPI.pin(activity.id, user)
836
837 %{data: %{"id" => object_id}} = Object.normalize(activity)
838 user = refresh_record(user)
839
840 assert user.pinned_objects |> Map.keys() == [object_id]
841 end
842
843 test "pin poll", %{user: user} do
844 {:ok, activity} =
845 CommonAPI.post(user, %{
846 status: "How is fediverse today?",
847 poll: %{options: ["Absolutely outstanding", "Not good"], expires_in: 20}
848 })
849
850 assert {:ok, ^activity} = CommonAPI.pin(activity.id, user)
851
852 %{data: %{"id" => object_id}} = Object.normalize(activity)
853
854 user = refresh_record(user)
855
856 assert user.pinned_objects |> Map.keys() == [object_id]
857 end
858
859 test "unlisted statuses can be pinned", %{user: user} do
860 {:ok, activity} = CommonAPI.post(user, %{status: "HI!!!", visibility: "unlisted"})
861 assert {:ok, ^activity} = CommonAPI.pin(activity.id, user)
862 end
863
864 test "only self-authored can be pinned", %{activity: activity} do
865 user = insert(:user)
866
867 assert {:error, :ownership_error} = CommonAPI.pin(activity.id, user)
868 end
869
870 test "max pinned statuses", %{user: user, activity: activity_one} do
871 {:ok, activity_two} = CommonAPI.post(user, %{status: "HI!!!"})
872
873 assert {:ok, ^activity_one} = CommonAPI.pin(activity_one.id, user)
874
875 user = refresh_record(user)
876
877 assert {:error, :pinned_statuses_limit_reached} = CommonAPI.pin(activity_two.id, user)
878 end
879
880 test "only public can be pinned", %{user: user} do
881 {:ok, activity} = CommonAPI.post(user, %{status: "private status", visibility: "private"})
882 {:error, :visibility_error} = CommonAPI.pin(activity.id, user)
883 end
884
885 test "unpin status", %{user: user, activity: activity} do
886 {:ok, activity} = CommonAPI.pin(activity.id, user)
887
888 user = refresh_record(user)
889
890 id = activity.id
891
892 assert match?({:ok, %{id: ^id}}, CommonAPI.unpin(activity.id, user))
893
894 user = refresh_record(user)
895
896 assert user.pinned_objects == %{}
897 end
898
899 test "should unpin when deleting a status", %{user: user, activity: activity} do
900 {:ok, activity} = CommonAPI.pin(activity.id, user)
901
902 user = refresh_record(user)
903
904 assert {:ok, _} = CommonAPI.delete(activity.id, user)
905
906 user = refresh_record(user)
907
908 assert user.pinned_objects == %{}
909 end
910
911 test "ephemeral activity won't be deleted if was pinned", %{user: user} do
912 {:ok, activity} = CommonAPI.post(user, %{status: "Hello!", expires_in: 601})
913
914 assert Pleroma.Workers.PurgeExpiredActivity.get_expiration(activity.id)
915
916 {:ok, _activity} = CommonAPI.pin(activity.id, user)
917 refute Pleroma.Workers.PurgeExpiredActivity.get_expiration(activity.id)
918
919 user = refresh_record(user)
920 {:ok, _} = CommonAPI.unpin(activity.id, user)
921
922 # recreates expiration job on unpin
923 assert Pleroma.Workers.PurgeExpiredActivity.get_expiration(activity.id)
924 end
925
926 test "ephemeral activity deletion job won't be deleted on pinning error", %{
927 user: user,
928 activity: activity
929 } do
930 clear_config([:instance, :max_pinned_statuses], 1)
931
932 {:ok, _activity} = CommonAPI.pin(activity.id, user)
933
934 {:ok, activity2} = CommonAPI.post(user, %{status: "another status", expires_in: 601})
935
936 assert Pleroma.Workers.PurgeExpiredActivity.get_expiration(activity2.id)
937
938 user = refresh_record(user)
939 {:error, :pinned_statuses_limit_reached} = CommonAPI.pin(activity2.id, user)
940
941 assert Pleroma.Workers.PurgeExpiredActivity.get_expiration(activity2.id)
942 end
943 end
944
945 describe "mute tests" do
946 setup do
947 user = insert(:user)
948
949 activity = insert(:note_activity)
950
951 [user: user, activity: activity]
952 end
953
954 test "marks notifications as read after mute" do
955 author = insert(:user)
956 activity = insert(:note_activity, user: author)
957
958 friend1 = insert(:user)
959 friend2 = insert(:user)
960
961 {:ok, reply_activity} =
962 CommonAPI.post(
963 friend2,
964 %{
965 status: "@#{author.nickname} @#{friend1.nickname} test reply",
966 in_reply_to_status_id: activity.id
967 }
968 )
969
970 {:ok, favorite_activity} = CommonAPI.favorite(friend2, activity.id)
971 {:ok, repeat_activity} = CommonAPI.repeat(activity.id, friend1)
972
973 assert Repo.aggregate(
974 from(n in Notification, where: n.seen == false and n.user_id == ^friend1.id),
975 :count
976 ) == 1
977
978 unread_notifications =
979 Repo.all(from(n in Notification, where: n.seen == false, where: n.user_id == ^author.id))
980
981 assert Enum.any?(unread_notifications, fn n ->
982 n.type == "favourite" && n.activity_id == favorite_activity.id
983 end)
984
985 assert Enum.any?(unread_notifications, fn n ->
986 n.type == "reblog" && n.activity_id == repeat_activity.id
987 end)
988
989 assert Enum.any?(unread_notifications, fn n ->
990 n.type == "mention" && n.activity_id == reply_activity.id
991 end)
992
993 {:ok, _} = CommonAPI.add_mute(author, activity)
994 assert CommonAPI.thread_muted?(author, activity)
995
996 assert Repo.aggregate(
997 from(n in Notification, where: n.seen == false and n.user_id == ^friend1.id),
998 :count
999 ) == 1
1000
1001 read_notifications =
1002 Repo.all(from(n in Notification, where: n.seen == true, where: n.user_id == ^author.id))
1003
1004 assert Enum.any?(read_notifications, fn n ->
1005 n.type == "favourite" && n.activity_id == favorite_activity.id
1006 end)
1007
1008 assert Enum.any?(read_notifications, fn n ->
1009 n.type == "reblog" && n.activity_id == repeat_activity.id
1010 end)
1011
1012 assert Enum.any?(read_notifications, fn n ->
1013 n.type == "mention" && n.activity_id == reply_activity.id
1014 end)
1015 end
1016
1017 test "add mute", %{user: user, activity: activity} do
1018 {:ok, _} = CommonAPI.add_mute(user, activity)
1019 assert CommonAPI.thread_muted?(user, activity)
1020 end
1021
1022 test "add expiring mute", %{user: user, activity: activity} do
1023 {:ok, _} = CommonAPI.add_mute(user, activity, %{expires_in: 60})
1024 assert CommonAPI.thread_muted?(user, activity)
1025
1026 worker = Pleroma.Workers.MuteExpireWorker
1027 args = %{"op" => "unmute_conversation", "user_id" => user.id, "activity_id" => activity.id}
1028
1029 assert_enqueued(
1030 worker: worker,
1031 args: args
1032 )
1033
1034 assert :ok = perform_job(worker, args)
1035 refute CommonAPI.thread_muted?(user, activity)
1036 end
1037
1038 test "remove mute", %{user: user, activity: activity} do
1039 CommonAPI.add_mute(user, activity)
1040 {:ok, _} = CommonAPI.remove_mute(user, activity)
1041 refute CommonAPI.thread_muted?(user, activity)
1042 end
1043
1044 test "remove mute by ids", %{user: user, activity: activity} do
1045 CommonAPI.add_mute(user, activity)
1046 {:ok, _} = CommonAPI.remove_mute(user.id, activity.id)
1047 refute CommonAPI.thread_muted?(user, activity)
1048 end
1049
1050 test "check that mutes can't be duplicate", %{user: user, activity: activity} do
1051 CommonAPI.add_mute(user, activity)
1052 {:error, _} = CommonAPI.add_mute(user, activity)
1053 end
1054 end
1055
1056 describe "reports" do
1057 test "creates a report" do
1058 reporter = insert(:user)
1059 target_user = insert(:user)
1060
1061 {:ok, activity} = CommonAPI.post(target_user, %{status: "foobar"})
1062
1063 reporter_ap_id = reporter.ap_id
1064 target_ap_id = target_user.ap_id
1065 activity_ap_id = activity.data["id"]
1066 comment = "foobar"
1067
1068 report_data = %{
1069 account_id: target_user.id,
1070 comment: comment,
1071 status_ids: [activity.id]
1072 }
1073
1074 note_obj = %{
1075 "type" => "Note",
1076 "id" => activity_ap_id,
1077 "content" => "foobar",
1078 "published" => activity.object.data["published"],
1079 "actor" => AccountView.render("show.json", %{user: target_user})
1080 }
1081
1082 assert {:ok, flag_activity} = CommonAPI.report(reporter, report_data)
1083
1084 assert %Activity{
1085 actor: ^reporter_ap_id,
1086 data: %{
1087 "type" => "Flag",
1088 "content" => ^comment,
1089 "object" => [^target_ap_id, ^note_obj],
1090 "state" => "open"
1091 }
1092 } = flag_activity
1093 end
1094
1095 test "updates report state" do
1096 [reporter, target_user] = insert_pair(:user)
1097 activity = insert(:note_activity, user: target_user)
1098
1099 {:ok, %Activity{id: report_id}} =
1100 CommonAPI.report(reporter, %{
1101 account_id: target_user.id,
1102 comment: "I feel offended",
1103 status_ids: [activity.id]
1104 })
1105
1106 {:ok, report} = CommonAPI.update_report_state(report_id, "resolved")
1107
1108 assert report.data["state"] == "resolved"
1109
1110 [reported_user, activity_id] = report.data["object"]
1111
1112 assert reported_user == target_user.ap_id
1113 assert activity_id == activity.data["id"]
1114 end
1115
1116 test "does not update report state when state is unsupported" do
1117 [reporter, target_user] = insert_pair(:user)
1118 activity = insert(:note_activity, user: target_user)
1119
1120 {:ok, %Activity{id: report_id}} =
1121 CommonAPI.report(reporter, %{
1122 account_id: target_user.id,
1123 comment: "I feel offended",
1124 status_ids: [activity.id]
1125 })
1126
1127 assert CommonAPI.update_report_state(report_id, "test") == {:error, "Unsupported state"}
1128 end
1129
1130 test "updates state of multiple reports" do
1131 [reporter, target_user] = insert_pair(:user)
1132 activity = insert(:note_activity, user: target_user)
1133
1134 {:ok, %Activity{id: first_report_id}} =
1135 CommonAPI.report(reporter, %{
1136 account_id: target_user.id,
1137 comment: "I feel offended",
1138 status_ids: [activity.id]
1139 })
1140
1141 {:ok, %Activity{id: second_report_id}} =
1142 CommonAPI.report(reporter, %{
1143 account_id: target_user.id,
1144 comment: "I feel very offended!",
1145 status_ids: [activity.id]
1146 })
1147
1148 {:ok, report_ids} =
1149 CommonAPI.update_report_state([first_report_id, second_report_id], "resolved")
1150
1151 first_report = Activity.get_by_id(first_report_id)
1152 second_report = Activity.get_by_id(second_report_id)
1153
1154 assert report_ids -- [first_report_id, second_report_id] == []
1155 assert first_report.data["state"] == "resolved"
1156 assert second_report.data["state"] == "resolved"
1157 end
1158 end
1159
1160 describe "reblog muting" do
1161 setup do
1162 muter = insert(:user)
1163
1164 muted = insert(:user)
1165
1166 [muter: muter, muted: muted]
1167 end
1168
1169 test "add a reblog mute", %{muter: muter, muted: muted} do
1170 {:ok, _reblog_mute} = CommonAPI.hide_reblogs(muter, muted)
1171
1172 assert User.showing_reblogs?(muter, muted) == false
1173 end
1174
1175 test "remove a reblog mute", %{muter: muter, muted: muted} do
1176 {:ok, _reblog_mute} = CommonAPI.hide_reblogs(muter, muted)
1177 {:ok, _reblog_mute} = CommonAPI.show_reblogs(muter, muted)
1178
1179 assert User.showing_reblogs?(muter, muted) == true
1180 end
1181 end
1182
1183 describe "follow/2" do
1184 test "directly follows a non-locked local user" do
1185 [follower, followed] = insert_pair(:user)
1186 {:ok, follower, followed, _} = CommonAPI.follow(follower, followed)
1187
1188 assert User.following?(follower, followed)
1189 end
1190 end
1191
1192 describe "unfollow/2" do
1193 test "also unsubscribes a user" do
1194 [follower, followed] = insert_pair(:user)
1195 {:ok, follower, followed, _} = CommonAPI.follow(follower, followed)
1196 {:ok, _subscription} = User.subscribe(follower, followed)
1197
1198 assert User.subscribed_to?(follower, followed)
1199
1200 {:ok, follower} = CommonAPI.unfollow(follower, followed)
1201
1202 refute User.subscribed_to?(follower, followed)
1203 end
1204
1205 test "cancels a pending follow for a local user" do
1206 follower = insert(:user)
1207 followed = insert(:user, is_locked: true)
1208
1209 assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} =
1210 CommonAPI.follow(follower, followed)
1211
1212 assert User.get_follow_state(follower, followed) == :follow_pending
1213 assert {:ok, follower} = CommonAPI.unfollow(follower, followed)
1214 assert User.get_follow_state(follower, followed) == nil
1215
1216 assert %{id: ^activity_id, data: %{"state" => "cancelled"}} =
1217 Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(follower, followed)
1218
1219 assert %{
1220 data: %{
1221 "type" => "Undo",
1222 "object" => %{"type" => "Follow", "state" => "cancelled"}
1223 }
1224 } = Pleroma.Web.ActivityPub.Utils.fetch_latest_undo(follower)
1225 end
1226
1227 test "cancels a pending follow for a remote user" do
1228 follower = insert(:user)
1229 followed = insert(:user, is_locked: true, local: false, ap_enabled: true)
1230
1231 assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} =
1232 CommonAPI.follow(follower, followed)
1233
1234 assert User.get_follow_state(follower, followed) == :follow_pending
1235 assert {:ok, follower} = CommonAPI.unfollow(follower, followed)
1236 assert User.get_follow_state(follower, followed) == nil
1237
1238 assert %{id: ^activity_id, data: %{"state" => "cancelled"}} =
1239 Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(follower, followed)
1240
1241 assert %{
1242 data: %{
1243 "type" => "Undo",
1244 "object" => %{"type" => "Follow", "state" => "cancelled"}
1245 }
1246 } = Pleroma.Web.ActivityPub.Utils.fetch_latest_undo(follower)
1247 end
1248 end
1249
1250 describe "accept_follow_request/2" do
1251 test "after acceptance, it sets all existing pending follow request states to 'accept'" do
1252 user = insert(:user, is_locked: true)
1253 follower = insert(:user)
1254 follower_two = insert(:user)
1255
1256 {:ok, _, _, follow_activity} = CommonAPI.follow(follower, user)
1257 {:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user)
1258 {:ok, _, _, follow_activity_three} = CommonAPI.follow(follower_two, user)
1259
1260 assert follow_activity.data["state"] == "pending"
1261 assert follow_activity_two.data["state"] == "pending"
1262 assert follow_activity_three.data["state"] == "pending"
1263
1264 {:ok, _follower} = CommonAPI.accept_follow_request(follower, user)
1265
1266 assert Repo.get(Activity, follow_activity.id).data["state"] == "accept"
1267 assert Repo.get(Activity, follow_activity_two.id).data["state"] == "accept"
1268 assert Repo.get(Activity, follow_activity_three.id).data["state"] == "pending"
1269 end
1270
1271 test "after rejection, it sets all existing pending follow request states to 'reject'" do
1272 user = insert(:user, is_locked: true)
1273 follower = insert(:user)
1274 follower_two = insert(:user)
1275
1276 {:ok, _, _, follow_activity} = CommonAPI.follow(follower, user)
1277 {:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user)
1278 {:ok, _, _, follow_activity_three} = CommonAPI.follow(follower_two, user)
1279
1280 assert follow_activity.data["state"] == "pending"
1281 assert follow_activity_two.data["state"] == "pending"
1282 assert follow_activity_three.data["state"] == "pending"
1283
1284 {:ok, _follower} = CommonAPI.reject_follow_request(follower, user)
1285
1286 assert Repo.get(Activity, follow_activity.id).data["state"] == "reject"
1287 assert Repo.get(Activity, follow_activity_two.id).data["state"] == "reject"
1288 assert Repo.get(Activity, follow_activity_three.id).data["state"] == "pending"
1289 end
1290
1291 test "doesn't create a following relationship if the corresponding follow request doesn't exist" do
1292 user = insert(:user, is_locked: true)
1293 not_follower = insert(:user)
1294 CommonAPI.accept_follow_request(not_follower, user)
1295
1296 assert Pleroma.FollowingRelationship.following?(not_follower, user) == false
1297 end
1298 end
1299
1300 describe "vote/3" do
1301 test "does not allow to vote twice" do
1302 user = insert(:user)
1303 other_user = insert(:user)
1304
1305 {:ok, activity} =
1306 CommonAPI.post(user, %{
1307 status: "Am I cute?",
1308 poll: %{options: ["Yes", "No"], expires_in: 20}
1309 })
1310
1311 object = Object.normalize(activity, fetch: false)
1312
1313 {:ok, _, object} = CommonAPI.vote(other_user, object, [0])
1314
1315 assert {:error, "Already voted"} == CommonAPI.vote(other_user, object, [1])
1316 end
1317 end
1318
1319 describe "listen/2" do
1320 test "returns a valid activity" do
1321 user = insert(:user)
1322
1323 {:ok, activity} =
1324 CommonAPI.listen(user, %{
1325 title: "lain radio episode 1",
1326 album: "lain radio",
1327 artist: "lain",
1328 length: 180_000
1329 })
1330
1331 object = Object.normalize(activity, fetch: false)
1332
1333 assert object.data["title"] == "lain radio episode 1"
1334
1335 assert Visibility.get_visibility(activity) == "public"
1336 end
1337
1338 test "respects visibility=private" do
1339 user = insert(:user)
1340
1341 {:ok, activity} =
1342 CommonAPI.listen(user, %{
1343 title: "lain radio episode 1",
1344 album: "lain radio",
1345 artist: "lain",
1346 length: 180_000,
1347 visibility: "private"
1348 })
1349
1350 object = Object.normalize(activity, fetch: false)
1351
1352 assert object.data["title"] == "lain radio episode 1"
1353
1354 assert Visibility.get_visibility(activity) == "private"
1355 end
1356 end
1357
1358 describe "get_user/1" do
1359 test "gets user by ap_id" do
1360 user = insert(:user)
1361 assert CommonAPI.get_user(user.ap_id) == user
1362 end
1363
1364 test "gets user by guessed nickname" do
1365 user = insert(:user, ap_id: "", nickname: "mario@mushroom.kingdom")
1366 assert CommonAPI.get_user("https://mushroom.kingdom/users/mario") == user
1367 end
1368
1369 test "fallback" do
1370 assert %User{
1371 name: "",
1372 ap_id: "",
1373 nickname: "erroruser@example.com"
1374 } = CommonAPI.get_user("")
1375 end
1376 end
1377
1378 describe "with `local` visibility" do
1379 setup do: clear_config([:instance, :federating], true)
1380
1381 test "post" do
1382 user = insert(:user)
1383
1384 with_mock Pleroma.Web.Federator, publish: fn _ -> :ok end do
1385 {:ok, activity} = CommonAPI.post(user, %{status: "#2hu #2HU", visibility: "local"})
1386
1387 assert Visibility.is_local_public?(activity)
1388 assert_not_called(Pleroma.Web.Federator.publish(activity))
1389 end
1390 end
1391
1392 test "delete" do
1393 user = insert(:user)
1394
1395 {:ok, %Activity{id: activity_id}} =
1396 CommonAPI.post(user, %{status: "#2hu #2HU", visibility: "local"})
1397
1398 with_mock Pleroma.Web.Federator, publish: fn _ -> :ok end do
1399 assert {:ok, %Activity{data: %{"deleted_activity_id" => ^activity_id}} = activity} =
1400 CommonAPI.delete(activity_id, user)
1401
1402 assert Visibility.is_local_public?(activity)
1403 assert_not_called(Pleroma.Web.Federator.publish(activity))
1404 end
1405 end
1406
1407 test "repeat" do
1408 user = insert(:user)
1409 other_user = insert(:user)
1410
1411 {:ok, %Activity{id: activity_id}} =
1412 CommonAPI.post(other_user, %{status: "cofe", visibility: "local"})
1413
1414 with_mock Pleroma.Web.Federator, publish: fn _ -> :ok end do
1415 assert {:ok, %Activity{data: %{"type" => "Announce"}} = activity} =
1416 CommonAPI.repeat(activity_id, user)
1417
1418 assert Visibility.is_local_public?(activity)
1419 refute called(Pleroma.Web.Federator.publish(activity))
1420 end
1421 end
1422
1423 test "unrepeat" do
1424 user = insert(:user)
1425 other_user = insert(:user)
1426
1427 {:ok, %Activity{id: activity_id}} =
1428 CommonAPI.post(other_user, %{status: "cofe", visibility: "local"})
1429
1430 assert {:ok, _} = CommonAPI.repeat(activity_id, user)
1431
1432 with_mock Pleroma.Web.Federator, publish: fn _ -> :ok end do
1433 assert {:ok, %Activity{data: %{"type" => "Undo"}} = activity} =
1434 CommonAPI.unrepeat(activity_id, user)
1435
1436 assert Visibility.is_local_public?(activity)
1437 refute called(Pleroma.Web.Federator.publish(activity))
1438 end
1439 end
1440
1441 test "favorite" do
1442 user = insert(:user)
1443 other_user = insert(:user)
1444
1445 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe", visibility: "local"})
1446
1447 with_mock Pleroma.Web.Federator, publish: fn _ -> :ok end do
1448 assert {:ok, %Activity{data: %{"type" => "Like"}} = activity} =
1449 CommonAPI.favorite(user, activity.id)
1450
1451 assert Visibility.is_local_public?(activity)
1452 refute called(Pleroma.Web.Federator.publish(activity))
1453 end
1454 end
1455
1456 test "unfavorite" do
1457 user = insert(:user)
1458 other_user = insert(:user)
1459
1460 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe", visibility: "local"})
1461
1462 {:ok, %Activity{}} = CommonAPI.favorite(user, activity.id)
1463
1464 with_mock Pleroma.Web.Federator, publish: fn _ -> :ok end do
1465 assert {:ok, activity} = CommonAPI.unfavorite(activity.id, user)
1466 assert Visibility.is_local_public?(activity)
1467 refute called(Pleroma.Web.Federator.publish(activity))
1468 end
1469 end
1470
1471 test "react_with_emoji" do
1472 user = insert(:user)
1473 other_user = insert(:user)
1474 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe", visibility: "local"})
1475
1476 with_mock Pleroma.Web.Federator, publish: fn _ -> :ok end do
1477 assert {:ok, %Activity{data: %{"type" => "EmojiReact"}} = activity} =
1478 CommonAPI.react_with_emoji(activity.id, user, "👍")
1479
1480 assert Visibility.is_local_public?(activity)
1481 refute called(Pleroma.Web.Federator.publish(activity))
1482 end
1483 end
1484
1485 test "unreact_with_emoji" do
1486 user = insert(:user)
1487 other_user = insert(:user)
1488 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe", visibility: "local"})
1489
1490 {:ok, _reaction} = CommonAPI.react_with_emoji(activity.id, user, "👍")
1491
1492 with_mock Pleroma.Web.Federator, publish: fn _ -> :ok end do
1493 assert {:ok, %Activity{data: %{"type" => "Undo"}} = activity} =
1494 CommonAPI.unreact_with_emoji(activity.id, user, "👍")
1495
1496 assert Visibility.is_local_public?(activity)
1497 refute called(Pleroma.Web.Federator.publish(activity))
1498 end
1499 end
1500 end
1501 end