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