Merge branch 'cleanup/masto_fe-default_settings' into 'develop'
[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 clear_config([:instance, :federating], true)
628
629 with_mock Pleroma.Web.Federator,
630 publish: fn _ -> nil end do
631 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
632 {:ok, reaction} = CommonAPI.react_with_emoji(activity.id, user, "👍")
633
634 {:ok, unreaction} = CommonAPI.unreact_with_emoji(activity.id, user, "👍")
635
636 assert unreaction.data["type"] == "Undo"
637 assert unreaction.data["object"] == reaction.data["id"]
638 assert unreaction.local
639
640 # On federation, it contains the undone (and deleted) object
641 unreaction_with_object = %{
642 unreaction
643 | data: Map.put(unreaction.data, "object", reaction.data)
644 }
645
646 assert called(Pleroma.Web.Federator.publish(unreaction_with_object))
647 end
648 end
649
650 test "repeating a status" do
651 user = insert(:user)
652 other_user = insert(:user)
653
654 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
655
656 {:ok, %Activity{} = announce_activity} = CommonAPI.repeat(activity.id, user)
657 assert Visibility.is_public?(announce_activity)
658 end
659
660 test "can't repeat a repeat" do
661 user = insert(:user)
662 other_user = insert(:user)
663 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
664
665 {:ok, %Activity{} = announce} = CommonAPI.repeat(activity.id, other_user)
666
667 refute match?({:ok, %Activity{}}, CommonAPI.repeat(announce.id, user))
668 end
669
670 test "repeating a status privately" do
671 user = insert(:user)
672 other_user = insert(:user)
673
674 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
675
676 {:ok, %Activity{} = announce_activity} =
677 CommonAPI.repeat(activity.id, user, %{visibility: "private"})
678
679 assert Visibility.is_private?(announce_activity)
680 refute Visibility.visible_for_user?(announce_activity, nil)
681 end
682
683 test "favoriting a status" do
684 user = insert(:user)
685 other_user = insert(:user)
686
687 {:ok, post_activity} = CommonAPI.post(other_user, %{status: "cofe"})
688
689 {:ok, %Activity{data: data}} = CommonAPI.favorite(user, post_activity.id)
690 assert data["type"] == "Like"
691 assert data["actor"] == user.ap_id
692 assert data["object"] == post_activity.data["object"]
693 end
694
695 test "retweeting a status twice returns the status" do
696 user = insert(:user)
697 other_user = insert(:user)
698
699 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
700 {:ok, %Activity{} = announce} = CommonAPI.repeat(activity.id, user)
701 {:ok, ^announce} = CommonAPI.repeat(activity.id, user)
702 end
703
704 test "favoriting a status twice returns ok, but without the like activity" do
705 user = insert(:user)
706 other_user = insert(:user)
707
708 {:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
709 {:ok, %Activity{}} = CommonAPI.favorite(user, activity.id)
710 assert {:ok, :already_liked} = CommonAPI.favorite(user, activity.id)
711 end
712 end
713
714 describe "pinned statuses" do
715 setup do
716 Pleroma.Config.put([:instance, :max_pinned_statuses], 1)
717
718 user = insert(:user)
719 {:ok, activity} = CommonAPI.post(user, %{status: "HI!!!"})
720
721 [user: user, activity: activity]
722 end
723
724 test "pin status", %{user: user, activity: activity} do
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 "pin poll", %{user: user} do
734 {:ok, activity} =
735 CommonAPI.post(user, %{
736 status: "How is fediverse today?",
737 poll: %{options: ["Absolutely outstanding", "Not good"], expires_in: 20}
738 })
739
740 assert {:ok, ^activity} = CommonAPI.pin(activity.id, user)
741
742 id = activity.id
743 user = refresh_record(user)
744
745 assert %User{pinned_activities: [^id]} = user
746 end
747
748 test "unlisted statuses can be pinned", %{user: user} do
749 {:ok, activity} = CommonAPI.post(user, %{status: "HI!!!", visibility: "unlisted"})
750 assert {:ok, ^activity} = CommonAPI.pin(activity.id, user)
751 end
752
753 test "only self-authored can be pinned", %{activity: activity} do
754 user = insert(:user)
755
756 assert {:error, "Could not pin"} = CommonAPI.pin(activity.id, user)
757 end
758
759 test "max pinned statuses", %{user: user, activity: activity_one} do
760 {:ok, activity_two} = CommonAPI.post(user, %{status: "HI!!!"})
761
762 assert {:ok, ^activity_one} = CommonAPI.pin(activity_one.id, user)
763
764 user = refresh_record(user)
765
766 assert {:error, "You have already pinned the maximum number of statuses"} =
767 CommonAPI.pin(activity_two.id, user)
768 end
769
770 test "unpin status", %{user: user, activity: activity} do
771 {:ok, activity} = CommonAPI.pin(activity.id, user)
772
773 user = refresh_record(user)
774
775 id = activity.id
776
777 assert match?({:ok, %{id: ^id}}, CommonAPI.unpin(activity.id, user))
778
779 user = refresh_record(user)
780
781 assert %User{pinned_activities: []} = user
782 end
783
784 test "should unpin when deleting a status", %{user: user, activity: activity} do
785 {:ok, activity} = CommonAPI.pin(activity.id, user)
786
787 user = refresh_record(user)
788
789 assert {:ok, _} = CommonAPI.delete(activity.id, user)
790
791 user = refresh_record(user)
792
793 assert %User{pinned_activities: []} = user
794 end
795 end
796
797 describe "mute tests" do
798 setup do
799 user = insert(:user)
800
801 activity = insert(:note_activity)
802
803 [user: user, activity: activity]
804 end
805
806 test "add mute", %{user: user, activity: activity} do
807 {:ok, _} = CommonAPI.add_mute(user, activity)
808 assert CommonAPI.thread_muted?(user, activity)
809 end
810
811 test "remove mute", %{user: user, activity: activity} do
812 CommonAPI.add_mute(user, activity)
813 {:ok, _} = CommonAPI.remove_mute(user, activity)
814 refute CommonAPI.thread_muted?(user, activity)
815 end
816
817 test "check that mutes can't be duplicate", %{user: user, activity: activity} do
818 CommonAPI.add_mute(user, activity)
819 {:error, _} = CommonAPI.add_mute(user, activity)
820 end
821 end
822
823 describe "reports" do
824 test "creates a report" do
825 reporter = insert(:user)
826 target_user = insert(:user)
827
828 {:ok, activity} = CommonAPI.post(target_user, %{status: "foobar"})
829
830 reporter_ap_id = reporter.ap_id
831 target_ap_id = target_user.ap_id
832 activity_ap_id = activity.data["id"]
833 comment = "foobar"
834
835 report_data = %{
836 account_id: target_user.id,
837 comment: comment,
838 status_ids: [activity.id]
839 }
840
841 note_obj = %{
842 "type" => "Note",
843 "id" => activity_ap_id,
844 "content" => "foobar",
845 "published" => activity.object.data["published"],
846 "actor" => AccountView.render("show.json", %{user: target_user})
847 }
848
849 assert {:ok, flag_activity} = CommonAPI.report(reporter, report_data)
850
851 assert %Activity{
852 actor: ^reporter_ap_id,
853 data: %{
854 "type" => "Flag",
855 "content" => ^comment,
856 "object" => [^target_ap_id, ^note_obj],
857 "state" => "open"
858 }
859 } = flag_activity
860 end
861
862 test "updates report state" do
863 [reporter, target_user] = insert_pair(:user)
864 activity = insert(:note_activity, user: target_user)
865
866 {:ok, %Activity{id: report_id}} =
867 CommonAPI.report(reporter, %{
868 account_id: target_user.id,
869 comment: "I feel offended",
870 status_ids: [activity.id]
871 })
872
873 {:ok, report} = CommonAPI.update_report_state(report_id, "resolved")
874
875 assert report.data["state"] == "resolved"
876
877 [reported_user, activity_id] = report.data["object"]
878
879 assert reported_user == target_user.ap_id
880 assert activity_id == activity.data["id"]
881 end
882
883 test "does not update report state when state is unsupported" do
884 [reporter, target_user] = insert_pair(:user)
885 activity = insert(:note_activity, user: target_user)
886
887 {:ok, %Activity{id: report_id}} =
888 CommonAPI.report(reporter, %{
889 account_id: target_user.id,
890 comment: "I feel offended",
891 status_ids: [activity.id]
892 })
893
894 assert CommonAPI.update_report_state(report_id, "test") == {:error, "Unsupported state"}
895 end
896
897 test "updates state of multiple reports" do
898 [reporter, target_user] = insert_pair(:user)
899 activity = insert(:note_activity, user: target_user)
900
901 {:ok, %Activity{id: first_report_id}} =
902 CommonAPI.report(reporter, %{
903 account_id: target_user.id,
904 comment: "I feel offended",
905 status_ids: [activity.id]
906 })
907
908 {:ok, %Activity{id: second_report_id}} =
909 CommonAPI.report(reporter, %{
910 account_id: target_user.id,
911 comment: "I feel very offended!",
912 status_ids: [activity.id]
913 })
914
915 {:ok, report_ids} =
916 CommonAPI.update_report_state([first_report_id, second_report_id], "resolved")
917
918 first_report = Activity.get_by_id(first_report_id)
919 second_report = Activity.get_by_id(second_report_id)
920
921 assert report_ids -- [first_report_id, second_report_id] == []
922 assert first_report.data["state"] == "resolved"
923 assert second_report.data["state"] == "resolved"
924 end
925 end
926
927 describe "reblog muting" do
928 setup do
929 muter = insert(:user)
930
931 muted = insert(:user)
932
933 [muter: muter, muted: muted]
934 end
935
936 test "add a reblog mute", %{muter: muter, muted: muted} do
937 {:ok, _reblog_mute} = CommonAPI.hide_reblogs(muter, muted)
938
939 assert User.showing_reblogs?(muter, muted) == false
940 end
941
942 test "remove a reblog mute", %{muter: muter, muted: muted} do
943 {:ok, _reblog_mute} = CommonAPI.hide_reblogs(muter, muted)
944 {:ok, _reblog_mute} = CommonAPI.show_reblogs(muter, muted)
945
946 assert User.showing_reblogs?(muter, muted) == true
947 end
948 end
949
950 describe "follow/2" do
951 test "directly follows a non-locked local user" do
952 [follower, followed] = insert_pair(:user)
953 {:ok, follower, followed, _} = CommonAPI.follow(follower, followed)
954
955 assert User.following?(follower, followed)
956 end
957 end
958
959 describe "unfollow/2" do
960 test "also unsubscribes a user" do
961 [follower, followed] = insert_pair(:user)
962 {:ok, follower, followed, _} = CommonAPI.follow(follower, followed)
963 {:ok, _subscription} = User.subscribe(follower, followed)
964
965 assert User.subscribed_to?(follower, followed)
966
967 {:ok, follower} = CommonAPI.unfollow(follower, followed)
968
969 refute User.subscribed_to?(follower, followed)
970 end
971
972 test "cancels a pending follow for a local user" do
973 follower = insert(:user)
974 followed = insert(:user, locked: 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
994 test "cancels a pending follow for a remote user" do
995 follower = insert(:user)
996 followed = insert(:user, locked: true, local: false, ap_enabled: true)
997
998 assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} =
999 CommonAPI.follow(follower, followed)
1000
1001 assert User.get_follow_state(follower, followed) == :follow_pending
1002 assert {:ok, follower} = CommonAPI.unfollow(follower, followed)
1003 assert User.get_follow_state(follower, followed) == nil
1004
1005 assert %{id: ^activity_id, data: %{"state" => "cancelled"}} =
1006 Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(follower, followed)
1007
1008 assert %{
1009 data: %{
1010 "type" => "Undo",
1011 "object" => %{"type" => "Follow", "state" => "cancelled"}
1012 }
1013 } = Pleroma.Web.ActivityPub.Utils.fetch_latest_undo(follower)
1014 end
1015 end
1016
1017 describe "accept_follow_request/2" do
1018 test "after acceptance, it sets all existing pending follow request states to 'accept'" do
1019 user = insert(:user, locked: true)
1020 follower = insert(:user)
1021 follower_two = insert(:user)
1022
1023 {:ok, _, _, follow_activity} = CommonAPI.follow(follower, user)
1024 {:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user)
1025 {:ok, _, _, follow_activity_three} = CommonAPI.follow(follower_two, user)
1026
1027 assert follow_activity.data["state"] == "pending"
1028 assert follow_activity_two.data["state"] == "pending"
1029 assert follow_activity_three.data["state"] == "pending"
1030
1031 {:ok, _follower} = CommonAPI.accept_follow_request(follower, user)
1032
1033 assert Repo.get(Activity, follow_activity.id).data["state"] == "accept"
1034 assert Repo.get(Activity, follow_activity_two.id).data["state"] == "accept"
1035 assert Repo.get(Activity, follow_activity_three.id).data["state"] == "pending"
1036 end
1037
1038 test "after rejection, it sets all existing pending follow request states to 'reject'" do
1039 user = insert(:user, locked: true)
1040 follower = insert(:user)
1041 follower_two = insert(:user)
1042
1043 {:ok, _, _, follow_activity} = CommonAPI.follow(follower, user)
1044 {:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user)
1045 {:ok, _, _, follow_activity_three} = CommonAPI.follow(follower_two, user)
1046
1047 assert follow_activity.data["state"] == "pending"
1048 assert follow_activity_two.data["state"] == "pending"
1049 assert follow_activity_three.data["state"] == "pending"
1050
1051 {:ok, _follower} = CommonAPI.reject_follow_request(follower, user)
1052
1053 assert Repo.get(Activity, follow_activity.id).data["state"] == "reject"
1054 assert Repo.get(Activity, follow_activity_two.id).data["state"] == "reject"
1055 assert Repo.get(Activity, follow_activity_three.id).data["state"] == "pending"
1056 end
1057
1058 test "doesn't create a following relationship if the corresponding follow request doesn't exist" do
1059 user = insert(:user, locked: true)
1060 not_follower = insert(:user)
1061 CommonAPI.accept_follow_request(not_follower, user)
1062
1063 assert Pleroma.FollowingRelationship.following?(not_follower, user) == false
1064 end
1065 end
1066
1067 describe "vote/3" do
1068 test "does not allow to vote twice" do
1069 user = insert(:user)
1070 other_user = insert(:user)
1071
1072 {:ok, activity} =
1073 CommonAPI.post(user, %{
1074 status: "Am I cute?",
1075 poll: %{options: ["Yes", "No"], expires_in: 20}
1076 })
1077
1078 object = Object.normalize(activity)
1079
1080 {:ok, _, object} = CommonAPI.vote(other_user, object, [0])
1081
1082 assert {:error, "Already voted"} == CommonAPI.vote(other_user, object, [1])
1083 end
1084 end
1085
1086 describe "listen/2" do
1087 test "returns a valid activity" do
1088 user = insert(:user)
1089
1090 {:ok, activity} =
1091 CommonAPI.listen(user, %{
1092 title: "lain radio episode 1",
1093 album: "lain radio",
1094 artist: "lain",
1095 length: 180_000
1096 })
1097
1098 object = Object.normalize(activity)
1099
1100 assert object.data["title"] == "lain radio episode 1"
1101
1102 assert Visibility.get_visibility(activity) == "public"
1103 end
1104
1105 test "respects visibility=private" do
1106 user = insert(:user)
1107
1108 {:ok, activity} =
1109 CommonAPI.listen(user, %{
1110 title: "lain radio episode 1",
1111 album: "lain radio",
1112 artist: "lain",
1113 length: 180_000,
1114 visibility: "private"
1115 })
1116
1117 object = Object.normalize(activity)
1118
1119 assert object.data["title"] == "lain radio episode 1"
1120
1121 assert Visibility.get_visibility(activity) == "private"
1122 end
1123 end
1124 end