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