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
11 alias Pleroma.Conversation.Participation
14 alias Pleroma.ScheduledActivity
16 alias Pleroma.Web.ActivityPub.ActivityPub
17 alias Pleroma.Web.CommonAPI
19 import Pleroma.Factory
21 describe "posting statuses" do
27 |> assign(:user, user)
32 test "posting a status", %{conn: conn} do
33 idempotency_key = "Pikachu rocks!"
37 |> put_req_header("idempotency-key", idempotency_key)
38 |> post("/api/v1/statuses", %{
40 "spoiler_text" => "2hu",
41 "sensitive" => "false"
44 {:ok, ttl} = Cachex.ttl(:idempotency_cache, idempotency_key)
46 assert ttl > :timer.seconds(6 * 60 * 60 - 1)
48 assert %{"content" => "cofe", "id" => id, "spoiler_text" => "2hu", "sensitive" => false} =
49 json_response(conn_one, 200)
51 assert Activity.get_by_id(id)
55 |> put_req_header("idempotency-key", idempotency_key)
56 |> post("/api/v1/statuses", %{
58 "spoiler_text" => "2hu",
59 "sensitive" => "false"
62 assert %{"id" => second_id} = json_response(conn_two, 200)
63 assert id == second_id
67 |> post("/api/v1/statuses", %{
69 "spoiler_text" => "2hu",
70 "sensitive" => "false"
73 assert %{"id" => third_id} = json_response(conn_three, 200)
76 # An activity that will expire:
82 |> post("api/v1/statuses", %{
84 "expires_in" => expires_in
87 assert fourth_response = %{"id" => fourth_id} = json_response(conn_four, 200)
88 assert activity = Activity.get_by_id(fourth_id)
89 assert expiration = ActivityExpiration.get_by_activity_id(fourth_id)
91 estimated_expires_at =
92 NaiveDateTime.utc_now()
93 |> NaiveDateTime.add(expires_in)
94 |> NaiveDateTime.truncate(:second)
96 # This assert will fail if the test takes longer than a minute. I sure hope it never does:
97 assert abs(NaiveDateTime.diff(expiration.scheduled_at, estimated_expires_at, :second)) < 60
99 assert fourth_response["pleroma"]["expires_at"] ==
100 NaiveDateTime.to_iso8601(expiration.scheduled_at)
103 test "posting an undefined status with an attachment", %{conn: conn} do
107 content_type: "image/jpg",
108 path: Path.absname("test/fixtures/image.jpg"),
109 filename: "an_image.jpg"
112 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
116 |> assign(:user, user)
117 |> post("/api/v1/statuses", %{
118 "media_ids" => [to_string(upload.id)]
121 assert json_response(conn, 200)
124 test "replying to a status", %{conn: conn} do
126 {:ok, replied_to} = CommonAPI.post(user, %{"status" => "cofe"})
130 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
132 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
134 activity = Activity.get_by_id(id)
136 assert activity.data["context"] == replied_to.data["context"]
137 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
140 test "replying to a direct message with visibility other than direct", %{conn: conn} do
142 {:ok, replied_to} = CommonAPI.post(user, %{"status" => "suya..", "visibility" => "direct"})
144 Enum.each(["public", "private", "unlisted"], fn visibility ->
147 |> post("/api/v1/statuses", %{
148 "status" => "@#{user.nickname} hey",
149 "in_reply_to_id" => replied_to.id,
150 "visibility" => visibility
153 assert json_response(conn, 422) == %{"error" => "The message visibility must be direct"}
157 test "posting a status with an invalid in_reply_to_id", %{conn: conn} do
160 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""})
162 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
163 assert Activity.get_by_id(id)
166 test "posting a sensitive status", %{conn: conn} do
169 |> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true})
171 assert %{"content" => "cofe", "id" => id, "sensitive" => true} = json_response(conn, 200)
172 assert Activity.get_by_id(id)
175 test "posting a fake status", %{conn: conn} do
178 |> post("/api/v1/statuses", %{
180 "\"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"
183 real_status = json_response(real_conn, 200)
186 assert Object.get_by_ap_id(real_status["uri"])
190 |> Map.put("id", nil)
191 |> Map.put("url", nil)
192 |> Map.put("uri", nil)
193 |> Map.put("created_at", nil)
194 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
198 |> post("/api/v1/statuses", %{
200 "\"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",
204 fake_status = json_response(fake_conn, 200)
207 refute Object.get_by_ap_id(fake_status["uri"])
211 |> Map.put("id", nil)
212 |> Map.put("url", nil)
213 |> Map.put("uri", nil)
214 |> Map.put("created_at", nil)
215 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
217 assert real_status == fake_status
220 test "posting a status with OGP link preview", %{conn: conn} do
221 Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
222 Config.put([:rich_media, :enabled], true)
226 |> post("/api/v1/statuses", %{
227 "status" => "https://example.com/ogp"
230 assert %{"id" => id, "card" => %{"title" => "The Rock"}} = json_response(conn, 200)
231 assert Activity.get_by_id(id)
234 test "posting a direct status", %{conn: conn} do
235 user2 = insert(:user)
236 content = "direct cofe @#{user2.nickname}"
240 |> post("api/v1/statuses", %{"status" => content, "visibility" => "direct"})
242 assert %{"id" => id} = response = json_response(conn, 200)
243 assert response["visibility"] == "direct"
244 assert response["pleroma"]["direct_conversation_id"]
245 assert activity = Activity.get_by_id(id)
246 assert activity.recipients == [user2.ap_id, conn.assigns[:user].ap_id]
247 assert activity.data["to"] == [user2.ap_id]
248 assert activity.data["cc"] == []
252 describe "posting scheduled statuses" do
253 test "creates a scheduled activity", %{conn: conn} do
255 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
259 |> assign(:user, user)
260 |> post("/api/v1/statuses", %{
261 "status" => "scheduled",
262 "scheduled_at" => scheduled_at
265 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
266 assert expected_scheduled_at == CommonAPI.Utils.to_masto_date(scheduled_at)
267 assert [] == Repo.all(Activity)
270 test "creates a scheduled activity with a media attachment", %{conn: conn} do
272 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
275 content_type: "image/jpg",
276 path: Path.absname("test/fixtures/image.jpg"),
277 filename: "an_image.jpg"
280 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
284 |> assign(:user, user)
285 |> post("/api/v1/statuses", %{
286 "media_ids" => [to_string(upload.id)],
287 "status" => "scheduled",
288 "scheduled_at" => scheduled_at
291 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
292 assert %{"type" => "image"} = media_attachment
295 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
300 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
304 |> assign(:user, user)
305 |> post("/api/v1/statuses", %{
306 "status" => "not scheduled",
307 "scheduled_at" => scheduled_at
310 assert %{"content" => "not scheduled"} = json_response(conn, 200)
311 assert [] == Repo.all(ScheduledActivity)
314 test "returns error when daily user limit is exceeded", %{conn: conn} do
318 NaiveDateTime.utc_now()
319 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
320 |> NaiveDateTime.to_iso8601()
322 attrs = %{params: %{}, scheduled_at: today}
323 {:ok, _} = ScheduledActivity.create(user, attrs)
324 {:ok, _} = ScheduledActivity.create(user, attrs)
328 |> assign(:user, user)
329 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
331 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
334 test "returns error when total user limit is exceeded", %{conn: conn} do
338 NaiveDateTime.utc_now()
339 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
340 |> NaiveDateTime.to_iso8601()
343 NaiveDateTime.utc_now()
344 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
345 |> NaiveDateTime.to_iso8601()
347 attrs = %{params: %{}, scheduled_at: today}
348 {:ok, _} = ScheduledActivity.create(user, attrs)
349 {:ok, _} = ScheduledActivity.create(user, attrs)
350 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
354 |> assign(:user, user)
355 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
357 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
361 describe "posting polls" do
362 test "posting a poll", %{conn: conn} do
364 time = NaiveDateTime.utc_now()
368 |> assign(:user, user)
369 |> post("/api/v1/statuses", %{
370 "status" => "Who is the #bestgrill?",
371 "poll" => %{"options" => ["Rei", "Asuka", "Misato"], "expires_in" => 420}
374 response = json_response(conn, 200)
376 assert Enum.all?(response["poll"]["options"], fn %{"title" => title} ->
377 title in ["Rei", "Asuka", "Misato"]
380 assert NaiveDateTime.diff(NaiveDateTime.from_iso8601!(response["poll"]["expires_at"]), time) in 420..430
381 refute response["poll"]["expred"]
384 test "option limit is enforced", %{conn: conn} do
386 limit = Config.get([:instance, :poll_limits, :max_options])
390 |> assign(:user, user)
391 |> post("/api/v1/statuses", %{
393 "poll" => %{"options" => Enum.map(0..limit, fn _ -> "desu" end), "expires_in" => 1}
396 %{"error" => error} = json_response(conn, 422)
397 assert error == "Poll can't contain more than #{limit} options"
400 test "option character limit is enforced", %{conn: conn} do
402 limit = Config.get([:instance, :poll_limits, :max_option_chars])
406 |> assign(:user, user)
407 |> post("/api/v1/statuses", %{
410 "options" => [Enum.reduce(0..limit, "", fn _, acc -> acc <> "." end)],
415 %{"error" => error} = json_response(conn, 422)
416 assert error == "Poll options cannot be longer than #{limit} characters each"
419 test "minimal date limit is enforced", %{conn: conn} do
421 limit = Config.get([:instance, :poll_limits, :min_expiration])
425 |> assign(:user, user)
426 |> post("/api/v1/statuses", %{
427 "status" => "imagine arbitrary limits",
429 "options" => ["this post was made by pleroma gang"],
430 "expires_in" => limit - 1
434 %{"error" => error} = json_response(conn, 422)
435 assert error == "Expiration date is too soon"
438 test "maximum date limit is enforced", %{conn: conn} do
440 limit = Config.get([:instance, :poll_limits, :max_expiration])
444 |> assign(:user, user)
445 |> post("/api/v1/statuses", %{
446 "status" => "imagine arbitrary limits",
448 "options" => ["this post was made by pleroma gang"],
449 "expires_in" => limit + 1
453 %{"error" => error} = json_response(conn, 422)
454 assert error == "Expiration date is too far in the future"
458 test "get a status", %{conn: conn} do
459 activity = insert(:note_activity)
463 |> get("/api/v1/statuses/#{activity.id}")
465 assert %{"id" => id} = json_response(conn, 200)
466 assert id == to_string(activity.id)
469 test "get a direct status", %{conn: conn} do
471 other_user = insert(:user)
474 CommonAPI.post(user, %{"status" => "@#{other_user.nickname}", "visibility" => "direct"})
478 |> assign(:user, user)
479 |> get("/api/v1/statuses/#{activity.id}")
481 [participation] = Participation.for_user(user)
483 res = json_response(conn, 200)
484 assert res["pleroma"]["direct_conversation_id"] == participation.id
487 test "get statuses by IDs", %{conn: conn} do
488 %{id: id1} = insert(:note_activity)
489 %{id: id2} = insert(:note_activity)
491 query_string = "ids[]=#{id1}&ids[]=#{id2}"
492 conn = get(conn, "/api/v1/statuses/?#{query_string}")
494 assert [%{"id" => ^id1}, %{"id" => ^id2}] = Enum.sort_by(json_response(conn, :ok), & &1["id"])
497 describe "deleting a status" do
498 test "when you created it", %{conn: conn} do
499 activity = insert(:note_activity)
500 author = User.get_cached_by_ap_id(activity.data["actor"])
504 |> assign(:user, author)
505 |> delete("/api/v1/statuses/#{activity.id}")
507 assert %{} = json_response(conn, 200)
509 refute Activity.get_by_id(activity.id)
512 test "when you didn't create it", %{conn: conn} do
513 activity = insert(:note_activity)
518 |> assign(:user, user)
519 |> delete("/api/v1/statuses/#{activity.id}")
521 assert %{"error" => _} = json_response(conn, 403)
523 assert Activity.get_by_id(activity.id) == activity
526 test "when you're an admin or moderator", %{conn: conn} do
527 activity1 = insert(:note_activity)
528 activity2 = insert(:note_activity)
529 admin = insert(:user, info: %{is_admin: true})
530 moderator = insert(:user, info: %{is_moderator: true})
534 |> assign(:user, admin)
535 |> delete("/api/v1/statuses/#{activity1.id}")
537 assert %{} = json_response(res_conn, 200)
541 |> assign(:user, moderator)
542 |> delete("/api/v1/statuses/#{activity2.id}")
544 assert %{} = json_response(res_conn, 200)
546 refute Activity.get_by_id(activity1.id)
547 refute Activity.get_by_id(activity2.id)
551 describe "reblogging" do
552 test "reblogs and returns the reblogged status", %{conn: conn} do
553 activity = insert(:note_activity)
558 |> assign(:user, user)
559 |> post("/api/v1/statuses/#{activity.id}/reblog")
562 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1},
564 } = json_response(conn, 200)
566 assert to_string(activity.id) == id
569 test "reblogs privately and returns the reblogged status", %{conn: conn} do
570 activity = insert(:note_activity)
575 |> assign(:user, user)
576 |> post("/api/v1/statuses/#{activity.id}/reblog", %{"visibility" => "private"})
579 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1},
581 "visibility" => "private"
582 } = json_response(conn, 200)
584 assert to_string(activity.id) == id
587 test "reblogged status for another user", %{conn: conn} do
588 activity = insert(:note_activity)
589 user1 = insert(:user)
590 user2 = insert(:user)
591 user3 = insert(:user)
592 CommonAPI.favorite(activity.id, user2)
593 {:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
594 {:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
595 {:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
599 |> assign(:user, user3)
600 |> get("/api/v1/statuses/#{reblog_activity1.id}")
603 "reblog" => %{"id" => id, "reblogged" => false, "reblogs_count" => 2},
604 "reblogged" => false,
605 "favourited" => false,
606 "bookmarked" => false
607 } = json_response(conn_res, 200)
611 |> assign(:user, user2)
612 |> get("/api/v1/statuses/#{reblog_activity1.id}")
615 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 2},
617 "favourited" => true,
619 } = json_response(conn_res, 200)
621 assert to_string(activity.id) == id
624 test "returns 400 error when activity is not exist", %{conn: conn} do
629 |> assign(:user, user)
630 |> post("/api/v1/statuses/foo/reblog")
632 assert json_response(conn, 400) == %{"error" => "Could not repeat"}
636 describe "unreblogging" do
637 test "unreblogs and returns the unreblogged status", %{conn: conn} do
638 activity = insert(:note_activity)
641 {:ok, _, _} = CommonAPI.repeat(activity.id, user)
645 |> assign(:user, user)
646 |> post("/api/v1/statuses/#{activity.id}/unreblog")
648 assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200)
650 assert to_string(activity.id) == id
653 test "returns 400 error when activity is not exist", %{conn: conn} do
658 |> assign(:user, user)
659 |> post("/api/v1/statuses/foo/unreblog")
661 assert json_response(conn, 400) == %{"error" => "Could not unrepeat"}
665 describe "favoriting" do
666 test "favs a status and returns it", %{conn: conn} do
667 activity = insert(:note_activity)
672 |> assign(:user, user)
673 |> post("/api/v1/statuses/#{activity.id}/favourite")
675 assert %{"id" => id, "favourites_count" => 1, "favourited" => true} =
676 json_response(conn, 200)
678 assert to_string(activity.id) == id
681 test "returns 400 error for a wrong id", %{conn: conn} do
686 |> assign(:user, user)
687 |> post("/api/v1/statuses/1/favourite")
689 assert json_response(conn, 400) == %{"error" => "Could not favorite"}
693 describe "unfavoriting" do
694 test "unfavorites a status and returns it", %{conn: conn} do
695 activity = insert(:note_activity)
698 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
702 |> assign(:user, user)
703 |> post("/api/v1/statuses/#{activity.id}/unfavourite")
705 assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
706 json_response(conn, 200)
708 assert to_string(activity.id) == id
711 test "returns 400 error for a wrong id", %{conn: conn} do
716 |> assign(:user, user)
717 |> post("/api/v1/statuses/1/unfavourite")
719 assert json_response(conn, 400) == %{"error" => "Could not unfavorite"}
723 describe "pinned statuses" do
726 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
728 [user: user, activity: activity]
731 clear_config([:instance, :max_pinned_statuses]) do
732 Config.put([:instance, :max_pinned_statuses], 1)
735 test "pin status", %{conn: conn, user: user, activity: activity} do
736 id_str = to_string(activity.id)
738 assert %{"id" => ^id_str, "pinned" => true} =
740 |> assign(:user, user)
741 |> post("/api/v1/statuses/#{activity.id}/pin")
742 |> json_response(200)
744 assert [%{"id" => ^id_str, "pinned" => true}] =
746 |> assign(:user, user)
747 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
748 |> json_response(200)
751 test "/pin: returns 400 error when activity is not public", %{conn: conn, user: user} do
752 {:ok, dm} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"})
756 |> assign(:user, user)
757 |> post("/api/v1/statuses/#{dm.id}/pin")
759 assert json_response(conn, 400) == %{"error" => "Could not pin"}
762 test "unpin status", %{conn: conn, user: user, activity: activity} do
763 {:ok, _} = CommonAPI.pin(activity.id, user)
765 id_str = to_string(activity.id)
766 user = refresh_record(user)
768 assert %{"id" => ^id_str, "pinned" => false} =
770 |> assign(:user, user)
771 |> post("/api/v1/statuses/#{activity.id}/unpin")
772 |> json_response(200)
776 |> assign(:user, user)
777 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
778 |> json_response(200)
781 test "/unpin: returns 400 error when activity is not exist", %{conn: conn, user: user} do
784 |> assign(:user, user)
785 |> post("/api/v1/statuses/1/unpin")
787 assert json_response(conn, 400) == %{"error" => "Could not unpin"}
790 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
791 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
793 id_str_one = to_string(activity_one.id)
795 assert %{"id" => ^id_str_one, "pinned" => true} =
797 |> assign(:user, user)
798 |> post("/api/v1/statuses/#{id_str_one}/pin")
799 |> json_response(200)
801 user = refresh_record(user)
803 assert %{"error" => "You have already pinned the maximum number of statuses"} =
805 |> assign(:user, user)
806 |> post("/api/v1/statuses/#{activity_two.id}/pin")
807 |> json_response(400)
813 Config.put([:rich_media, :enabled], true)
819 test "returns rich-media card", %{conn: conn, user: user} do
820 Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
822 {:ok, activity} = CommonAPI.post(user, %{"status" => "https://example.com/ogp"})
825 "image" => "http://ia.media-imdb.com/images/rock.jpg",
826 "provider_name" => "example.com",
827 "provider_url" => "https://example.com",
828 "title" => "The Rock",
830 "url" => "https://example.com/ogp",
832 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
835 "image" => "http://ia.media-imdb.com/images/rock.jpg",
836 "title" => "The Rock",
837 "type" => "video.movie",
838 "url" => "https://example.com/ogp",
840 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer."
847 |> get("/api/v1/statuses/#{activity.id}/card")
848 |> json_response(200)
850 assert response == card_data
852 # works with private posts
854 CommonAPI.post(user, %{"status" => "https://example.com/ogp", "visibility" => "direct"})
858 |> assign(:user, user)
859 |> get("/api/v1/statuses/#{activity.id}/card")
860 |> json_response(200)
862 assert response_two == card_data
865 test "replaces missing description with an empty string", %{conn: conn, user: user} do
866 Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
869 CommonAPI.post(user, %{"status" => "https://example.com/ogp-missing-data"})
873 |> get("/api/v1/statuses/#{activity.id}/card")
874 |> json_response(:ok)
876 assert response == %{
878 "title" => "Pleroma",
881 "provider_name" => "example.com",
882 "provider_url" => "https://example.com",
883 "url" => "https://example.com/ogp-missing-data",
886 "title" => "Pleroma",
888 "url" => "https://example.com/ogp-missing-data"
897 for_user = insert(:user)
900 CommonAPI.post(user, %{
901 "status" => "heweoo?"
905 CommonAPI.post(user, %{
906 "status" => "heweoo!"
911 |> assign(:user, for_user)
912 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
914 assert json_response(response1, 200)["bookmarked"] == true
918 |> assign(:user, for_user)
919 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
921 assert json_response(response2, 200)["bookmarked"] == true
925 |> assign(:user, for_user)
926 |> get("/api/v1/bookmarks")
928 assert [json_response(response2, 200), json_response(response1, 200)] ==
929 json_response(bookmarks, 200)
933 |> assign(:user, for_user)
934 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
936 assert json_response(response1, 200)["bookmarked"] == false
940 |> assign(:user, for_user)
941 |> get("/api/v1/bookmarks")
943 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
946 describe "conversation muting" do
948 post_user = insert(:user)
951 {:ok, activity} = CommonAPI.post(post_user, %{"status" => "HIE"})
953 [user: user, activity: activity]
956 test "mute conversation", %{conn: conn, user: user, activity: activity} do
957 id_str = to_string(activity.id)
959 assert %{"id" => ^id_str, "muted" => true} =
961 |> assign(:user, user)
962 |> post("/api/v1/statuses/#{activity.id}/mute")
963 |> json_response(200)
966 test "cannot mute already muted conversation", %{conn: conn, user: user, activity: activity} do
967 {:ok, _} = CommonAPI.add_mute(user, activity)
971 |> assign(:user, user)
972 |> post("/api/v1/statuses/#{activity.id}/mute")
974 assert json_response(conn, 400) == %{"error" => "conversation is already muted"}
977 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
978 {:ok, _} = CommonAPI.add_mute(user, activity)
980 id_str = to_string(activity.id)
981 user = refresh_record(user)
983 assert %{"id" => ^id_str, "muted" => false} =
985 |> assign(:user, user)
986 |> post("/api/v1/statuses/#{activity.id}/unmute")
987 |> json_response(200)
991 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
992 user1 = insert(:user)
993 user2 = insert(:user)
994 user3 = insert(:user)
996 {:ok, replied_to} = CommonAPI.post(user1, %{"status" => "cofe"})
998 # Reply to status from another user
1001 |> assign(:user, user2)
1002 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
1004 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
1006 activity = Activity.get_by_id_with_object(id)
1008 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
1009 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
1011 # Reblog from the third user
1014 |> assign(:user, user3)
1015 |> post("/api/v1/statuses/#{activity.id}/reblog")
1017 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
1018 json_response(conn2, 200)
1020 assert to_string(activity.id) == id
1022 # Getting third user status
1025 |> assign(:user, user3)
1026 |> get("api/v1/timelines/home")
1028 [reblogged_activity] = json_response(conn3, 200)
1030 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
1032 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
1033 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
1036 describe "GET /api/v1/statuses/:id/favourited_by" do
1038 user = insert(:user)
1039 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
1043 |> assign(:user, user)
1045 [conn: conn, activity: activity, user: user]
1048 test "returns users who have favorited the status", %{conn: conn, activity: activity} do
1049 other_user = insert(:user)
1050 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
1054 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
1055 |> json_response(:ok)
1057 [%{"id" => id}] = response
1059 assert id == other_user.id
1062 test "returns empty array when status has not been favorited yet", %{
1068 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
1069 |> json_response(:ok)
1071 assert Enum.empty?(response)
1074 test "does not return users who have favorited the status but are blocked", %{
1075 conn: %{assigns: %{user: user}} = conn,
1078 other_user = insert(:user)
1079 {:ok, user} = User.block(user, other_user)
1081 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
1085 |> assign(:user, user)
1086 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
1087 |> json_response(:ok)
1089 assert Enum.empty?(response)
1092 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
1093 other_user = insert(:user)
1094 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
1098 |> assign(:user, nil)
1099 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
1100 |> json_response(:ok)
1102 [%{"id" => id}] = response
1103 assert id == other_user.id
1106 test "requires authentification for private posts", %{conn: conn, user: user} do
1107 other_user = insert(:user)
1110 CommonAPI.post(user, %{
1111 "status" => "@#{other_user.nickname} wanna get some #cofe together?",
1112 "visibility" => "direct"
1115 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
1118 |> assign(:user, nil)
1119 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
1120 |> json_response(404)
1124 |> assign(:user, other_user)
1125 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
1126 |> json_response(200)
1128 [%{"id" => id}] = response
1129 assert id == other_user.id
1133 describe "GET /api/v1/statuses/:id/reblogged_by" do
1135 user = insert(:user)
1136 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
1140 |> assign(:user, user)
1142 [conn: conn, activity: activity, user: user]
1145 test "returns users who have reblogged the status", %{conn: conn, activity: activity} do
1146 other_user = insert(:user)
1147 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
1151 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
1152 |> json_response(:ok)
1154 [%{"id" => id}] = response
1156 assert id == other_user.id
1159 test "returns empty array when status has not been reblogged yet", %{
1165 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
1166 |> json_response(:ok)
1168 assert Enum.empty?(response)
1171 test "does not return users who have reblogged the status but are blocked", %{
1172 conn: %{assigns: %{user: user}} = conn,
1175 other_user = insert(:user)
1176 {:ok, user} = User.block(user, other_user)
1178 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
1182 |> assign(:user, user)
1183 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
1184 |> json_response(:ok)
1186 assert Enum.empty?(response)
1189 test "does not return users who have reblogged the status privately", %{
1190 conn: %{assigns: %{user: user}} = conn,
1193 other_user = insert(:user)
1195 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user, %{"visibility" => "private"})
1199 |> assign(:user, user)
1200 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
1201 |> json_response(:ok)
1203 assert Enum.empty?(response)
1206 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
1207 other_user = insert(:user)
1208 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
1212 |> assign(:user, nil)
1213 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
1214 |> json_response(:ok)
1216 [%{"id" => id}] = response
1217 assert id == other_user.id
1220 test "requires authentification for private posts", %{conn: conn, user: user} do
1221 other_user = insert(:user)
1224 CommonAPI.post(user, %{
1225 "status" => "@#{other_user.nickname} wanna get some #cofe together?",
1226 "visibility" => "direct"
1230 |> assign(:user, nil)
1231 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
1232 |> json_response(404)
1236 |> assign(:user, other_user)
1237 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
1238 |> json_response(200)
1240 assert [] == response
1245 user = insert(:user)
1247 {:ok, %{id: id1}} = CommonAPI.post(user, %{"status" => "1"})
1248 {:ok, %{id: id2}} = CommonAPI.post(user, %{"status" => "2", "in_reply_to_status_id" => id1})
1249 {:ok, %{id: id3}} = CommonAPI.post(user, %{"status" => "3", "in_reply_to_status_id" => id2})
1250 {:ok, %{id: id4}} = CommonAPI.post(user, %{"status" => "4", "in_reply_to_status_id" => id3})
1251 {:ok, %{id: id5}} = CommonAPI.post(user, %{"status" => "5", "in_reply_to_status_id" => id4})
1255 |> assign(:user, nil)
1256 |> get("/api/v1/statuses/#{id3}/context")
1257 |> json_response(:ok)
1260 "ancestors" => [%{"id" => ^id1}, %{"id" => ^id2}],
1261 "descendants" => [%{"id" => ^id4}, %{"id" => ^id5}]
1265 test "returns the favorites of a user", %{conn: conn} do
1266 user = insert(:user)
1267 other_user = insert(:user)
1269 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
1270 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
1272 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1276 |> assign(:user, user)
1277 |> get("/api/v1/favourites")
1279 assert [status] = json_response(first_conn, 200)
1280 assert status["id"] == to_string(activity.id)
1282 assert [{"link", _link_header}] =
1283 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
1285 # Honours query params
1286 {:ok, second_activity} =
1287 CommonAPI.post(other_user, %{
1289 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
1292 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
1294 last_like = status["id"]
1298 |> assign(:user, user)
1299 |> get("/api/v1/favourites?since_id=#{last_like}")
1301 assert [second_status] = json_response(second_conn, 200)
1302 assert second_status["id"] == to_string(second_activity.id)
1306 |> assign(:user, user)
1307 |> get("/api/v1/favourites?limit=0")
1309 assert [] = json_response(third_conn, 200)