4a10a5bc488e651ffbe2b1deb683f5fb9e62a02d
[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=\"#{
213 other_user.id
214 }\" 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 assert object.data["source"] == post
592 end
593
594 test "it filters out obviously bad tags when accepting a post as Markdown" do
595 user = insert(:user)
596
597 post = "<p><b>2hu</b></p><script>alert('xss')</script>"
598
599 {:ok, activity} =
600 CommonAPI.post(user, %{
601 status: post,
602 content_type: "text/markdown"
603 })
604
605 object = Object.normalize(activity, fetch: false)
606
607 assert object.data["content"] == "<p><b>2hu</b></p>"
608 assert object.data["source"] == post
609 end
610
611 test "it does not allow replies to direct messages that are not direct messages themselves" do
612 user = insert(:user)
613
614 {:ok, activity} = CommonAPI.post(user, %{status: "suya..", visibility: "direct"})
615
616 assert {:ok, _} =
617 CommonAPI.post(user, %{
618 status: "suya..",
619 visibility: "direct",
620 in_reply_to_status_id: activity.id
621 })
622
623 Enum.each(["public", "private", "unlisted"], fn visibility ->
624 assert {:error, "The message visibility must be direct"} =
625 CommonAPI.post(user, %{
626 status: "suya..",
627 visibility: visibility,
628 in_reply_to_status_id: activity.id
629 })
630 end)
631 end
632
633 test "replying with a direct message will NOT auto-add the author of the reply to the recipient list" do
634 user = insert(:user)
635 other_user = insert(:user)
636 third_user = insert(:user)
637
638 {:ok, post} = CommonAPI.post(user, %{status: "I'm stupid"})
639
640 {:ok, open_answer} =
641 CommonAPI.post(other_user, %{status: "No ur smart", in_reply_to_status_id: post.id})
642
643 # The OP is implicitly added
644 assert user.ap_id in open_answer.recipients
645
646 {:ok, secret_answer} =
647 CommonAPI.post(other_user, %{
648 status: "lol, that guy really is stupid, right, @#{third_user.nickname}?",
649 in_reply_to_status_id: post.id,
650 visibility: "direct"
651 })
652
653 assert third_user.ap_id in secret_answer.recipients
654
655 # The OP is not added
656 refute user.ap_id in secret_answer.recipients
657 end
658
659 test "it allows to address a list" do
660 user = insert(:user)
661 {:ok, list} = Pleroma.List.create("foo", user)
662
663 {:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})
664
665 assert activity.data["bcc"] == [list.ap_id]
666 assert activity.recipients == [list.ap_id, user.ap_id]
667 assert activity.data["listMessage"] == list.ap_id
668 end
669
670 test "it returns error when status is empty and no attachments" do
671 user = insert(:user)
672
673 assert {:error, "Cannot post an empty status without attachments"} =
674 CommonAPI.post(user, %{status: ""})
675 end
676
677 test "it validates character limits are correctly enforced" do
678 clear_config([:instance, :limit], 5)
679
680 user = insert(:user)
681
682 assert {:error, "The status is over the character limit"} =
683 CommonAPI.post(user, %{status: "foobar"})
684
685 assert {:ok, _activity} = CommonAPI.post(user, %{status: "12345"})
686 end
687
688 test "it can handle activities that expire" do
689 user = insert(:user)
690
691 expires_at = DateTime.add(DateTime.utc_now(), 1_000_000)
692
693 assert {:ok, activity} = CommonAPI.post(user, %{status: "chai", expires_in: 1_000_000})
694
695 assert_enqueued(
696 worker: Pleroma.Workers.PurgeExpiredActivity,
697 args: %{activity_id: activity.id},
698 scheduled_at: expires_at
699 )
700 end
701 end
702
703 describe "reactions" do
704 test "reacting to a status with an emoji" do
705 user = insert(:user)
706 other_user = insert(:user)
707
708 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
709
710 {:ok, reaction} = CommonAPI.react_with_emoji(activity.id, user, "👍")
711
712 assert reaction.data["actor"] == user.ap_id
713 assert reaction.data["content"] == "👍"
714
715 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
716
717 {:error, _} = CommonAPI.react_with_emoji(activity.id, user, ".")
718 end
719
720 test "unreacting to a status with an emoji" do
721 user = insert(:user)
722 other_user = insert(:user)
723
724 clear_config([:instance, :federating], true)
725
726 with_mock Pleroma.Web.Federator,
727 publish: fn _ -> nil end do
728 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
729 {:ok, reaction} = CommonAPI.react_with_emoji(activity.id, user, "👍")
730
731 {:ok, unreaction} = CommonAPI.unreact_with_emoji(activity.id, user, "👍")
732
733 assert unreaction.data["type"] == "Undo"
734 assert unreaction.data["object"] == reaction.data["id"]
735 assert unreaction.local
736
737 # On federation, it contains the undone (and deleted) object
738 unreaction_with_object = %{
739 unreaction
740 | data: Map.put(unreaction.data, "object", reaction.data)
741 }
742
743 assert called(Pleroma.Web.Federator.publish(unreaction_with_object))
744 end
745 end
746
747 test "repeating a status" do
748 user = insert(:user)
749 other_user = insert(:user)
750
751 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
752
753 {:ok, %Activity{} = announce_activity} = CommonAPI.repeat(activity.id, user)
754 assert Visibility.is_public?(announce_activity)
755 end
756
757 test "can't repeat a repeat" do
758 user = insert(:user)
759 other_user = insert(:user)
760 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
761
762 {:ok, %Activity{} = announce} = CommonAPI.repeat(activity.id, other_user)
763
764 refute match?({:ok, %Activity{}}, CommonAPI.repeat(announce.id, user))
765 end
766
767 test "repeating a status privately" do
768 user = insert(:user)
769 other_user = insert(:user)
770
771 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
772
773 {:ok, %Activity{} = announce_activity} =
774 CommonAPI.repeat(activity.id, user, %{visibility: "private"})
775
776 assert Visibility.is_private?(announce_activity)
777 refute Visibility.visible_for_user?(announce_activity, nil)
778 end
779
780 test "author can repeat own private statuses" do
781 author = insert(:user)
782 follower = insert(:user)
783 CommonAPI.follow(follower, author)
784
785 {:ok, activity} = CommonAPI.post(author, %{status: "cofe", visibility: "private"})
786
787 {:ok, %Activity{} = announce_activity} = CommonAPI.repeat(activity.id, author)
788
789 assert Visibility.is_private?(announce_activity)
790 refute Visibility.visible_for_user?(announce_activity, nil)
791
792 assert Visibility.visible_for_user?(activity, follower)
793 assert {:error, :not_found} = CommonAPI.repeat(activity.id, follower)
794 end
795
796 test "favoriting a status" do
797 user = insert(:user)
798 other_user = insert(:user)
799
800 {:ok, post_activity} = CommonAPI.post(other_user, %{status: "cofe"})
801
802 {:ok, %Activity{data: data}} = CommonAPI.favorite(user, post_activity.id)
803 assert data["type"] == "Like"
804 assert data["actor"] == user.ap_id
805 assert data["object"] == post_activity.data["object"]
806 end
807
808 test "retweeting a status twice returns the status" do
809 user = insert(:user)
810 other_user = insert(:user)
811
812 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
813 {:ok, %Activity{} = announce} = CommonAPI.repeat(activity.id, user)
814 {:ok, ^announce} = CommonAPI.repeat(activity.id, user)
815 end
816
817 test "favoriting a status twice returns ok, but without the like activity" do
818 user = insert(:user)
819 other_user = insert(:user)
820
821 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
822 {:ok, %Activity{}} = CommonAPI.favorite(user, activity.id)
823 assert {:ok, :already_liked} = CommonAPI.favorite(user, activity.id)
824 end
825 end
826
827 describe "pinned statuses" do
828 setup do
829 clear_config([:instance, :max_pinned_statuses], 1)
830
831 user = insert(:user)
832 {:ok, activity} = CommonAPI.post(user, %{status: "HI!!!"})
833
834 [user: user, activity: activity]
835 end
836
837 test "activity not found error", %{user: user} do
838 assert {:error, :not_found} = CommonAPI.pin("id", user)
839 end
840
841 test "pin status", %{user: user, activity: activity} do
842 assert {:ok, ^activity} = CommonAPI.pin(activity.id, user)
843
844 %{data: %{"id" => object_id}} = Object.normalize(activity)
845 user = refresh_record(user)
846
847 assert user.pinned_objects |> Map.keys() == [object_id]
848 end
849
850 test "pin poll", %{user: user} do
851 {:ok, activity} =
852 CommonAPI.post(user, %{
853 status: "How is fediverse today?",
854 poll: %{options: ["Absolutely outstanding", "Not good"], expires_in: 20}
855 })
856
857 assert {:ok, ^activity} = CommonAPI.pin(activity.id, user)
858
859 %{data: %{"id" => object_id}} = Object.normalize(activity)
860
861 user = refresh_record(user)
862
863 assert user.pinned_objects |> Map.keys() == [object_id]
864 end
865
866 test "unlisted statuses can be pinned", %{user: user} do
867 {:ok, activity} = CommonAPI.post(user, %{status: "HI!!!", visibility: "unlisted"})
868 assert {:ok, ^activity} = CommonAPI.pin(activity.id, user)
869 end
870
871 test "only self-authored can be pinned", %{activity: activity} do
872 user = insert(:user)
873
874 assert {:error, :ownership_error} = CommonAPI.pin(activity.id, user)
875 end
876
877 test "max pinned statuses", %{user: user, activity: activity_one} do
878 {:ok, activity_two} = CommonAPI.post(user, %{status: "HI!!!"})
879
880 assert {:ok, ^activity_one} = CommonAPI.pin(activity_one.id, user)
881
882 user = refresh_record(user)
883
884 assert {:error, :pinned_statuses_limit_reached} = CommonAPI.pin(activity_two.id, user)
885 end
886
887 test "only public can be pinned", %{user: user} do
888 {:ok, activity} = CommonAPI.post(user, %{status: "private status", visibility: "private"})
889 {:error, :visibility_error} = CommonAPI.pin(activity.id, user)
890 end
891
892 test "unpin status", %{user: user, activity: activity} do
893 {:ok, activity} = CommonAPI.pin(activity.id, user)
894
895 user = refresh_record(user)
896
897 id = activity.id
898
899 assert match?({:ok, %{id: ^id}}, CommonAPI.unpin(activity.id, user))
900
901 user = refresh_record(user)
902
903 assert user.pinned_objects == %{}
904 end
905
906 test "should unpin when deleting a status", %{user: user, activity: activity} do
907 {:ok, activity} = CommonAPI.pin(activity.id, user)
908
909 user = refresh_record(user)
910
911 assert {:ok, _} = CommonAPI.delete(activity.id, user)
912
913 user = refresh_record(user)
914
915 assert user.pinned_objects == %{}
916 end
917
918 test "ephemeral activity won't be deleted if was pinned", %{user: user} do
919 {:ok, activity} = CommonAPI.post(user, %{status: "Hello!", expires_in: 601})
920
921 assert Pleroma.Workers.PurgeExpiredActivity.get_expiration(activity.id)
922
923 {:ok, _activity} = CommonAPI.pin(activity.id, user)
924 refute Pleroma.Workers.PurgeExpiredActivity.get_expiration(activity.id)
925
926 user = refresh_record(user)
927 {:ok, _} = CommonAPI.unpin(activity.id, user)
928
929 # recreates expiration job on unpin
930 assert Pleroma.Workers.PurgeExpiredActivity.get_expiration(activity.id)
931 end
932
933 test "ephemeral activity deletion job won't be deleted on pinning error", %{
934 user: user,
935 activity: activity
936 } do
937 clear_config([:instance, :max_pinned_statuses], 1)
938
939 {:ok, _activity} = CommonAPI.pin(activity.id, user)
940
941 {:ok, activity2} = CommonAPI.post(user, %{status: "another status", expires_in: 601})
942
943 assert Pleroma.Workers.PurgeExpiredActivity.get_expiration(activity2.id)
944
945 user = refresh_record(user)
946 {:error, :pinned_statuses_limit_reached} = CommonAPI.pin(activity2.id, user)
947
948 assert Pleroma.Workers.PurgeExpiredActivity.get_expiration(activity2.id)
949 end
950 end
951
952 describe "mute tests" do
953 setup do
954 user = insert(:user)
955
956 activity = insert(:note_activity)
957
958 [user: user, activity: activity]
959 end
960
961 test "marks notifications as read after mute" do
962 author = insert(:user)
963 activity = insert(:note_activity, user: author)
964
965 friend1 = insert(:user)
966 friend2 = insert(:user)
967
968 {:ok, reply_activity} =
969 CommonAPI.post(
970 friend2,
971 %{
972 status: "@#{author.nickname} @#{friend1.nickname} test reply",
973 in_reply_to_status_id: activity.id
974 }
975 )
976
977 {:ok, favorite_activity} = CommonAPI.favorite(friend2, activity.id)
978 {:ok, repeat_activity} = CommonAPI.repeat(activity.id, friend1)
979
980 assert Repo.aggregate(
981 from(n in Notification, where: n.seen == false and n.user_id == ^friend1.id),
982 :count
983 ) == 1
984
985 unread_notifications =
986 Repo.all(from(n in Notification, where: n.seen == false, where: n.user_id == ^author.id))
987
988 assert Enum.any?(unread_notifications, fn n ->
989 n.type == "favourite" && n.activity_id == favorite_activity.id
990 end)
991
992 assert Enum.any?(unread_notifications, fn n ->
993 n.type == "reblog" && n.activity_id == repeat_activity.id
994 end)
995
996 assert Enum.any?(unread_notifications, fn n ->
997 n.type == "mention" && n.activity_id == reply_activity.id
998 end)
999
1000 {:ok, _} = CommonAPI.add_mute(author, activity)
1001 assert CommonAPI.thread_muted?(author, activity)
1002
1003 assert Repo.aggregate(
1004 from(n in Notification, where: n.seen == false and n.user_id == ^friend1.id),
1005 :count
1006 ) == 1
1007
1008 read_notifications =
1009 Repo.all(from(n in Notification, where: n.seen == true, where: n.user_id == ^author.id))
1010
1011 assert Enum.any?(read_notifications, fn n ->
1012 n.type == "favourite" && n.activity_id == favorite_activity.id
1013 end)
1014
1015 assert Enum.any?(read_notifications, fn n ->
1016 n.type == "reblog" && n.activity_id == repeat_activity.id
1017 end)
1018
1019 assert Enum.any?(read_notifications, fn n ->
1020 n.type == "mention" && n.activity_id == reply_activity.id
1021 end)
1022 end
1023
1024 test "add mute", %{user: user, activity: activity} do
1025 {:ok, _} = CommonAPI.add_mute(user, activity)
1026 assert CommonAPI.thread_muted?(user, activity)
1027 end
1028
1029 test "add expiring mute", %{user: user, activity: activity} do
1030 {:ok, _} = CommonAPI.add_mute(user, activity, %{expires_in: 60})
1031 assert CommonAPI.thread_muted?(user, activity)
1032
1033 worker = Pleroma.Workers.MuteExpireWorker
1034 args = %{"op" => "unmute_conversation", "user_id" => user.id, "activity_id" => activity.id}
1035
1036 assert_enqueued(
1037 worker: worker,
1038 args: args
1039 )
1040
1041 assert :ok = perform_job(worker, args)
1042 refute CommonAPI.thread_muted?(user, activity)
1043 end
1044
1045 test "remove mute", %{user: user, activity: activity} do
1046 CommonAPI.add_mute(user, activity)
1047 {:ok, _} = CommonAPI.remove_mute(user, activity)
1048 refute CommonAPI.thread_muted?(user, activity)
1049 end
1050
1051 test "remove mute by ids", %{user: user, activity: activity} do
1052 CommonAPI.add_mute(user, activity)
1053 {:ok, _} = CommonAPI.remove_mute(user.id, activity.id)
1054 refute CommonAPI.thread_muted?(user, activity)
1055 end
1056
1057 test "check that mutes can't be duplicate", %{user: user, activity: activity} do
1058 CommonAPI.add_mute(user, activity)
1059 {:error, _} = CommonAPI.add_mute(user, activity)
1060 end
1061 end
1062
1063 describe "reports" do
1064 test "creates a report" do
1065 reporter = insert(:user)
1066 target_user = insert(:user)
1067
1068 {:ok, activity} = CommonAPI.post(target_user, %{status: "foobar"})
1069
1070 reporter_ap_id = reporter.ap_id
1071 target_ap_id = target_user.ap_id
1072 activity_ap_id = activity.data["id"]
1073 comment = "foobar"
1074
1075 report_data = %{
1076 account_id: target_user.id,
1077 comment: comment,
1078 status_ids: [activity.id]
1079 }
1080
1081 note_obj = %{
1082 "type" => "Note",
1083 "id" => activity_ap_id,
1084 "content" => "foobar",
1085 "published" => activity.object.data["published"],
1086 "actor" => AccountView.render("show.json", %{user: target_user})
1087 }
1088
1089 assert {:ok, flag_activity} = CommonAPI.report(reporter, report_data)
1090
1091 assert %Activity{
1092 actor: ^reporter_ap_id,
1093 data: %{
1094 "type" => "Flag",
1095 "content" => ^comment,
1096 "object" => [^target_ap_id, ^note_obj],
1097 "state" => "open"
1098 }
1099 } = flag_activity
1100 end
1101
1102 test "updates report state" do
1103 [reporter, target_user] = insert_pair(:user)
1104 activity = insert(:note_activity, user: target_user)
1105
1106 {:ok, %Activity{id: report_id}} =
1107 CommonAPI.report(reporter, %{
1108 account_id: target_user.id,
1109 comment: "I feel offended",
1110 status_ids: [activity.id]
1111 })
1112
1113 {:ok, report} = CommonAPI.update_report_state(report_id, "resolved")
1114
1115 assert report.data["state"] == "resolved"
1116
1117 [reported_user, activity_id] = report.data["object"]
1118
1119 assert reported_user == target_user.ap_id
1120 assert activity_id == activity.data["id"]
1121 end
1122
1123 test "does not update report state when state is unsupported" do
1124 [reporter, target_user] = insert_pair(:user)
1125 activity = insert(:note_activity, user: target_user)
1126
1127 {:ok, %Activity{id: report_id}} =
1128 CommonAPI.report(reporter, %{
1129 account_id: target_user.id,
1130 comment: "I feel offended",
1131 status_ids: [activity.id]
1132 })
1133
1134 assert CommonAPI.update_report_state(report_id, "test") == {:error, "Unsupported state"}
1135 end
1136
1137 test "updates state of multiple reports" do
1138 [reporter, target_user] = insert_pair(:user)
1139 activity = insert(:note_activity, user: target_user)
1140
1141 {:ok, %Activity{id: first_report_id}} =
1142 CommonAPI.report(reporter, %{
1143 account_id: target_user.id,
1144 comment: "I feel offended",
1145 status_ids: [activity.id]
1146 })
1147
1148 {:ok, %Activity{id: second_report_id}} =
1149 CommonAPI.report(reporter, %{
1150 account_id: target_user.id,
1151 comment: "I feel very offended!",
1152 status_ids: [activity.id]
1153 })
1154
1155 {:ok, report_ids} =
1156 CommonAPI.update_report_state([first_report_id, second_report_id], "resolved")
1157
1158 first_report = Activity.get_by_id(first_report_id)
1159 second_report = Activity.get_by_id(second_report_id)
1160
1161 assert report_ids -- [first_report_id, second_report_id] == []
1162 assert first_report.data["state"] == "resolved"
1163 assert second_report.data["state"] == "resolved"
1164 end
1165 end
1166
1167 describe "reblog muting" do
1168 setup do
1169 muter = insert(:user)
1170
1171 muted = insert(:user)
1172
1173 [muter: muter, muted: muted]
1174 end
1175
1176 test "add a reblog mute", %{muter: muter, muted: muted} do
1177 {:ok, _reblog_mute} = CommonAPI.hide_reblogs(muter, muted)
1178
1179 assert User.showing_reblogs?(muter, muted) == false
1180 end
1181
1182 test "remove a reblog mute", %{muter: muter, muted: muted} do
1183 {:ok, _reblog_mute} = CommonAPI.hide_reblogs(muter, muted)
1184 {:ok, _reblog_mute} = CommonAPI.show_reblogs(muter, muted)
1185
1186 assert User.showing_reblogs?(muter, muted) == true
1187 end
1188 end
1189
1190 describe "follow/2" do
1191 test "directly follows a non-locked local user" do
1192 [follower, followed] = insert_pair(:user)
1193 {:ok, follower, followed, _} = CommonAPI.follow(follower, followed)
1194
1195 assert User.following?(follower, followed)
1196 end
1197 end
1198
1199 describe "unfollow/2" do
1200 test "also unsubscribes a user" do
1201 [follower, followed] = insert_pair(:user)
1202 {:ok, follower, followed, _} = CommonAPI.follow(follower, followed)
1203 {:ok, _subscription} = User.subscribe(follower, followed)
1204
1205 assert User.subscribed_to?(follower, followed)
1206
1207 {:ok, follower} = CommonAPI.unfollow(follower, followed)
1208
1209 refute User.subscribed_to?(follower, followed)
1210 end
1211
1212 test "cancels a pending follow for a local user" do
1213 follower = insert(:user)
1214 followed = insert(:user, is_locked: true)
1215
1216 assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} =
1217 CommonAPI.follow(follower, followed)
1218
1219 assert User.get_follow_state(follower, followed) == :follow_pending
1220 assert {:ok, follower} = CommonAPI.unfollow(follower, followed)
1221 assert User.get_follow_state(follower, followed) == nil
1222
1223 assert %{id: ^activity_id, data: %{"state" => "cancelled"}} =
1224 Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(follower, followed)
1225
1226 assert %{
1227 data: %{
1228 "type" => "Undo",
1229 "object" => %{"type" => "Follow", "state" => "cancelled"}
1230 }
1231 } = Pleroma.Web.ActivityPub.Utils.fetch_latest_undo(follower)
1232 end
1233
1234 test "cancels a pending follow for a remote user" do
1235 follower = insert(:user)
1236 followed = insert(:user, is_locked: true, local: false, ap_enabled: true)
1237
1238 assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} =
1239 CommonAPI.follow(follower, followed)
1240
1241 assert User.get_follow_state(follower, followed) == :follow_pending
1242 assert {:ok, follower} = CommonAPI.unfollow(follower, followed)
1243 assert User.get_follow_state(follower, followed) == nil
1244
1245 assert %{id: ^activity_id, data: %{"state" => "cancelled"}} =
1246 Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(follower, followed)
1247
1248 assert %{
1249 data: %{
1250 "type" => "Undo",
1251 "object" => %{"type" => "Follow", "state" => "cancelled"}
1252 }
1253 } = Pleroma.Web.ActivityPub.Utils.fetch_latest_undo(follower)
1254 end
1255 end
1256
1257 describe "accept_follow_request/2" do
1258 test "after acceptance, it sets all existing pending follow request states to 'accept'" do
1259 user = insert(:user, is_locked: true)
1260 follower = insert(:user)
1261 follower_two = insert(:user)
1262
1263 {:ok, _, _, follow_activity} = CommonAPI.follow(follower, user)
1264 {:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user)
1265 {:ok, _, _, follow_activity_three} = CommonAPI.follow(follower_two, user)
1266
1267 assert follow_activity.data["state"] == "pending"
1268 assert follow_activity_two.data["state"] == "pending"
1269 assert follow_activity_three.data["state"] == "pending"
1270
1271 {:ok, _follower} = CommonAPI.accept_follow_request(follower, user)
1272
1273 assert Repo.get(Activity, follow_activity.id).data["state"] == "accept"
1274 assert Repo.get(Activity, follow_activity_two.id).data["state"] == "accept"
1275 assert Repo.get(Activity, follow_activity_three.id).data["state"] == "pending"
1276 end
1277
1278 test "after rejection, it sets all existing pending follow request states to 'reject'" do
1279 user = insert(:user, is_locked: true)
1280 follower = insert(:user)
1281 follower_two = insert(:user)
1282
1283 {:ok, _, _, follow_activity} = CommonAPI.follow(follower, user)
1284 {:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user)
1285 {:ok, _, _, follow_activity_three} = CommonAPI.follow(follower_two, user)
1286
1287 assert follow_activity.data["state"] == "pending"
1288 assert follow_activity_two.data["state"] == "pending"
1289 assert follow_activity_three.data["state"] == "pending"
1290
1291 {:ok, _follower} = CommonAPI.reject_follow_request(follower, user)
1292
1293 assert Repo.get(Activity, follow_activity.id).data["state"] == "reject"
1294 assert Repo.get(Activity, follow_activity_two.id).data["state"] == "reject"
1295 assert Repo.get(Activity, follow_activity_three.id).data["state"] == "pending"
1296 end
1297
1298 test "doesn't create a following relationship if the corresponding follow request doesn't exist" do
1299 user = insert(:user, is_locked: true)
1300 not_follower = insert(:user)
1301 CommonAPI.accept_follow_request(not_follower, user)
1302
1303 assert Pleroma.FollowingRelationship.following?(not_follower, user) == false
1304 end
1305 end
1306
1307 describe "vote/3" do
1308 test "does not allow to vote twice" do
1309 user = insert(:user)
1310 other_user = insert(:user)
1311
1312 {:ok, activity} =
1313 CommonAPI.post(user, %{
1314 status: "Am I cute?",
1315 poll: %{options: ["Yes", "No"], expires_in: 20}
1316 })
1317
1318 object = Object.normalize(activity, fetch: false)
1319
1320 {:ok, _, object} = CommonAPI.vote(other_user, object, [0])
1321
1322 assert {:error, "Already voted"} == CommonAPI.vote(other_user, object, [1])
1323 end
1324 end
1325
1326 describe "listen/2" do
1327 test "returns a valid activity" do
1328 user = insert(:user)
1329
1330 {:ok, activity} =
1331 CommonAPI.listen(user, %{
1332 title: "lain radio episode 1",
1333 album: "lain radio",
1334 artist: "lain",
1335 length: 180_000
1336 })
1337
1338 object = Object.normalize(activity, fetch: false)
1339
1340 assert object.data["title"] == "lain radio episode 1"
1341
1342 assert Visibility.get_visibility(activity) == "public"
1343 end
1344
1345 test "respects visibility=private" do
1346 user = insert(:user)
1347
1348 {:ok, activity} =
1349 CommonAPI.listen(user, %{
1350 title: "lain radio episode 1",
1351 album: "lain radio",
1352 artist: "lain",
1353 length: 180_000,
1354 visibility: "private"
1355 })
1356
1357 object = Object.normalize(activity, fetch: false)
1358
1359 assert object.data["title"] == "lain radio episode 1"
1360
1361 assert Visibility.get_visibility(activity) == "private"
1362 end
1363 end
1364
1365 describe "get_user/1" do
1366 test "gets user by ap_id" do
1367 user = insert(:user)
1368 assert CommonAPI.get_user(user.ap_id) == user
1369 end
1370
1371 test "gets user by guessed nickname" do
1372 user = insert(:user, ap_id: "", nickname: "mario@mushroom.kingdom")
1373 assert CommonAPI.get_user("https://mushroom.kingdom/users/mario") == user
1374 end
1375
1376 test "fallback" do
1377 assert %User{
1378 name: "",
1379 ap_id: "",
1380 nickname: "erroruser@example.com"
1381 } = CommonAPI.get_user("")
1382 end
1383 end
1384
1385 describe "with `local` visibility" do
1386 setup do: clear_config([:instance, :federating], true)
1387
1388 test "post" do
1389 user = insert(:user)
1390
1391 with_mock Pleroma.Web.Federator, publish: fn _ -> :ok end do
1392 {:ok, activity} = CommonAPI.post(user, %{status: "#2hu #2HU", visibility: "local"})
1393
1394 assert Visibility.is_local_public?(activity)
1395 assert_not_called(Pleroma.Web.Federator.publish(activity))
1396 end
1397 end
1398
1399 test "delete" do
1400 user = insert(:user)
1401
1402 {:ok, %Activity{id: activity_id}} =
1403 CommonAPI.post(user, %{status: "#2hu #2HU", visibility: "local"})
1404
1405 with_mock Pleroma.Web.Federator, publish: fn _ -> :ok end do
1406 assert {:ok, %Activity{data: %{"deleted_activity_id" => ^activity_id}} = activity} =
1407 CommonAPI.delete(activity_id, user)
1408
1409 assert Visibility.is_local_public?(activity)
1410 assert_not_called(Pleroma.Web.Federator.publish(activity))
1411 end
1412 end
1413
1414 test "repeat" do
1415 user = insert(:user)
1416 other_user = insert(:user)
1417
1418 {:ok, %Activity{id: activity_id}} =
1419 CommonAPI.post(other_user, %{status: "cofe", visibility: "local"})
1420
1421 with_mock Pleroma.Web.Federator, publish: fn _ -> :ok end do
1422 assert {:ok, %Activity{data: %{"type" => "Announce"}} = activity} =
1423 CommonAPI.repeat(activity_id, user)
1424
1425 assert Visibility.is_local_public?(activity)
1426 refute called(Pleroma.Web.Federator.publish(activity))
1427 end
1428 end
1429
1430 test "unrepeat" do
1431 user = insert(:user)
1432 other_user = insert(:user)
1433
1434 {:ok, %Activity{id: activity_id}} =
1435 CommonAPI.post(other_user, %{status: "cofe", visibility: "local"})
1436
1437 assert {:ok, _} = CommonAPI.repeat(activity_id, user)
1438
1439 with_mock Pleroma.Web.Federator, publish: fn _ -> :ok end do
1440 assert {:ok, %Activity{data: %{"type" => "Undo"}} = activity} =
1441 CommonAPI.unrepeat(activity_id, user)
1442
1443 assert Visibility.is_local_public?(activity)
1444 refute called(Pleroma.Web.Federator.publish(activity))
1445 end
1446 end
1447
1448 test "favorite" do
1449 user = insert(:user)
1450 other_user = insert(:user)
1451
1452 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe", visibility: "local"})
1453
1454 with_mock Pleroma.Web.Federator, publish: fn _ -> :ok end do
1455 assert {:ok, %Activity{data: %{"type" => "Like"}} = activity} =
1456 CommonAPI.favorite(user, activity.id)
1457
1458 assert Visibility.is_local_public?(activity)
1459 refute called(Pleroma.Web.Federator.publish(activity))
1460 end
1461 end
1462
1463 test "unfavorite" do
1464 user = insert(:user)
1465 other_user = insert(:user)
1466
1467 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe", visibility: "local"})
1468
1469 {:ok, %Activity{}} = CommonAPI.favorite(user, activity.id)
1470
1471 with_mock Pleroma.Web.Federator, publish: fn _ -> :ok end do
1472 assert {:ok, activity} = CommonAPI.unfavorite(activity.id, user)
1473 assert Visibility.is_local_public?(activity)
1474 refute called(Pleroma.Web.Federator.publish(activity))
1475 end
1476 end
1477
1478 test "react_with_emoji" do
1479 user = insert(:user)
1480 other_user = insert(:user)
1481 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe", visibility: "local"})
1482
1483 with_mock Pleroma.Web.Federator, publish: fn _ -> :ok end do
1484 assert {:ok, %Activity{data: %{"type" => "EmojiReact"}} = activity} =
1485 CommonAPI.react_with_emoji(activity.id, user, "👍")
1486
1487 assert Visibility.is_local_public?(activity)
1488 refute called(Pleroma.Web.Federator.publish(activity))
1489 end
1490 end
1491
1492 test "unreact_with_emoji" do
1493 user = insert(:user)
1494 other_user = insert(:user)
1495 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe", visibility: "local"})
1496
1497 {:ok, _reaction} = CommonAPI.react_with_emoji(activity.id, user, "👍")
1498
1499 with_mock Pleroma.Web.Federator, publish: fn _ -> :ok end do
1500 assert {:ok, %Activity{data: %{"type" => "Undo"}} = activity} =
1501 CommonAPI.unreact_with_emoji(activity.id, user, "👍")
1502
1503 assert Visibility.is_local_public?(activity)
1504 refute called(Pleroma.Web.Federator.publish(activity))
1505 end
1506 end
1507 end
1508 end