Merge branch 'user-search-nickname-rank-boost' 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 {: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 "follow/2" do
938 test "directly follows a non-locked local user" do
939 [follower, followed] = insert_pair(:user)
940 {:ok, follower, followed, _} = CommonAPI.follow(follower, followed)
941
942 assert User.following?(follower, followed)
943 end
944 end
945
946 describe "unfollow/2" do
947 test "also unsubscribes a user" do
948 [follower, followed] = insert_pair(:user)
949 {:ok, follower, followed, _} = CommonAPI.follow(follower, followed)
950 {:ok, _subscription} = User.subscribe(follower, followed)
951
952 assert User.subscribed_to?(follower, followed)
953
954 {:ok, follower} = CommonAPI.unfollow(follower, followed)
955
956 refute User.subscribed_to?(follower, followed)
957 end
958
959 test "cancels a pending follow for a local user" do
960 follower = insert(:user)
961 followed = insert(:user, locked: true)
962
963 assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} =
964 CommonAPI.follow(follower, followed)
965
966 assert User.get_follow_state(follower, followed) == :follow_pending
967 assert {:ok, follower} = CommonAPI.unfollow(follower, followed)
968 assert User.get_follow_state(follower, followed) == nil
969
970 assert %{id: ^activity_id, data: %{"state" => "cancelled"}} =
971 Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(follower, followed)
972
973 assert %{
974 data: %{
975 "type" => "Undo",
976 "object" => %{"type" => "Follow", "state" => "cancelled"}
977 }
978 } = Pleroma.Web.ActivityPub.Utils.fetch_latest_undo(follower)
979 end
980
981 test "cancels a pending follow for a remote user" do
982 follower = insert(:user)
983 followed = insert(:user, locked: true, local: false, ap_enabled: true)
984
985 assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} =
986 CommonAPI.follow(follower, followed)
987
988 assert User.get_follow_state(follower, followed) == :follow_pending
989 assert {:ok, follower} = CommonAPI.unfollow(follower, followed)
990 assert User.get_follow_state(follower, followed) == nil
991
992 assert %{id: ^activity_id, data: %{"state" => "cancelled"}} =
993 Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(follower, followed)
994
995 assert %{
996 data: %{
997 "type" => "Undo",
998 "object" => %{"type" => "Follow", "state" => "cancelled"}
999 }
1000 } = Pleroma.Web.ActivityPub.Utils.fetch_latest_undo(follower)
1001 end
1002 end
1003
1004 describe "accept_follow_request/2" do
1005 test "after acceptance, it sets all existing pending follow request states to 'accept'" do
1006 user = insert(:user, locked: true)
1007 follower = insert(:user)
1008 follower_two = insert(:user)
1009
1010 {:ok, _, _, follow_activity} = CommonAPI.follow(follower, user)
1011 {:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user)
1012 {:ok, _, _, follow_activity_three} = CommonAPI.follow(follower_two, user)
1013
1014 assert follow_activity.data["state"] == "pending"
1015 assert follow_activity_two.data["state"] == "pending"
1016 assert follow_activity_three.data["state"] == "pending"
1017
1018 {:ok, _follower} = CommonAPI.accept_follow_request(follower, user)
1019
1020 assert Repo.get(Activity, follow_activity.id).data["state"] == "accept"
1021 assert Repo.get(Activity, follow_activity_two.id).data["state"] == "accept"
1022 assert Repo.get(Activity, follow_activity_three.id).data["state"] == "pending"
1023 end
1024
1025 test "after rejection, it sets all existing pending follow request states to 'reject'" do
1026 user = insert(:user, locked: true)
1027 follower = insert(:user)
1028 follower_two = insert(:user)
1029
1030 {:ok, _, _, follow_activity} = CommonAPI.follow(follower, user)
1031 {:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user)
1032 {:ok, _, _, follow_activity_three} = CommonAPI.follow(follower_two, user)
1033
1034 assert follow_activity.data["state"] == "pending"
1035 assert follow_activity_two.data["state"] == "pending"
1036 assert follow_activity_three.data["state"] == "pending"
1037
1038 {:ok, _follower} = CommonAPI.reject_follow_request(follower, user)
1039
1040 assert Repo.get(Activity, follow_activity.id).data["state"] == "reject"
1041 assert Repo.get(Activity, follow_activity_two.id).data["state"] == "reject"
1042 assert Repo.get(Activity, follow_activity_three.id).data["state"] == "pending"
1043 end
1044
1045 test "doesn't create a following relationship if the corresponding follow request doesn't exist" do
1046 user = insert(:user, locked: true)
1047 not_follower = insert(:user)
1048 CommonAPI.accept_follow_request(not_follower, user)
1049
1050 assert Pleroma.FollowingRelationship.following?(not_follower, user) == false
1051 end
1052 end
1053
1054 describe "vote/3" do
1055 test "does not allow to vote twice" do
1056 user = insert(:user)
1057 other_user = insert(:user)
1058
1059 {:ok, activity} =
1060 CommonAPI.post(user, %{
1061 status: "Am I cute?",
1062 poll: %{options: ["Yes", "No"], expires_in: 20}
1063 })
1064
1065 object = Object.normalize(activity)
1066
1067 {:ok, _, object} = CommonAPI.vote(other_user, object, [0])
1068
1069 assert {:error, "Already voted"} == CommonAPI.vote(other_user, object, [1])
1070 end
1071 end
1072
1073 describe "listen/2" do
1074 test "returns a valid activity" do
1075 user = insert(:user)
1076
1077 {:ok, activity} =
1078 CommonAPI.listen(user, %{
1079 title: "lain radio episode 1",
1080 album: "lain radio",
1081 artist: "lain",
1082 length: 180_000
1083 })
1084
1085 object = Object.normalize(activity)
1086
1087 assert object.data["title"] == "lain radio episode 1"
1088
1089 assert Visibility.get_visibility(activity) == "public"
1090 end
1091
1092 test "respects visibility=private" 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 visibility: "private"
1102 })
1103
1104 object = Object.normalize(activity)
1105
1106 assert object.data["title"] == "lain radio episode 1"
1107
1108 assert Visibility.get_visibility(activity) == "private"
1109 end
1110 end
1111 end