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