1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
6 use Pleroma.Web.ConnCase
9 alias Pleroma.ActivityExpiration
13 alias Pleroma.ScheduledActivity
15 alias Pleroma.Web.ActivityPub.ActivityPub
16 alias Pleroma.Web.CommonAPI
18 import Pleroma.Factory
20 describe "posting statuses" do
26 |> assign(:user, user)
31 test "posting a status", %{conn: conn} do
32 idempotency_key = "Pikachu rocks!"
36 |> put_req_header("idempotency-key", idempotency_key)
37 |> post("/api/v1/statuses", %{
39 "spoiler_text" => "2hu",
40 "sensitive" => "false"
43 {:ok, ttl} = Cachex.ttl(:idempotency_cache, idempotency_key)
45 assert ttl > :timer.seconds(6 * 60 * 60 - 1)
47 assert %{"content" => "cofe", "id" => id, "spoiler_text" => "2hu", "sensitive" => false} =
48 json_response(conn_one, 200)
50 assert Activity.get_by_id(id)
54 |> put_req_header("idempotency-key", idempotency_key)
55 |> post("/api/v1/statuses", %{
57 "spoiler_text" => "2hu",
58 "sensitive" => "false"
61 assert %{"id" => second_id} = json_response(conn_two, 200)
62 assert id == second_id
66 |> post("/api/v1/statuses", %{
68 "spoiler_text" => "2hu",
69 "sensitive" => "false"
72 assert %{"id" => third_id} = json_response(conn_three, 200)
75 # An activity that will expire:
81 |> post("api/v1/statuses", %{
83 "expires_in" => expires_in
86 assert fourth_response = %{"id" => fourth_id} = json_response(conn_four, 200)
87 assert activity = Activity.get_by_id(fourth_id)
88 assert expiration = ActivityExpiration.get_by_activity_id(fourth_id)
90 estimated_expires_at =
91 NaiveDateTime.utc_now()
92 |> NaiveDateTime.add(expires_in)
93 |> NaiveDateTime.truncate(:second)
95 # This assert will fail if the test takes longer than a minute. I sure hope it never does:
96 assert abs(NaiveDateTime.diff(expiration.scheduled_at, estimated_expires_at, :second)) < 60
98 assert fourth_response["pleroma"]["expires_at"] ==
99 NaiveDateTime.to_iso8601(expiration.scheduled_at)
102 test "replying to a status", %{conn: conn} do
104 {:ok, replied_to} = CommonAPI.post(user, %{"status" => "cofe"})
108 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
110 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
112 activity = Activity.get_by_id(id)
114 assert activity.data["context"] == replied_to.data["context"]
115 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
118 test "replying to a direct message with visibility other than direct", %{conn: conn} do
120 {:ok, replied_to} = CommonAPI.post(user, %{"status" => "suya..", "visibility" => "direct"})
122 Enum.each(["public", "private", "unlisted"], fn visibility ->
125 |> post("/api/v1/statuses", %{
126 "status" => "@#{user.nickname} hey",
127 "in_reply_to_id" => replied_to.id,
128 "visibility" => visibility
131 assert json_response(conn, 422) == %{"error" => "The message visibility must be direct"}
135 test "posting a status with an invalid in_reply_to_id", %{conn: conn} do
138 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""})
140 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
141 assert Activity.get_by_id(id)
144 test "posting a sensitive status", %{conn: conn} do
147 |> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true})
149 assert %{"content" => "cofe", "id" => id, "sensitive" => true} = json_response(conn, 200)
150 assert Activity.get_by_id(id)
153 test "posting a fake status", %{conn: conn} do
156 |> post("/api/v1/statuses", %{
158 "\"Tenshi Eating a Corndog\" is a much discussed concept on /jp/. The significance of it is disputed, so I will focus on one core concept: the symbolism behind it"
161 real_status = json_response(real_conn, 200)
164 assert Object.get_by_ap_id(real_status["uri"])
168 |> Map.put("id", nil)
169 |> Map.put("url", nil)
170 |> Map.put("uri", nil)
171 |> Map.put("created_at", nil)
172 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
176 |> post("/api/v1/statuses", %{
178 "\"Tenshi Eating a Corndog\" is a much discussed concept on /jp/. The significance of it is disputed, so I will focus on one core concept: the symbolism behind it",
182 fake_status = json_response(fake_conn, 200)
185 refute Object.get_by_ap_id(fake_status["uri"])
189 |> Map.put("id", nil)
190 |> Map.put("url", nil)
191 |> Map.put("uri", nil)
192 |> Map.put("created_at", nil)
193 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
195 assert real_status == fake_status
198 test "posting a status with OGP link preview", %{conn: conn} do
199 Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
200 Config.put([:rich_media, :enabled], true)
204 |> post("/api/v1/statuses", %{
205 "status" => "https://example.com/ogp"
208 assert %{"id" => id, "card" => %{"title" => "The Rock"}} = json_response(conn, 200)
209 assert Activity.get_by_id(id)
212 test "posting a direct status", %{conn: conn} do
213 user2 = insert(:user)
214 content = "direct cofe @#{user2.nickname}"
218 |> post("api/v1/statuses", %{"status" => content, "visibility" => "direct"})
220 assert %{"id" => id} = response = json_response(conn, 200)
221 assert response["visibility"] == "direct"
222 assert response["pleroma"]["direct_conversation_id"]
223 assert activity = Activity.get_by_id(id)
224 assert activity.recipients == [user2.ap_id, conn.assigns[:user].ap_id]
225 assert activity.data["to"] == [user2.ap_id]
226 assert activity.data["cc"] == []
230 describe "posting scheduled statuses" do
231 test "creates a scheduled activity", %{conn: conn} do
233 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
237 |> assign(:user, user)
238 |> post("/api/v1/statuses", %{
239 "status" => "scheduled",
240 "scheduled_at" => scheduled_at
243 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
244 assert expected_scheduled_at == CommonAPI.Utils.to_masto_date(scheduled_at)
245 assert [] == Repo.all(Activity)
248 test "creates a scheduled activity with a media attachment", %{conn: conn} do
250 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
253 content_type: "image/jpg",
254 path: Path.absname("test/fixtures/image.jpg"),
255 filename: "an_image.jpg"
258 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
262 |> assign(:user, user)
263 |> post("/api/v1/statuses", %{
264 "media_ids" => [to_string(upload.id)],
265 "status" => "scheduled",
266 "scheduled_at" => scheduled_at
269 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
270 assert %{"type" => "image"} = media_attachment
273 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
278 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
282 |> assign(:user, user)
283 |> post("/api/v1/statuses", %{
284 "status" => "not scheduled",
285 "scheduled_at" => scheduled_at
288 assert %{"content" => "not scheduled"} = json_response(conn, 200)
289 assert [] == Repo.all(ScheduledActivity)
292 test "returns error when daily user limit is exceeded", %{conn: conn} do
296 NaiveDateTime.utc_now()
297 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
298 |> NaiveDateTime.to_iso8601()
300 attrs = %{params: %{}, scheduled_at: today}
301 {:ok, _} = ScheduledActivity.create(user, attrs)
302 {:ok, _} = ScheduledActivity.create(user, attrs)
306 |> assign(:user, user)
307 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
309 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
312 test "returns error when total user limit is exceeded", %{conn: conn} do
316 NaiveDateTime.utc_now()
317 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
318 |> NaiveDateTime.to_iso8601()
321 NaiveDateTime.utc_now()
322 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
323 |> NaiveDateTime.to_iso8601()
325 attrs = %{params: %{}, scheduled_at: today}
326 {:ok, _} = ScheduledActivity.create(user, attrs)
327 {:ok, _} = ScheduledActivity.create(user, attrs)
328 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
332 |> assign(:user, user)
333 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
335 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
339 describe "posting polls" do
340 test "posting a poll", %{conn: conn} do
342 time = NaiveDateTime.utc_now()
346 |> assign(:user, user)
347 |> post("/api/v1/statuses", %{
348 "status" => "Who is the #bestgrill?",
349 "poll" => %{"options" => ["Rei", "Asuka", "Misato"], "expires_in" => 420}
352 response = json_response(conn, 200)
354 assert Enum.all?(response["poll"]["options"], fn %{"title" => title} ->
355 title in ["Rei", "Asuka", "Misato"]
358 assert NaiveDateTime.diff(NaiveDateTime.from_iso8601!(response["poll"]["expires_at"]), time) in 420..430
359 refute response["poll"]["expred"]
362 test "option limit is enforced", %{conn: conn} do
364 limit = Config.get([:instance, :poll_limits, :max_options])
368 |> assign(:user, user)
369 |> post("/api/v1/statuses", %{
371 "poll" => %{"options" => Enum.map(0..limit, fn _ -> "desu" end), "expires_in" => 1}
374 %{"error" => error} = json_response(conn, 422)
375 assert error == "Poll can't contain more than #{limit} options"
378 test "option character limit is enforced", %{conn: conn} do
380 limit = Config.get([:instance, :poll_limits, :max_option_chars])
384 |> assign(:user, user)
385 |> post("/api/v1/statuses", %{
388 "options" => [Enum.reduce(0..limit, "", fn _, acc -> acc <> "." end)],
393 %{"error" => error} = json_response(conn, 422)
394 assert error == "Poll options cannot be longer than #{limit} characters each"
397 test "minimal date limit is enforced", %{conn: conn} do
399 limit = Config.get([:instance, :poll_limits, :min_expiration])
403 |> assign(:user, user)
404 |> post("/api/v1/statuses", %{
405 "status" => "imagine arbitrary limits",
407 "options" => ["this post was made by pleroma gang"],
408 "expires_in" => limit - 1
412 %{"error" => error} = json_response(conn, 422)
413 assert error == "Expiration date is too soon"
416 test "maximum date limit is enforced", %{conn: conn} do
418 limit = Config.get([:instance, :poll_limits, :max_expiration])
422 |> assign(:user, user)
423 |> post("/api/v1/statuses", %{
424 "status" => "imagine arbitrary limits",
426 "options" => ["this post was made by pleroma gang"],
427 "expires_in" => limit + 1
431 %{"error" => error} = json_response(conn, 422)
432 assert error == "Expiration date is too far in the future"
436 test "get a status", %{conn: conn} do
437 activity = insert(:note_activity)
441 |> get("/api/v1/statuses/#{activity.id}")
443 assert %{"id" => id} = json_response(conn, 200)
444 assert id == to_string(activity.id)
447 test "get statuses by IDs", %{conn: conn} do
448 %{id: id1} = insert(:note_activity)
449 %{id: id2} = insert(:note_activity)
451 query_string = "ids[]=#{id1}&ids[]=#{id2}"
452 conn = get(conn, "/api/v1/statuses/?#{query_string}")
454 assert [%{"id" => ^id1}, %{"id" => ^id2}] = Enum.sort_by(json_response(conn, :ok), & &1["id"])
457 describe "deleting a status" do
458 test "when you created it", %{conn: conn} do
459 activity = insert(:note_activity)
460 author = User.get_cached_by_ap_id(activity.data["actor"])
464 |> assign(:user, author)
465 |> delete("/api/v1/statuses/#{activity.id}")
467 assert %{} = json_response(conn, 200)
469 refute Activity.get_by_id(activity.id)
472 test "when you didn't create it", %{conn: conn} do
473 activity = insert(:note_activity)
478 |> assign(:user, user)
479 |> delete("/api/v1/statuses/#{activity.id}")
481 assert %{"error" => _} = json_response(conn, 403)
483 assert Activity.get_by_id(activity.id) == activity
486 test "when you're an admin or moderator", %{conn: conn} do
487 activity1 = insert(:note_activity)
488 activity2 = insert(:note_activity)
489 admin = insert(:user, info: %{is_admin: true})
490 moderator = insert(:user, info: %{is_moderator: true})
494 |> assign(:user, admin)
495 |> delete("/api/v1/statuses/#{activity1.id}")
497 assert %{} = json_response(res_conn, 200)
501 |> assign(:user, moderator)
502 |> delete("/api/v1/statuses/#{activity2.id}")
504 assert %{} = json_response(res_conn, 200)
506 refute Activity.get_by_id(activity1.id)
507 refute Activity.get_by_id(activity2.id)
511 describe "reblogging" do
512 test "reblogs and returns the reblogged status", %{conn: conn} do
513 activity = insert(:note_activity)
518 |> assign(:user, user)
519 |> post("/api/v1/statuses/#{activity.id}/reblog")
522 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1},
524 } = json_response(conn, 200)
526 assert to_string(activity.id) == id
529 test "reblogged status for another user", %{conn: conn} do
530 activity = insert(:note_activity)
531 user1 = insert(:user)
532 user2 = insert(:user)
533 user3 = insert(:user)
534 CommonAPI.favorite(activity.id, user2)
535 {:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
536 {:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
537 {:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
541 |> assign(:user, user3)
542 |> get("/api/v1/statuses/#{reblog_activity1.id}")
545 "reblog" => %{"id" => id, "reblogged" => false, "reblogs_count" => 2},
546 "reblogged" => false,
547 "favourited" => false,
548 "bookmarked" => false
549 } = json_response(conn_res, 200)
553 |> assign(:user, user2)
554 |> get("/api/v1/statuses/#{reblog_activity1.id}")
557 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 2},
559 "favourited" => true,
561 } = json_response(conn_res, 200)
563 assert to_string(activity.id) == id
566 test "returns 400 error when activity is not exist", %{conn: conn} do
571 |> assign(:user, user)
572 |> post("/api/v1/statuses/foo/reblog")
574 assert json_response(conn, 400) == %{"error" => "Could not repeat"}
578 describe "unreblogging" do
579 test "unreblogs and returns the unreblogged status", %{conn: conn} do
580 activity = insert(:note_activity)
583 {:ok, _, _} = CommonAPI.repeat(activity.id, user)
587 |> assign(:user, user)
588 |> post("/api/v1/statuses/#{activity.id}/unreblog")
590 assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200)
592 assert to_string(activity.id) == id
595 test "returns 400 error when activity is not exist", %{conn: conn} do
600 |> assign(:user, user)
601 |> post("/api/v1/statuses/foo/unreblog")
603 assert json_response(conn, 400) == %{"error" => "Could not unrepeat"}
607 describe "favoriting" do
608 test "favs a status and returns it", %{conn: conn} do
609 activity = insert(:note_activity)
614 |> assign(:user, user)
615 |> post("/api/v1/statuses/#{activity.id}/favourite")
617 assert %{"id" => id, "favourites_count" => 1, "favourited" => true} =
618 json_response(conn, 200)
620 assert to_string(activity.id) == id
623 test "returns 400 error for a wrong id", %{conn: conn} do
628 |> assign(:user, user)
629 |> post("/api/v1/statuses/1/favourite")
631 assert json_response(conn, 400) == %{"error" => "Could not favorite"}
635 describe "unfavoriting" do
636 test "unfavorites a status and returns it", %{conn: conn} do
637 activity = insert(:note_activity)
640 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
644 |> assign(:user, user)
645 |> post("/api/v1/statuses/#{activity.id}/unfavourite")
647 assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
648 json_response(conn, 200)
650 assert to_string(activity.id) == id
653 test "returns 400 error for a wrong id", %{conn: conn} do
658 |> assign(:user, user)
659 |> post("/api/v1/statuses/1/unfavourite")
661 assert json_response(conn, 400) == %{"error" => "Could not unfavorite"}
665 describe "pinned statuses" do
668 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
670 [user: user, activity: activity]
673 clear_config([:instance, :max_pinned_statuses]) do
674 Config.put([:instance, :max_pinned_statuses], 1)
677 test "pin status", %{conn: conn, user: user, activity: activity} do
678 id_str = to_string(activity.id)
680 assert %{"id" => ^id_str, "pinned" => true} =
682 |> assign(:user, user)
683 |> post("/api/v1/statuses/#{activity.id}/pin")
684 |> json_response(200)
686 assert [%{"id" => ^id_str, "pinned" => true}] =
688 |> assign(:user, user)
689 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
690 |> json_response(200)
693 test "/pin: returns 400 error when activity is not public", %{conn: conn, user: user} do
694 {:ok, dm} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"})
698 |> assign(:user, user)
699 |> post("/api/v1/statuses/#{dm.id}/pin")
701 assert json_response(conn, 400) == %{"error" => "Could not pin"}
704 test "unpin status", %{conn: conn, user: user, activity: activity} do
705 {:ok, _} = CommonAPI.pin(activity.id, user)
707 id_str = to_string(activity.id)
708 user = refresh_record(user)
710 assert %{"id" => ^id_str, "pinned" => false} =
712 |> assign(:user, user)
713 |> post("/api/v1/statuses/#{activity.id}/unpin")
714 |> json_response(200)
718 |> assign(:user, user)
719 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
720 |> json_response(200)
723 test "/unpin: returns 400 error when activity is not exist", %{conn: conn, user: user} do
726 |> assign(:user, user)
727 |> post("/api/v1/statuses/1/unpin")
729 assert json_response(conn, 400) == %{"error" => "Could not unpin"}
732 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
733 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
735 id_str_one = to_string(activity_one.id)
737 assert %{"id" => ^id_str_one, "pinned" => true} =
739 |> assign(:user, user)
740 |> post("/api/v1/statuses/#{id_str_one}/pin")
741 |> json_response(200)
743 user = refresh_record(user)
745 assert %{"error" => "You have already pinned the maximum number of statuses"} =
747 |> assign(:user, user)
748 |> post("/api/v1/statuses/#{activity_two.id}/pin")
749 |> json_response(400)
755 Config.put([:rich_media, :enabled], true)
761 test "returns rich-media card", %{conn: conn, user: user} do
762 Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
764 {:ok, activity} = CommonAPI.post(user, %{"status" => "https://example.com/ogp"})
767 "image" => "http://ia.media-imdb.com/images/rock.jpg",
768 "provider_name" => "example.com",
769 "provider_url" => "https://example.com",
770 "title" => "The Rock",
772 "url" => "https://example.com/ogp",
774 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
777 "image" => "http://ia.media-imdb.com/images/rock.jpg",
778 "title" => "The Rock",
779 "type" => "video.movie",
780 "url" => "https://example.com/ogp",
782 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer."
789 |> get("/api/v1/statuses/#{activity.id}/card")
790 |> json_response(200)
792 assert response == card_data
794 # works with private posts
796 CommonAPI.post(user, %{"status" => "https://example.com/ogp", "visibility" => "direct"})
800 |> assign(:user, user)
801 |> get("/api/v1/statuses/#{activity.id}/card")
802 |> json_response(200)
804 assert response_two == card_data
807 test "replaces missing description with an empty string", %{conn: conn, user: user} do
808 Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
811 CommonAPI.post(user, %{"status" => "https://example.com/ogp-missing-data"})
815 |> get("/api/v1/statuses/#{activity.id}/card")
816 |> json_response(:ok)
818 assert response == %{
820 "title" => "Pleroma",
823 "provider_name" => "example.com",
824 "provider_url" => "https://example.com",
825 "url" => "https://example.com/ogp-missing-data",
828 "title" => "Pleroma",
830 "url" => "https://example.com/ogp-missing-data"
839 for_user = insert(:user)
842 CommonAPI.post(user, %{
843 "status" => "heweoo?"
847 CommonAPI.post(user, %{
848 "status" => "heweoo!"
853 |> assign(:user, for_user)
854 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
856 assert json_response(response1, 200)["bookmarked"] == true
860 |> assign(:user, for_user)
861 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
863 assert json_response(response2, 200)["bookmarked"] == true
867 |> assign(:user, for_user)
868 |> get("/api/v1/bookmarks")
870 assert [json_response(response2, 200), json_response(response1, 200)] ==
871 json_response(bookmarks, 200)
875 |> assign(:user, for_user)
876 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
878 assert json_response(response1, 200)["bookmarked"] == false
882 |> assign(:user, for_user)
883 |> get("/api/v1/bookmarks")
885 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
888 describe "conversation muting" do
890 post_user = insert(:user)
893 {:ok, activity} = CommonAPI.post(post_user, %{"status" => "HIE"})
895 [user: user, activity: activity]
898 test "mute conversation", %{conn: conn, user: user, activity: activity} do
899 id_str = to_string(activity.id)
901 assert %{"id" => ^id_str, "muted" => true} =
903 |> assign(:user, user)
904 |> post("/api/v1/statuses/#{activity.id}/mute")
905 |> json_response(200)
908 test "cannot mute already muted conversation", %{conn: conn, user: user, activity: activity} do
909 {:ok, _} = CommonAPI.add_mute(user, activity)
913 |> assign(:user, user)
914 |> post("/api/v1/statuses/#{activity.id}/mute")
916 assert json_response(conn, 400) == %{"error" => "conversation is already muted"}
919 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
920 {:ok, _} = CommonAPI.add_mute(user, activity)
922 id_str = to_string(activity.id)
923 user = refresh_record(user)
925 assert %{"id" => ^id_str, "muted" => false} =
927 |> assign(:user, user)
928 |> post("/api/v1/statuses/#{activity.id}/unmute")
929 |> json_response(200)
933 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
934 user1 = insert(:user)
935 user2 = insert(:user)
936 user3 = insert(:user)
938 {:ok, replied_to} = CommonAPI.post(user1, %{"status" => "cofe"})
940 # Reply to status from another user
943 |> assign(:user, user2)
944 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
946 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
948 activity = Activity.get_by_id_with_object(id)
950 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
951 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
953 # Reblog from the third user
956 |> assign(:user, user3)
957 |> post("/api/v1/statuses/#{activity.id}/reblog")
959 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
960 json_response(conn2, 200)
962 assert to_string(activity.id) == id
964 # Getting third user status
967 |> assign(:user, user3)
968 |> get("api/v1/timelines/home")
970 [reblogged_activity] = json_response(conn3, 200)
972 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
974 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
975 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
978 describe "GET /api/v1/statuses/:id/favourited_by" do
981 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
985 |> assign(:user, user)
987 [conn: conn, activity: activity, user: user]
990 test "returns users who have favorited the status", %{conn: conn, activity: activity} do
991 other_user = insert(:user)
992 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
996 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
997 |> json_response(:ok)
999 [%{"id" => id}] = response
1001 assert id == other_user.id
1004 test "returns empty array when status has not been favorited yet", %{
1010 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
1011 |> json_response(:ok)
1013 assert Enum.empty?(response)
1016 test "does not return users who have favorited the status but are blocked", %{
1017 conn: %{assigns: %{user: user}} = conn,
1020 other_user = insert(:user)
1021 {:ok, user} = User.block(user, other_user)
1023 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
1027 |> assign(:user, user)
1028 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
1029 |> json_response(:ok)
1031 assert Enum.empty?(response)
1034 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
1035 other_user = insert(:user)
1036 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
1040 |> assign(:user, nil)
1041 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
1042 |> json_response(:ok)
1044 [%{"id" => id}] = response
1045 assert id == other_user.id
1048 test "requires authentification for private posts", %{conn: conn, user: user} do
1049 other_user = insert(:user)
1052 CommonAPI.post(user, %{
1053 "status" => "@#{other_user.nickname} wanna get some #cofe together?",
1054 "visibility" => "direct"
1057 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
1060 |> assign(:user, nil)
1061 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
1062 |> json_response(404)
1066 |> assign(:user, other_user)
1067 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
1068 |> json_response(200)
1070 [%{"id" => id}] = response
1071 assert id == other_user.id
1075 describe "GET /api/v1/statuses/:id/reblogged_by" do
1077 user = insert(:user)
1078 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
1082 |> assign(:user, user)
1084 [conn: conn, activity: activity, user: user]
1087 test "returns users who have reblogged the status", %{conn: conn, activity: activity} do
1088 other_user = insert(:user)
1089 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
1093 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
1094 |> json_response(:ok)
1096 [%{"id" => id}] = response
1098 assert id == other_user.id
1101 test "returns empty array when status has not been reblogged yet", %{
1107 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
1108 |> json_response(:ok)
1110 assert Enum.empty?(response)
1113 test "does not return users who have reblogged the status but are blocked", %{
1114 conn: %{assigns: %{user: user}} = conn,
1117 other_user = insert(:user)
1118 {:ok, user} = User.block(user, other_user)
1120 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
1124 |> assign(:user, user)
1125 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
1126 |> json_response(:ok)
1128 assert Enum.empty?(response)
1131 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
1132 other_user = insert(:user)
1133 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
1137 |> assign(:user, nil)
1138 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
1139 |> json_response(:ok)
1141 [%{"id" => id}] = response
1142 assert id == other_user.id
1145 test "requires authentification for private posts", %{conn: conn, user: user} do
1146 other_user = insert(:user)
1149 CommonAPI.post(user, %{
1150 "status" => "@#{other_user.nickname} wanna get some #cofe together?",
1151 "visibility" => "direct"
1155 |> assign(:user, nil)
1156 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
1157 |> json_response(404)
1161 |> assign(:user, other_user)
1162 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
1163 |> json_response(200)
1165 assert [] == response
1170 user = insert(:user)
1172 {:ok, %{id: id1}} = CommonAPI.post(user, %{"status" => "1"})
1173 {:ok, %{id: id2}} = CommonAPI.post(user, %{"status" => "2", "in_reply_to_status_id" => id1})
1174 {:ok, %{id: id3}} = CommonAPI.post(user, %{"status" => "3", "in_reply_to_status_id" => id2})
1175 {:ok, %{id: id4}} = CommonAPI.post(user, %{"status" => "4", "in_reply_to_status_id" => id3})
1176 {:ok, %{id: id5}} = CommonAPI.post(user, %{"status" => "5", "in_reply_to_status_id" => id4})
1180 |> assign(:user, nil)
1181 |> get("/api/v1/statuses/#{id3}/context")
1182 |> json_response(:ok)
1185 "ancestors" => [%{"id" => ^id1}, %{"id" => ^id2}],
1186 "descendants" => [%{"id" => ^id4}, %{"id" => ^id5}]