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