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