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