Merge branch 'develop' of git.pleroma.social:pleroma/pleroma into remove-twitter-api
[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 assert object.data["source"] == post
495 end
496
497 test "it filters out obviously bad tags when accepting a post as Markdown" do
498 user = insert(:user)
499
500 post = "<p><b>2hu</b></p><script>alert('xss')</script>"
501
502 {:ok, activity} =
503 CommonAPI.post(user, %{
504 status: post,
505 content_type: "text/markdown"
506 })
507
508 object = Object.normalize(activity)
509
510 assert object.data["content"] == "<p><b>2hu</b></p>alert(&#39;xss&#39;)"
511 assert object.data["source"] == post
512 end
513
514 test "it does not allow replies to direct messages that are not direct messages themselves" do
515 user = insert(:user)
516
517 {:ok, activity} = CommonAPI.post(user, %{status: "suya..", visibility: "direct"})
518
519 assert {:ok, _} =
520 CommonAPI.post(user, %{
521 status: "suya..",
522 visibility: "direct",
523 in_reply_to_status_id: activity.id
524 })
525
526 Enum.each(["public", "private", "unlisted"], fn visibility ->
527 assert {:error, "The message visibility must be direct"} =
528 CommonAPI.post(user, %{
529 status: "suya..",
530 visibility: visibility,
531 in_reply_to_status_id: activity.id
532 })
533 end)
534 end
535
536 test "replying with a direct message will NOT auto-add the author of the reply to the recipient list" do
537 user = insert(:user)
538 other_user = insert(:user)
539 third_user = insert(:user)
540
541 {:ok, post} = CommonAPI.post(user, %{status: "I'm stupid"})
542
543 {:ok, open_answer} =
544 CommonAPI.post(other_user, %{status: "No ur smart", in_reply_to_status_id: post.id})
545
546 # The OP is implicitly added
547 assert user.ap_id in open_answer.recipients
548
549 {:ok, secret_answer} =
550 CommonAPI.post(other_user, %{
551 status: "lol, that guy really is stupid, right, @#{third_user.nickname}?",
552 in_reply_to_status_id: post.id,
553 visibility: "direct"
554 })
555
556 assert third_user.ap_id in secret_answer.recipients
557
558 # The OP is not added
559 refute user.ap_id in secret_answer.recipients
560 end
561
562 test "it allows to address a list" do
563 user = insert(:user)
564 {:ok, list} = Pleroma.List.create("foo", user)
565
566 {:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})
567
568 assert activity.data["bcc"] == [list.ap_id]
569 assert activity.recipients == [list.ap_id, user.ap_id]
570 assert activity.data["listMessage"] == list.ap_id
571 end
572
573 test "it returns error when status is empty and no attachments" do
574 user = insert(:user)
575
576 assert {:error, "Cannot post an empty status without attachments"} =
577 CommonAPI.post(user, %{status: ""})
578 end
579
580 test "it validates character limits are correctly enforced" do
581 Pleroma.Config.put([:instance, :limit], 5)
582
583 user = insert(:user)
584
585 assert {:error, "The status is over the character limit"} =
586 CommonAPI.post(user, %{status: "foobar"})
587
588 assert {:ok, activity} = CommonAPI.post(user, %{status: "12345"})
589 end
590
591 test "it can handle activities that expire" do
592 user = insert(:user)
593
594 expires_at =
595 NaiveDateTime.utc_now()
596 |> NaiveDateTime.truncate(:second)
597 |> NaiveDateTime.add(1_000_000, :second)
598
599 assert {:ok, activity} = CommonAPI.post(user, %{status: "chai", expires_in: 1_000_000})
600
601 assert expiration = Pleroma.ActivityExpiration.get_by_activity_id(activity.id)
602 assert expiration.scheduled_at == expires_at
603 end
604 end
605
606 describe "reactions" do
607 test "reacting to a status with an emoji" do
608 user = insert(:user)
609 other_user = insert(:user)
610
611 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
612
613 {:ok, reaction} = CommonAPI.react_with_emoji(activity.id, user, "👍")
614
615 assert reaction.data["actor"] == user.ap_id
616 assert reaction.data["content"] == "👍"
617
618 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
619
620 {:error, _} = CommonAPI.react_with_emoji(activity.id, user, ".")
621 end
622
623 test "unreacting to a status with an emoji" do
624 user = insert(:user)
625 other_user = insert(:user)
626
627 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
628 {:ok, reaction} = CommonAPI.react_with_emoji(activity.id, user, "👍")
629
630 {:ok, unreaction} = CommonAPI.unreact_with_emoji(activity.id, user, "👍")
631
632 assert unreaction.data["type"] == "Undo"
633 assert unreaction.data["object"] == reaction.data["id"]
634 assert unreaction.local
635 end
636
637 test "repeating a status" do
638 user = insert(:user)
639 other_user = insert(:user)
640
641 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
642
643 {:ok, %Activity{} = announce_activity} = CommonAPI.repeat(activity.id, user)
644 assert Visibility.is_public?(announce_activity)
645 end
646
647 test "can't repeat a repeat" do
648 user = insert(:user)
649 other_user = insert(:user)
650 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
651
652 {:ok, %Activity{} = announce} = CommonAPI.repeat(activity.id, other_user)
653
654 refute match?({:ok, %Activity{}}, CommonAPI.repeat(announce.id, user))
655 end
656
657 test "repeating a status privately" do
658 user = insert(:user)
659 other_user = insert(:user)
660
661 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
662
663 {:ok, %Activity{} = announce_activity} =
664 CommonAPI.repeat(activity.id, user, %{visibility: "private"})
665
666 assert Visibility.is_private?(announce_activity)
667 refute Visibility.visible_for_user?(announce_activity, nil)
668 end
669
670 test "favoriting a status" do
671 user = insert(:user)
672 other_user = insert(:user)
673
674 {:ok, post_activity} = CommonAPI.post(other_user, %{status: "cofe"})
675
676 {:ok, %Activity{data: data}} = CommonAPI.favorite(user, post_activity.id)
677 assert data["type"] == "Like"
678 assert data["actor"] == user.ap_id
679 assert data["object"] == post_activity.data["object"]
680 end
681
682 test "retweeting a status twice returns the status" do
683 user = insert(:user)
684 other_user = insert(:user)
685
686 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
687 {:ok, %Activity{} = announce} = CommonAPI.repeat(activity.id, user)
688 {:ok, ^announce} = CommonAPI.repeat(activity.id, user)
689 end
690
691 test "favoriting a status twice returns ok, but without the like activity" do
692 user = insert(:user)
693 other_user = insert(:user)
694
695 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
696 {:ok, %Activity{}} = CommonAPI.favorite(user, activity.id)
697 assert {:ok, :already_liked} = CommonAPI.favorite(user, activity.id)
698 end
699 end
700
701 describe "pinned statuses" do
702 setup do
703 Pleroma.Config.put([:instance, :max_pinned_statuses], 1)
704
705 user = insert(:user)
706 {:ok, activity} = CommonAPI.post(user, %{status: "HI!!!"})
707
708 [user: user, activity: activity]
709 end
710
711 test "pin status", %{user: user, activity: activity} do
712 assert {:ok, ^activity} = CommonAPI.pin(activity.id, user)
713
714 id = activity.id
715 user = refresh_record(user)
716
717 assert %User{pinned_activities: [^id]} = user
718 end
719
720 test "pin poll", %{user: user} do
721 {:ok, activity} =
722 CommonAPI.post(user, %{
723 status: "How is fediverse today?",
724 poll: %{options: ["Absolutely outstanding", "Not good"], expires_in: 20}
725 })
726
727 assert {:ok, ^activity} = CommonAPI.pin(activity.id, user)
728
729 id = activity.id
730 user = refresh_record(user)
731
732 assert %User{pinned_activities: [^id]} = user
733 end
734
735 test "unlisted statuses can be pinned", %{user: user} do
736 {:ok, activity} = CommonAPI.post(user, %{status: "HI!!!", visibility: "unlisted"})
737 assert {:ok, ^activity} = CommonAPI.pin(activity.id, user)
738 end
739
740 test "only self-authored can be pinned", %{activity: activity} do
741 user = insert(:user)
742
743 assert {:error, "Could not pin"} = CommonAPI.pin(activity.id, user)
744 end
745
746 test "max pinned statuses", %{user: user, activity: activity_one} do
747 {:ok, activity_two} = CommonAPI.post(user, %{status: "HI!!!"})
748
749 assert {:ok, ^activity_one} = CommonAPI.pin(activity_one.id, user)
750
751 user = refresh_record(user)
752
753 assert {:error, "You have already pinned the maximum number of statuses"} =
754 CommonAPI.pin(activity_two.id, user)
755 end
756
757 test "unpin status", %{user: user, activity: activity} do
758 {:ok, activity} = CommonAPI.pin(activity.id, user)
759
760 user = refresh_record(user)
761
762 id = activity.id
763
764 assert match?({:ok, %{id: ^id}}, CommonAPI.unpin(activity.id, user))
765
766 user = refresh_record(user)
767
768 assert %User{pinned_activities: []} = user
769 end
770
771 test "should unpin when deleting a status", %{user: user, activity: activity} do
772 {:ok, activity} = CommonAPI.pin(activity.id, user)
773
774 user = refresh_record(user)
775
776 assert {:ok, _} = CommonAPI.delete(activity.id, user)
777
778 user = refresh_record(user)
779
780 assert %User{pinned_activities: []} = user
781 end
782 end
783
784 describe "mute tests" do
785 setup do
786 user = insert(:user)
787
788 activity = insert(:note_activity)
789
790 [user: user, activity: activity]
791 end
792
793 test "add mute", %{user: user, activity: activity} do
794 {:ok, _} = CommonAPI.add_mute(user, activity)
795 assert CommonAPI.thread_muted?(user, activity)
796 end
797
798 test "remove mute", %{user: user, activity: activity} do
799 CommonAPI.add_mute(user, activity)
800 {:ok, _} = CommonAPI.remove_mute(user, activity)
801 refute CommonAPI.thread_muted?(user, activity)
802 end
803
804 test "check that mutes can't be duplicate", %{user: user, activity: activity} do
805 CommonAPI.add_mute(user, activity)
806 {:error, _} = CommonAPI.add_mute(user, activity)
807 end
808 end
809
810 describe "reports" do
811 test "creates a report" do
812 reporter = insert(:user)
813 target_user = insert(:user)
814
815 {:ok, activity} = CommonAPI.post(target_user, %{status: "foobar"})
816
817 reporter_ap_id = reporter.ap_id
818 target_ap_id = target_user.ap_id
819 activity_ap_id = activity.data["id"]
820 comment = "foobar"
821
822 report_data = %{
823 account_id: target_user.id,
824 comment: comment,
825 status_ids: [activity.id]
826 }
827
828 note_obj = %{
829 "type" => "Note",
830 "id" => activity_ap_id,
831 "content" => "foobar",
832 "published" => activity.object.data["published"],
833 "actor" => AccountView.render("show.json", %{user: target_user})
834 }
835
836 assert {:ok, flag_activity} = CommonAPI.report(reporter, report_data)
837
838 assert %Activity{
839 actor: ^reporter_ap_id,
840 data: %{
841 "type" => "Flag",
842 "content" => ^comment,
843 "object" => [^target_ap_id, ^note_obj],
844 "state" => "open"
845 }
846 } = flag_activity
847 end
848
849 test "updates report state" do
850 [reporter, target_user] = insert_pair(:user)
851 activity = insert(:note_activity, user: target_user)
852
853 {:ok, %Activity{id: report_id}} =
854 CommonAPI.report(reporter, %{
855 account_id: target_user.id,
856 comment: "I feel offended",
857 status_ids: [activity.id]
858 })
859
860 {:ok, report} = CommonAPI.update_report_state(report_id, "resolved")
861
862 assert report.data["state"] == "resolved"
863
864 [reported_user, activity_id] = report.data["object"]
865
866 assert reported_user == target_user.ap_id
867 assert activity_id == activity.data["id"]
868 end
869
870 test "does not update report state when state is unsupported" do
871 [reporter, target_user] = insert_pair(:user)
872 activity = insert(:note_activity, user: target_user)
873
874 {:ok, %Activity{id: report_id}} =
875 CommonAPI.report(reporter, %{
876 account_id: target_user.id,
877 comment: "I feel offended",
878 status_ids: [activity.id]
879 })
880
881 assert CommonAPI.update_report_state(report_id, "test") == {:error, "Unsupported state"}
882 end
883
884 test "updates state of multiple reports" do
885 [reporter, target_user] = insert_pair(:user)
886 activity = insert(:note_activity, user: target_user)
887
888 {:ok, %Activity{id: first_report_id}} =
889 CommonAPI.report(reporter, %{
890 account_id: target_user.id,
891 comment: "I feel offended",
892 status_ids: [activity.id]
893 })
894
895 {:ok, %Activity{id: second_report_id}} =
896 CommonAPI.report(reporter, %{
897 account_id: target_user.id,
898 comment: "I feel very offended!",
899 status_ids: [activity.id]
900 })
901
902 {:ok, report_ids} =
903 CommonAPI.update_report_state([first_report_id, second_report_id], "resolved")
904
905 first_report = Activity.get_by_id(first_report_id)
906 second_report = Activity.get_by_id(second_report_id)
907
908 assert report_ids -- [first_report_id, second_report_id] == []
909 assert first_report.data["state"] == "resolved"
910 assert second_report.data["state"] == "resolved"
911 end
912 end
913
914 describe "reblog muting" do
915 setup do
916 muter = insert(:user)
917
918 muted = insert(:user)
919
920 [muter: muter, muted: muted]
921 end
922
923 test "add a reblog mute", %{muter: muter, muted: muted} do
924 {:ok, _reblog_mute} = CommonAPI.hide_reblogs(muter, muted)
925
926 assert User.showing_reblogs?(muter, muted) == false
927 end
928
929 test "remove a reblog mute", %{muter: muter, muted: muted} do
930 {:ok, _reblog_mute} = CommonAPI.hide_reblogs(muter, muted)
931 {:ok, _reblog_mute} = CommonAPI.show_reblogs(muter, muted)
932
933 assert User.showing_reblogs?(muter, muted) == true
934 end
935 end
936
937 describe "unfollow/2" do
938 test "also unsubscribes a user" do
939 [follower, followed] = insert_pair(:user)
940 {:ok, follower, followed, _} = CommonAPI.follow(follower, followed)
941 {:ok, _subscription} = User.subscribe(follower, followed)
942
943 assert User.subscribed_to?(follower, followed)
944
945 {:ok, follower} = CommonAPI.unfollow(follower, followed)
946
947 refute User.subscribed_to?(follower, followed)
948 end
949
950 test "cancels a pending follow for a local user" do
951 follower = insert(:user)
952 followed = insert(:user, locked: true)
953
954 assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} =
955 CommonAPI.follow(follower, followed)
956
957 assert User.get_follow_state(follower, followed) == :follow_pending
958 assert {:ok, follower} = CommonAPI.unfollow(follower, followed)
959 assert User.get_follow_state(follower, followed) == nil
960
961 assert %{id: ^activity_id, data: %{"state" => "cancelled"}} =
962 Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(follower, followed)
963
964 assert %{
965 data: %{
966 "type" => "Undo",
967 "object" => %{"type" => "Follow", "state" => "cancelled"}
968 }
969 } = Pleroma.Web.ActivityPub.Utils.fetch_latest_undo(follower)
970 end
971
972 test "cancels a pending follow for a remote user" do
973 follower = insert(:user)
974 followed = insert(:user, locked: true, local: false, ap_enabled: true)
975
976 assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} =
977 CommonAPI.follow(follower, followed)
978
979 assert User.get_follow_state(follower, followed) == :follow_pending
980 assert {:ok, follower} = CommonAPI.unfollow(follower, followed)
981 assert User.get_follow_state(follower, followed) == nil
982
983 assert %{id: ^activity_id, data: %{"state" => "cancelled"}} =
984 Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(follower, followed)
985
986 assert %{
987 data: %{
988 "type" => "Undo",
989 "object" => %{"type" => "Follow", "state" => "cancelled"}
990 }
991 } = Pleroma.Web.ActivityPub.Utils.fetch_latest_undo(follower)
992 end
993 end
994
995 describe "accept_follow_request/2" do
996 test "after acceptance, it sets all existing pending follow request states to 'accept'" do
997 user = insert(:user, locked: true)
998 follower = insert(:user)
999 follower_two = insert(:user)
1000
1001 {:ok, follow_activity} = ActivityPub.follow(follower, user)
1002 {:ok, follow_activity_two} = ActivityPub.follow(follower, user)
1003 {:ok, follow_activity_three} = ActivityPub.follow(follower_two, user)
1004
1005 assert follow_activity.data["state"] == "pending"
1006 assert follow_activity_two.data["state"] == "pending"
1007 assert follow_activity_three.data["state"] == "pending"
1008
1009 {:ok, _follower} = CommonAPI.accept_follow_request(follower, user)
1010
1011 assert Repo.get(Activity, follow_activity.id).data["state"] == "accept"
1012 assert Repo.get(Activity, follow_activity_two.id).data["state"] == "accept"
1013 assert Repo.get(Activity, follow_activity_three.id).data["state"] == "pending"
1014 end
1015
1016 test "after rejection, it sets all existing pending follow request states to 'reject'" do
1017 user = insert(:user, locked: true)
1018 follower = insert(:user)
1019 follower_two = insert(:user)
1020
1021 {:ok, follow_activity} = ActivityPub.follow(follower, user)
1022 {:ok, follow_activity_two} = ActivityPub.follow(follower, user)
1023 {:ok, follow_activity_three} = ActivityPub.follow(follower_two, user)
1024
1025 assert follow_activity.data["state"] == "pending"
1026 assert follow_activity_two.data["state"] == "pending"
1027 assert follow_activity_three.data["state"] == "pending"
1028
1029 {:ok, _follower} = CommonAPI.reject_follow_request(follower, user)
1030
1031 assert Repo.get(Activity, follow_activity.id).data["state"] == "reject"
1032 assert Repo.get(Activity, follow_activity_two.id).data["state"] == "reject"
1033 assert Repo.get(Activity, follow_activity_three.id).data["state"] == "pending"
1034 end
1035
1036 test "doesn't create a following relationship if the corresponding follow request doesn't exist" do
1037 user = insert(:user, locked: true)
1038 not_follower = insert(:user)
1039 CommonAPI.accept_follow_request(not_follower, user)
1040
1041 assert Pleroma.FollowingRelationship.following?(not_follower, user) == false
1042 end
1043 end
1044
1045 describe "vote/3" do
1046 test "does not allow to vote twice" do
1047 user = insert(:user)
1048 other_user = insert(:user)
1049
1050 {:ok, activity} =
1051 CommonAPI.post(user, %{
1052 status: "Am I cute?",
1053 poll: %{options: ["Yes", "No"], expires_in: 20}
1054 })
1055
1056 object = Object.normalize(activity)
1057
1058 {:ok, _, object} = CommonAPI.vote(other_user, object, [0])
1059
1060 assert {:error, "Already voted"} == CommonAPI.vote(other_user, object, [1])
1061 end
1062 end
1063
1064 describe "listen/2" do
1065 test "returns a valid activity" do
1066 user = insert(:user)
1067
1068 {:ok, activity} =
1069 CommonAPI.listen(user, %{
1070 title: "lain radio episode 1",
1071 album: "lain radio",
1072 artist: "lain",
1073 length: 180_000
1074 })
1075
1076 object = Object.normalize(activity)
1077
1078 assert object.data["title"] == "lain radio episode 1"
1079
1080 assert Visibility.get_visibility(activity) == "public"
1081 end
1082
1083 test "respects visibility=private" do
1084 user = insert(:user)
1085
1086 {:ok, activity} =
1087 CommonAPI.listen(user, %{
1088 title: "lain radio episode 1",
1089 album: "lain radio",
1090 artist: "lain",
1091 length: 180_000,
1092 visibility: "private"
1093 })
1094
1095 object = Object.normalize(activity)
1096
1097 assert object.data["title"] == "lain radio episode 1"
1098
1099 assert Visibility.get_visibility(activity) == "private"
1100 end
1101 end
1102 end