Merge branch 'develop' into feature/1885-MRF-metadata
[akkoma] / test / web / common_api / common_api_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.CommonAPITest do
6 use Pleroma.DataCase
7 alias Pleroma.Activity
8 alias Pleroma.Chat
9 alias Pleroma.Conversation.Participation
10 alias Pleroma.Notification
11 alias Pleroma.Object
12 alias Pleroma.User
13 alias Pleroma.Web.ActivityPub.ActivityPub
14 alias Pleroma.Web.ActivityPub.Transmogrifier
15 alias Pleroma.Web.ActivityPub.Visibility
16 alias Pleroma.Web.AdminAPI.AccountView
17 alias Pleroma.Web.CommonAPI
18
19 import Pleroma.Factory
20 import Mock
21
22 require Pleroma.Constants
23
24 setup do: clear_config([:instance, :safe_dm_mentions])
25 setup do: clear_config([:instance, :limit])
26 setup do: clear_config([:instance, :max_pinned_statuses])
27
28 describe "blocking" do
29 setup do
30 blocker = insert(:user)
31 blocked = insert(:user)
32 User.follow(blocker, blocked)
33 User.follow(blocked, blocker)
34 %{blocker: blocker, blocked: blocked}
35 end
36
37 test "it blocks and federates", %{blocker: blocker, blocked: blocked} do
38 clear_config([:instance, :federating], true)
39
40 with_mock Pleroma.Web.Federator,
41 publish: fn _ -> nil end do
42 assert {:ok, block} = CommonAPI.block(blocker, blocked)
43
44 assert block.local
45 assert User.blocks?(blocker, blocked)
46 refute User.following?(blocker, blocked)
47 refute User.following?(blocked, blocker)
48
49 assert called(Pleroma.Web.Federator.publish(block))
50 end
51 end
52
53 test "it blocks and does not federate if outgoing blocks are disabled", %{
54 blocker: blocker,
55 blocked: blocked
56 } do
57 clear_config([:instance, :federating], true)
58 clear_config([:activitypub, :outgoing_blocks], false)
59
60 with_mock Pleroma.Web.Federator,
61 publish: fn _ -> nil end do
62 assert {:ok, block} = CommonAPI.block(blocker, blocked)
63
64 assert block.local
65 assert User.blocks?(blocker, blocked)
66 refute User.following?(blocker, blocked)
67 refute User.following?(blocked, blocker)
68
69 refute called(Pleroma.Web.Federator.publish(block))
70 end
71 end
72 end
73
74 describe "posting chat messages" do
75 setup do: clear_config([:instance, :chat_limit])
76
77 test "it posts a chat message without content but with an attachment" do
78 author = insert(:user)
79 recipient = insert(:user)
80
81 file = %Plug.Upload{
82 content_type: "image/jpg",
83 path: Path.absname("test/fixtures/image.jpg"),
84 filename: "an_image.jpg"
85 }
86
87 {:ok, upload} = ActivityPub.upload(file, actor: author.ap_id)
88
89 with_mocks([
90 {
91 Pleroma.Web.Streamer,
92 [],
93 [
94 stream: fn _, _ ->
95 nil
96 end
97 ]
98 },
99 {
100 Pleroma.Web.Push,
101 [],
102 [
103 send: fn _ -> nil end
104 ]
105 }
106 ]) do
107 {:ok, activity} =
108 CommonAPI.post_chat_message(
109 author,
110 recipient,
111 nil,
112 media_id: upload.id
113 )
114
115 notification =
116 Notification.for_user_and_activity(recipient, activity)
117 |> Repo.preload(:activity)
118
119 assert called(Pleroma.Web.Push.send(notification))
120 assert called(Pleroma.Web.Streamer.stream(["user", "user:notification"], notification))
121 assert called(Pleroma.Web.Streamer.stream(["user", "user:pleroma_chat"], :_))
122
123 assert activity
124 end
125 end
126
127 test "it adds html newlines" do
128 author = insert(:user)
129 recipient = insert(:user)
130
131 other_user = insert(:user)
132
133 {:ok, activity} =
134 CommonAPI.post_chat_message(
135 author,
136 recipient,
137 "uguu\nuguuu"
138 )
139
140 assert other_user.ap_id not in activity.recipients
141
142 object = Object.normalize(activity, false)
143
144 assert object.data["content"] == "uguu<br/>uguuu"
145 end
146
147 test "it linkifies" do
148 author = insert(:user)
149 recipient = insert(:user)
150
151 other_user = insert(:user)
152
153 {:ok, activity} =
154 CommonAPI.post_chat_message(
155 author,
156 recipient,
157 "https://example.org is the site of @#{other_user.nickname} #2hu"
158 )
159
160 assert other_user.ap_id not in activity.recipients
161
162 object = Object.normalize(activity, false)
163
164 assert object.data["content"] ==
165 "<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=\"#{
166 other_user.id
167 }\" 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>"
168 end
169
170 test "it posts a chat message" do
171 author = insert(:user)
172 recipient = insert(:user)
173
174 {:ok, activity} =
175 CommonAPI.post_chat_message(
176 author,
177 recipient,
178 "a test message <script>alert('uuu')</script> :firefox:"
179 )
180
181 assert activity.data["type"] == "Create"
182 assert activity.local
183 object = Object.normalize(activity)
184
185 assert object.data["type"] == "ChatMessage"
186 assert object.data["to"] == [recipient.ap_id]
187
188 assert object.data["content"] ==
189 "a test message &lt;script&gt;alert(&#39;uuu&#39;)&lt;/script&gt; :firefox:"
190
191 assert object.data["emoji"] == %{
192 "firefox" => "http://localhost:4001/emoji/Firefox.gif"
193 }
194
195 assert Chat.get(author.id, recipient.ap_id)
196 assert Chat.get(recipient.id, author.ap_id)
197
198 assert :ok == Pleroma.Web.Federator.perform(:publish, activity)
199 end
200
201 test "it reject messages over the local limit" do
202 Pleroma.Config.put([:instance, :chat_limit], 2)
203
204 author = insert(:user)
205 recipient = insert(:user)
206
207 {:error, message} =
208 CommonAPI.post_chat_message(
209 author,
210 recipient,
211 "123"
212 )
213
214 assert message == :content_too_long
215 end
216 end
217
218 describe "unblocking" do
219 test "it works even without an existing block activity" do
220 blocked = insert(:user)
221 blocker = insert(:user)
222 User.block(blocker, blocked)
223
224 assert User.blocks?(blocker, blocked)
225 assert {:ok, :no_activity} == CommonAPI.unblock(blocker, blocked)
226 refute User.blocks?(blocker, blocked)
227 end
228 end
229
230 describe "deletion" do
231 test "it works with pruned objects" do
232 user = insert(:user)
233
234 {:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
235
236 clear_config([:instance, :federating], true)
237
238 Object.normalize(post, false)
239 |> Object.prune()
240
241 with_mock Pleroma.Web.Federator,
242 publish: fn _ -> nil end do
243 assert {:ok, delete} = CommonAPI.delete(post.id, user)
244 assert delete.local
245 assert called(Pleroma.Web.Federator.publish(delete))
246 end
247
248 refute Activity.get_by_id(post.id)
249 end
250
251 test "it allows users to delete their posts" do
252 user = insert(:user)
253
254 {:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
255
256 clear_config([:instance, :federating], true)
257
258 with_mock Pleroma.Web.Federator,
259 publish: fn _ -> nil end do
260 assert {:ok, delete} = CommonAPI.delete(post.id, user)
261 assert delete.local
262 assert called(Pleroma.Web.Federator.publish(delete))
263 end
264
265 refute Activity.get_by_id(post.id)
266 end
267
268 test "it does not allow a user to delete their posts" do
269 user = insert(:user)
270 other_user = insert(:user)
271
272 {:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
273
274 assert {:error, "Could not delete"} = CommonAPI.delete(post.id, other_user)
275 assert Activity.get_by_id(post.id)
276 end
277
278 test "it allows moderators to delete other user's posts" do
279 user = insert(:user)
280 moderator = insert(:user, is_moderator: true)
281
282 {:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
283
284 assert {:ok, delete} = CommonAPI.delete(post.id, moderator)
285 assert delete.local
286
287 refute Activity.get_by_id(post.id)
288 end
289
290 test "it allows admins to delete other user's posts" do
291 user = insert(:user)
292 moderator = insert(:user, is_admin: true)
293
294 {:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
295
296 assert {:ok, delete} = CommonAPI.delete(post.id, moderator)
297 assert delete.local
298
299 refute Activity.get_by_id(post.id)
300 end
301
302 test "superusers deleting non-local posts won't federate the delete" do
303 # This is the user of the ingested activity
304 _user =
305 insert(:user,
306 local: false,
307 ap_id: "http://mastodon.example.org/users/admin",
308 last_refreshed_at: NaiveDateTime.utc_now()
309 )
310
311 moderator = insert(:user, is_admin: true)
312
313 data =
314 File.read!("test/fixtures/mastodon-post-activity.json")
315 |> Jason.decode!()
316
317 {:ok, post} = Transmogrifier.handle_incoming(data)
318
319 with_mock Pleroma.Web.Federator,
320 publish: fn _ -> nil end do
321 assert {:ok, delete} = CommonAPI.delete(post.id, moderator)
322 assert delete.local
323 refute called(Pleroma.Web.Federator.publish(:_))
324 end
325
326 refute Activity.get_by_id(post.id)
327 end
328 end
329
330 test "favoriting race condition" do
331 user = insert(:user)
332 users_serial = insert_list(10, :user)
333 users = insert_list(10, :user)
334
335 {:ok, activity} = CommonAPI.post(user, %{status: "."})
336
337 users_serial
338 |> Enum.map(fn user ->
339 CommonAPI.favorite(user, activity.id)
340 end)
341
342 object = Object.get_by_ap_id(activity.data["object"])
343 assert object.data["like_count"] == 10
344
345 users
346 |> Enum.map(fn user ->
347 Task.async(fn ->
348 CommonAPI.favorite(user, activity.id)
349 end)
350 end)
351 |> Enum.map(&Task.await/1)
352
353 object = Object.get_by_ap_id(activity.data["object"])
354 assert object.data["like_count"] == 20
355 end
356
357 test "repeating race condition" do
358 user = insert(:user)
359 users_serial = insert_list(10, :user)
360 users = insert_list(10, :user)
361
362 {:ok, activity} = CommonAPI.post(user, %{status: "."})
363
364 users_serial
365 |> Enum.map(fn user ->
366 CommonAPI.repeat(activity.id, user)
367 end)
368
369 object = Object.get_by_ap_id(activity.data["object"])
370 assert object.data["announcement_count"] == 10
371
372 users
373 |> Enum.map(fn user ->
374 Task.async(fn ->
375 CommonAPI.repeat(activity.id, user)
376 end)
377 end)
378 |> Enum.map(&Task.await/1)
379
380 object = Object.get_by_ap_id(activity.data["object"])
381 assert object.data["announcement_count"] == 20
382 end
383
384 test "when replying to a conversation / participation, it will set the correct context id even if no explicit reply_to is given" do
385 user = insert(:user)
386 {:ok, activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"})
387
388 [participation] = Participation.for_user(user)
389
390 {:ok, convo_reply} =
391 CommonAPI.post(user, %{status: ".", in_reply_to_conversation_id: participation.id})
392
393 assert Visibility.is_direct?(convo_reply)
394
395 assert activity.data["context"] == convo_reply.data["context"]
396 end
397
398 test "when replying to a conversation / participation, it only mentions the recipients explicitly declared in the participation" do
399 har = insert(:user)
400 jafnhar = insert(:user)
401 tridi = insert(:user)
402
403 {:ok, activity} =
404 CommonAPI.post(har, %{
405 status: "@#{jafnhar.nickname} hey",
406 visibility: "direct"
407 })
408
409 assert har.ap_id in activity.recipients
410 assert jafnhar.ap_id in activity.recipients
411
412 [participation] = Participation.for_user(har)
413
414 {:ok, activity} =
415 CommonAPI.post(har, %{
416 status: "I don't really like @#{tridi.nickname}",
417 visibility: "direct",
418 in_reply_to_status_id: activity.id,
419 in_reply_to_conversation_id: participation.id
420 })
421
422 assert har.ap_id in activity.recipients
423 assert jafnhar.ap_id in activity.recipients
424 refute tridi.ap_id in activity.recipients
425 end
426
427 test "with the safe_dm_mention option set, it does not mention people beyond the initial tags" do
428 har = insert(:user)
429 jafnhar = insert(:user)
430 tridi = insert(:user)
431
432 Pleroma.Config.put([:instance, :safe_dm_mentions], true)
433
434 {:ok, activity} =
435 CommonAPI.post(har, %{
436 status: "@#{jafnhar.nickname} hey, i never want to see @#{tridi.nickname} again",
437 visibility: "direct"
438 })
439
440 refute tridi.ap_id in activity.recipients
441 assert jafnhar.ap_id in activity.recipients
442 end
443
444 test "it de-duplicates tags" do
445 user = insert(:user)
446 {:ok, activity} = CommonAPI.post(user, %{status: "#2hu #2HU"})
447
448 object = Object.normalize(activity)
449
450 assert object.data["tag"] == ["2hu"]
451 end
452
453 test "it adds emoji in the object" do
454 user = insert(:user)
455 {:ok, activity} = CommonAPI.post(user, %{status: ":firefox:"})
456
457 assert Object.normalize(activity).data["emoji"]["firefox"]
458 end
459
460 describe "posting" do
461 test "it supports explicit addressing" do
462 user = insert(:user)
463 user_two = insert(:user)
464 user_three = insert(:user)
465 user_four = insert(:user)
466
467 {:ok, activity} =
468 CommonAPI.post(user, %{
469 status:
470 "Hey, I think @#{user_three.nickname} is ugly. @#{user_four.nickname} is alright though.",
471 to: [user_two.nickname, user_four.nickname, "nonexistent"]
472 })
473
474 assert user.ap_id in activity.recipients
475 assert user_two.ap_id in activity.recipients
476 assert user_four.ap_id in activity.recipients
477 refute user_three.ap_id in activity.recipients
478 end
479
480 test "it filters out obviously bad tags when accepting a post as HTML" do
481 user = insert(:user)
482
483 post = "<p><b>2hu</b></p><script>alert('xss')</script>"
484
485 {:ok, activity} =
486 CommonAPI.post(user, %{
487 status: post,
488 content_type: "text/html"
489 })
490
491 object = Object.normalize(activity)
492
493 assert object.data["content"] == "<p><b>2hu</b></p>alert(&#39;xss&#39;)"
494 end
495
496 test "it filters out obviously bad tags when accepting a post as Markdown" do
497 user = insert(:user)
498
499 post = "<p><b>2hu</b></p><script>alert('xss')</script>"
500
501 {:ok, activity} =
502 CommonAPI.post(user, %{
503 status: post,
504 content_type: "text/markdown"
505 })
506
507 object = Object.normalize(activity)
508
509 assert object.data["content"] == "<p><b>2hu</b></p>alert(&#39;xss&#39;)"
510 end
511
512 test "it does not allow replies to direct messages that are not direct messages themselves" do
513 user = insert(:user)
514
515 {:ok, activity} = CommonAPI.post(user, %{status: "suya..", visibility: "direct"})
516
517 assert {:ok, _} =
518 CommonAPI.post(user, %{
519 status: "suya..",
520 visibility: "direct",
521 in_reply_to_status_id: activity.id
522 })
523
524 Enum.each(["public", "private", "unlisted"], fn visibility ->
525 assert {:error, "The message visibility must be direct"} =
526 CommonAPI.post(user, %{
527 status: "suya..",
528 visibility: visibility,
529 in_reply_to_status_id: activity.id
530 })
531 end)
532 end
533
534 test "replying with a direct message will NOT auto-add the author of the reply to the recipient list" do
535 user = insert(:user)
536 other_user = insert(:user)
537 third_user = insert(:user)
538
539 {:ok, post} = CommonAPI.post(user, %{status: "I'm stupid"})
540
541 {:ok, open_answer} =
542 CommonAPI.post(other_user, %{status: "No ur smart", in_reply_to_status_id: post.id})
543
544 # The OP is implicitly added
545 assert user.ap_id in open_answer.recipients
546
547 {:ok, secret_answer} =
548 CommonAPI.post(other_user, %{
549 status: "lol, that guy really is stupid, right, @#{third_user.nickname}?",
550 in_reply_to_status_id: post.id,
551 visibility: "direct"
552 })
553
554 assert third_user.ap_id in secret_answer.recipients
555
556 # The OP is not added
557 refute user.ap_id in secret_answer.recipients
558 end
559
560 test "it allows to address a list" do
561 user = insert(:user)
562 {:ok, list} = Pleroma.List.create("foo", user)
563
564 {:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})
565
566 assert activity.data["bcc"] == [list.ap_id]
567 assert activity.recipients == [list.ap_id, user.ap_id]
568 assert activity.data["listMessage"] == list.ap_id
569 end
570
571 test "it returns error when status is empty and no attachments" do
572 user = insert(:user)
573
574 assert {:error, "Cannot post an empty status without attachments"} =
575 CommonAPI.post(user, %{status: ""})
576 end
577
578 test "it validates character limits are correctly enforced" do
579 Pleroma.Config.put([:instance, :limit], 5)
580
581 user = insert(:user)
582
583 assert {:error, "The status is over the character limit"} =
584 CommonAPI.post(user, %{status: "foobar"})
585
586 assert {:ok, activity} = CommonAPI.post(user, %{status: "12345"})
587 end
588
589 test "it can handle activities that expire" do
590 user = insert(:user)
591
592 expires_at =
593 NaiveDateTime.utc_now()
594 |> NaiveDateTime.truncate(:second)
595 |> NaiveDateTime.add(1_000_000, :second)
596
597 assert {:ok, activity} = CommonAPI.post(user, %{status: "chai", expires_in: 1_000_000})
598
599 assert expiration = Pleroma.ActivityExpiration.get_by_activity_id(activity.id)
600 assert expiration.scheduled_at == expires_at
601 end
602 end
603
604 describe "reactions" do
605 test "reacting to a status with an emoji" do
606 user = insert(:user)
607 other_user = insert(:user)
608
609 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
610
611 {:ok, reaction} = CommonAPI.react_with_emoji(activity.id, user, "👍")
612
613 assert reaction.data["actor"] == user.ap_id
614 assert reaction.data["content"] == "👍"
615
616 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
617
618 {:error, _} = CommonAPI.react_with_emoji(activity.id, user, ".")
619 end
620
621 test "unreacting to a status with an emoji" do
622 user = insert(:user)
623 other_user = insert(:user)
624
625 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
626 {:ok, reaction} = CommonAPI.react_with_emoji(activity.id, user, "👍")
627
628 {:ok, unreaction} = CommonAPI.unreact_with_emoji(activity.id, user, "👍")
629
630 assert unreaction.data["type"] == "Undo"
631 assert unreaction.data["object"] == reaction.data["id"]
632 assert unreaction.local
633 end
634
635 test "repeating a status" do
636 user = insert(:user)
637 other_user = insert(:user)
638
639 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
640
641 {:ok, %Activity{} = announce_activity} = CommonAPI.repeat(activity.id, user)
642 assert Visibility.is_public?(announce_activity)
643 end
644
645 test "can't repeat a repeat" do
646 user = insert(:user)
647 other_user = insert(:user)
648 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
649
650 {:ok, %Activity{} = announce} = CommonAPI.repeat(activity.id, other_user)
651
652 refute match?({:ok, %Activity{}}, CommonAPI.repeat(announce.id, user))
653 end
654
655 test "repeating a status privately" do
656 user = insert(:user)
657 other_user = insert(:user)
658
659 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
660
661 {:ok, %Activity{} = announce_activity} =
662 CommonAPI.repeat(activity.id, user, %{visibility: "private"})
663
664 assert Visibility.is_private?(announce_activity)
665 refute Visibility.visible_for_user?(announce_activity, nil)
666 end
667
668 test "favoriting a status" do
669 user = insert(:user)
670 other_user = insert(:user)
671
672 {:ok, post_activity} = CommonAPI.post(other_user, %{status: "cofe"})
673
674 {:ok, %Activity{data: data}} = CommonAPI.favorite(user, post_activity.id)
675 assert data["type"] == "Like"
676 assert data["actor"] == user.ap_id
677 assert data["object"] == post_activity.data["object"]
678 end
679
680 test "retweeting a status twice returns the status" do
681 user = insert(:user)
682 other_user = insert(:user)
683
684 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
685 {:ok, %Activity{} = announce} = CommonAPI.repeat(activity.id, user)
686 {:ok, ^announce} = CommonAPI.repeat(activity.id, user)
687 end
688
689 test "favoriting a status twice returns ok, but without the like activity" do
690 user = insert(:user)
691 other_user = insert(:user)
692
693 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
694 {:ok, %Activity{}} = CommonAPI.favorite(user, activity.id)
695 assert {:ok, :already_liked} = CommonAPI.favorite(user, activity.id)
696 end
697 end
698
699 describe "pinned statuses" do
700 setup do
701 Pleroma.Config.put([:instance, :max_pinned_statuses], 1)
702
703 user = insert(:user)
704 {:ok, activity} = CommonAPI.post(user, %{status: "HI!!!"})
705
706 [user: user, activity: activity]
707 end
708
709 test "pin status", %{user: user, activity: activity} do
710 assert {:ok, ^activity} = CommonAPI.pin(activity.id, user)
711
712 id = activity.id
713 user = refresh_record(user)
714
715 assert %User{pinned_activities: [^id]} = user
716 end
717
718 test "pin poll", %{user: user} do
719 {:ok, activity} =
720 CommonAPI.post(user, %{
721 status: "How is fediverse today?",
722 poll: %{options: ["Absolutely outstanding", "Not good"], expires_in: 20}
723 })
724
725 assert {:ok, ^activity} = CommonAPI.pin(activity.id, user)
726
727 id = activity.id
728 user = refresh_record(user)
729
730 assert %User{pinned_activities: [^id]} = user
731 end
732
733 test "unlisted statuses can be pinned", %{user: user} do
734 {:ok, activity} = CommonAPI.post(user, %{status: "HI!!!", visibility: "unlisted"})
735 assert {:ok, ^activity} = CommonAPI.pin(activity.id, user)
736 end
737
738 test "only self-authored can be pinned", %{activity: activity} do
739 user = insert(:user)
740
741 assert {:error, "Could not pin"} = CommonAPI.pin(activity.id, user)
742 end
743
744 test "max pinned statuses", %{user: user, activity: activity_one} do
745 {:ok, activity_two} = CommonAPI.post(user, %{status: "HI!!!"})
746
747 assert {:ok, ^activity_one} = CommonAPI.pin(activity_one.id, user)
748
749 user = refresh_record(user)
750
751 assert {:error, "You have already pinned the maximum number of statuses"} =
752 CommonAPI.pin(activity_two.id, user)
753 end
754
755 test "unpin status", %{user: user, activity: activity} do
756 {:ok, activity} = CommonAPI.pin(activity.id, user)
757
758 user = refresh_record(user)
759
760 id = activity.id
761
762 assert match?({:ok, %{id: ^id}}, CommonAPI.unpin(activity.id, user))
763
764 user = refresh_record(user)
765
766 assert %User{pinned_activities: []} = user
767 end
768
769 test "should unpin when deleting a status", %{user: user, activity: activity} do
770 {:ok, activity} = CommonAPI.pin(activity.id, user)
771
772 user = refresh_record(user)
773
774 assert {:ok, _} = CommonAPI.delete(activity.id, user)
775
776 user = refresh_record(user)
777
778 assert %User{pinned_activities: []} = user
779 end
780 end
781
782 describe "mute tests" do
783 setup do
784 user = insert(:user)
785
786 activity = insert(:note_activity)
787
788 [user: user, activity: activity]
789 end
790
791 test "add mute", %{user: user, activity: activity} do
792 {:ok, _} = CommonAPI.add_mute(user, activity)
793 assert CommonAPI.thread_muted?(user, activity)
794 end
795
796 test "remove mute", %{user: user, activity: activity} do
797 CommonAPI.add_mute(user, activity)
798 {:ok, _} = CommonAPI.remove_mute(user, activity)
799 refute CommonAPI.thread_muted?(user, activity)
800 end
801
802 test "check that mutes can't be duplicate", %{user: user, activity: activity} do
803 CommonAPI.add_mute(user, activity)
804 {:error, _} = CommonAPI.add_mute(user, activity)
805 end
806 end
807
808 describe "reports" do
809 test "creates a report" do
810 reporter = insert(:user)
811 target_user = insert(:user)
812
813 {:ok, activity} = CommonAPI.post(target_user, %{status: "foobar"})
814
815 reporter_ap_id = reporter.ap_id
816 target_ap_id = target_user.ap_id
817 activity_ap_id = activity.data["id"]
818 comment = "foobar"
819
820 report_data = %{
821 account_id: target_user.id,
822 comment: comment,
823 status_ids: [activity.id]
824 }
825
826 note_obj = %{
827 "type" => "Note",
828 "id" => activity_ap_id,
829 "content" => "foobar",
830 "published" => activity.object.data["published"],
831 "actor" => AccountView.render("show.json", %{user: target_user})
832 }
833
834 assert {:ok, flag_activity} = CommonAPI.report(reporter, report_data)
835
836 assert %Activity{
837 actor: ^reporter_ap_id,
838 data: %{
839 "type" => "Flag",
840 "content" => ^comment,
841 "object" => [^target_ap_id, ^note_obj],
842 "state" => "open"
843 }
844 } = flag_activity
845 end
846
847 test "updates report state" do
848 [reporter, target_user] = insert_pair(:user)
849 activity = insert(:note_activity, user: target_user)
850
851 {:ok, %Activity{id: report_id}} =
852 CommonAPI.report(reporter, %{
853 account_id: target_user.id,
854 comment: "I feel offended",
855 status_ids: [activity.id]
856 })
857
858 {:ok, report} = CommonAPI.update_report_state(report_id, "resolved")
859
860 assert report.data["state"] == "resolved"
861
862 [reported_user, activity_id] = report.data["object"]
863
864 assert reported_user == target_user.ap_id
865 assert activity_id == activity.data["id"]
866 end
867
868 test "does not update report state when state is unsupported" do
869 [reporter, target_user] = insert_pair(:user)
870 activity = insert(:note_activity, user: target_user)
871
872 {:ok, %Activity{id: report_id}} =
873 CommonAPI.report(reporter, %{
874 account_id: target_user.id,
875 comment: "I feel offended",
876 status_ids: [activity.id]
877 })
878
879 assert CommonAPI.update_report_state(report_id, "test") == {:error, "Unsupported state"}
880 end
881
882 test "updates state of multiple reports" do
883 [reporter, target_user] = insert_pair(:user)
884 activity = insert(:note_activity, user: target_user)
885
886 {:ok, %Activity{id: first_report_id}} =
887 CommonAPI.report(reporter, %{
888 account_id: target_user.id,
889 comment: "I feel offended",
890 status_ids: [activity.id]
891 })
892
893 {:ok, %Activity{id: second_report_id}} =
894 CommonAPI.report(reporter, %{
895 account_id: target_user.id,
896 comment: "I feel very offended!",
897 status_ids: [activity.id]
898 })
899
900 {:ok, report_ids} =
901 CommonAPI.update_report_state([first_report_id, second_report_id], "resolved")
902
903 first_report = Activity.get_by_id(first_report_id)
904 second_report = Activity.get_by_id(second_report_id)
905
906 assert report_ids -- [first_report_id, second_report_id] == []
907 assert first_report.data["state"] == "resolved"
908 assert second_report.data["state"] == "resolved"
909 end
910 end
911
912 describe "reblog muting" do
913 setup do
914 muter = insert(:user)
915
916 muted = insert(:user)
917
918 [muter: muter, muted: muted]
919 end
920
921 test "add a reblog mute", %{muter: muter, muted: muted} do
922 {:ok, _reblog_mute} = CommonAPI.hide_reblogs(muter, muted)
923
924 assert User.showing_reblogs?(muter, muted) == false
925 end
926
927 test "remove a reblog mute", %{muter: muter, muted: muted} do
928 {:ok, _reblog_mute} = CommonAPI.hide_reblogs(muter, muted)
929 {:ok, _reblog_mute} = CommonAPI.show_reblogs(muter, muted)
930
931 assert User.showing_reblogs?(muter, muted) == true
932 end
933 end
934
935 describe "unfollow/2" do
936 test "also unsubscribes a user" do
937 [follower, followed] = insert_pair(:user)
938 {:ok, follower, followed, _} = CommonAPI.follow(follower, followed)
939 {:ok, _subscription} = User.subscribe(follower, followed)
940
941 assert User.subscribed_to?(follower, followed)
942
943 {:ok, follower} = CommonAPI.unfollow(follower, followed)
944
945 refute User.subscribed_to?(follower, followed)
946 end
947
948 test "cancels a pending follow for a local user" do
949 follower = insert(:user)
950 followed = insert(:user, locked: true)
951
952 assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} =
953 CommonAPI.follow(follower, followed)
954
955 assert User.get_follow_state(follower, followed) == :follow_pending
956 assert {:ok, follower} = CommonAPI.unfollow(follower, followed)
957 assert User.get_follow_state(follower, followed) == nil
958
959 assert %{id: ^activity_id, data: %{"state" => "cancelled"}} =
960 Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(follower, followed)
961
962 assert %{
963 data: %{
964 "type" => "Undo",
965 "object" => %{"type" => "Follow", "state" => "cancelled"}
966 }
967 } = Pleroma.Web.ActivityPub.Utils.fetch_latest_undo(follower)
968 end
969
970 test "cancels a pending follow for a remote user" do
971 follower = insert(:user)
972 followed = insert(:user, locked: true, local: false, ap_enabled: true)
973
974 assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} =
975 CommonAPI.follow(follower, followed)
976
977 assert User.get_follow_state(follower, followed) == :follow_pending
978 assert {:ok, follower} = CommonAPI.unfollow(follower, followed)
979 assert User.get_follow_state(follower, followed) == nil
980
981 assert %{id: ^activity_id, data: %{"state" => "cancelled"}} =
982 Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(follower, followed)
983
984 assert %{
985 data: %{
986 "type" => "Undo",
987 "object" => %{"type" => "Follow", "state" => "cancelled"}
988 }
989 } = Pleroma.Web.ActivityPub.Utils.fetch_latest_undo(follower)
990 end
991 end
992
993 describe "accept_follow_request/2" do
994 test "after acceptance, it sets all existing pending follow request states to 'accept'" do
995 user = insert(:user, locked: true)
996 follower = insert(:user)
997 follower_two = insert(:user)
998
999 {:ok, follow_activity} = ActivityPub.follow(follower, user)
1000 {:ok, follow_activity_two} = ActivityPub.follow(follower, user)
1001 {:ok, follow_activity_three} = ActivityPub.follow(follower_two, user)
1002
1003 assert follow_activity.data["state"] == "pending"
1004 assert follow_activity_two.data["state"] == "pending"
1005 assert follow_activity_three.data["state"] == "pending"
1006
1007 {:ok, _follower} = CommonAPI.accept_follow_request(follower, user)
1008
1009 assert Repo.get(Activity, follow_activity.id).data["state"] == "accept"
1010 assert Repo.get(Activity, follow_activity_two.id).data["state"] == "accept"
1011 assert Repo.get(Activity, follow_activity_three.id).data["state"] == "pending"
1012 end
1013
1014 test "after rejection, it sets all existing pending follow request states to 'reject'" do
1015 user = insert(:user, locked: true)
1016 follower = insert(:user)
1017 follower_two = insert(:user)
1018
1019 {:ok, follow_activity} = ActivityPub.follow(follower, user)
1020 {:ok, follow_activity_two} = ActivityPub.follow(follower, user)
1021 {:ok, follow_activity_three} = ActivityPub.follow(follower_two, user)
1022
1023 assert follow_activity.data["state"] == "pending"
1024 assert follow_activity_two.data["state"] == "pending"
1025 assert follow_activity_three.data["state"] == "pending"
1026
1027 {:ok, _follower} = CommonAPI.reject_follow_request(follower, user)
1028
1029 assert Repo.get(Activity, follow_activity.id).data["state"] == "reject"
1030 assert Repo.get(Activity, follow_activity_two.id).data["state"] == "reject"
1031 assert Repo.get(Activity, follow_activity_three.id).data["state"] == "pending"
1032 end
1033
1034 test "doesn't create a following relationship if the corresponding follow request doesn't exist" do
1035 user = insert(:user, locked: true)
1036 not_follower = insert(:user)
1037 CommonAPI.accept_follow_request(not_follower, user)
1038
1039 assert Pleroma.FollowingRelationship.following?(not_follower, user) == false
1040 end
1041 end
1042
1043 describe "vote/3" do
1044 test "does not allow to vote twice" do
1045 user = insert(:user)
1046 other_user = insert(:user)
1047
1048 {:ok, activity} =
1049 CommonAPI.post(user, %{
1050 status: "Am I cute?",
1051 poll: %{options: ["Yes", "No"], expires_in: 20}
1052 })
1053
1054 object = Object.normalize(activity)
1055
1056 {:ok, _, object} = CommonAPI.vote(other_user, object, [0])
1057
1058 assert {:error, "Already voted"} == CommonAPI.vote(other_user, object, [1])
1059 end
1060 end
1061
1062 describe "listen/2" do
1063 test "returns a valid activity" do
1064 user = insert(:user)
1065
1066 {:ok, activity} =
1067 CommonAPI.listen(user, %{
1068 title: "lain radio episode 1",
1069 album: "lain radio",
1070 artist: "lain",
1071 length: 180_000
1072 })
1073
1074 object = Object.normalize(activity)
1075
1076 assert object.data["title"] == "lain radio episode 1"
1077
1078 assert Visibility.get_visibility(activity) == "public"
1079 end
1080
1081 test "respects visibility=private" do
1082 user = insert(:user)
1083
1084 {:ok, activity} =
1085 CommonAPI.listen(user, %{
1086 title: "lain radio episode 1",
1087 album: "lain radio",
1088 artist: "lain",
1089 length: 180_000,
1090 visibility: "private"
1091 })
1092
1093 object = Object.normalize(activity)
1094
1095 assert object.data["title"] == "lain radio episode 1"
1096
1097 assert Visibility.get_visibility(activity) == "private"
1098 end
1099 end
1100 end