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