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