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