From: Egor Kislitsyn Date: Thu, 26 Sep 2019 06:38:45 +0000 (+0700) Subject: Move StatusController tests from MastodonAPIControllerTest to StatusControllerTest X-Git-Url: https://git.squeep.com/?a=commitdiff_plain;h=76b7e5cd5b34e68055bab7353cb845f3429d897f;p=akkoma Move StatusController tests from MastodonAPIControllerTest to StatusControllerTest --- diff --git a/test/web/mastodon_api/controllers/status_controller_test.exs b/test/web/mastodon_api/controllers/status_controller_test.exs new file mode 100644 index 000000000..f80ce7704 --- /dev/null +++ b/test/web/mastodon_api/controllers/status_controller_test.exs @@ -0,0 +1,1056 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do + use Pleroma.Web.ConnCase + + alias Pleroma.Activity + alias Pleroma.ActivityExpiration + alias Pleroma.Config + alias Pleroma.Object + alias Pleroma.User + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + describe "posting statuses" do + setup do + user = insert(:user) + + conn = + build_conn() + |> assign(:user, user) + + [conn: conn] + end + + test "posting a status", %{conn: conn} do + idempotency_key = "Pikachu rocks!" + + conn_one = + conn + |> put_req_header("idempotency-key", idempotency_key) + |> post("/api/v1/statuses", %{ + "status" => "cofe", + "spoiler_text" => "2hu", + "sensitive" => "false" + }) + + {:ok, ttl} = Cachex.ttl(:idempotency_cache, idempotency_key) + # Six hours + assert ttl > :timer.seconds(6 * 60 * 60 - 1) + + assert %{"content" => "cofe", "id" => id, "spoiler_text" => "2hu", "sensitive" => false} = + json_response(conn_one, 200) + + assert Activity.get_by_id(id) + + conn_two = + conn + |> put_req_header("idempotency-key", idempotency_key) + |> post("/api/v1/statuses", %{ + "status" => "cofe", + "spoiler_text" => "2hu", + "sensitive" => "false" + }) + + assert %{"id" => second_id} = json_response(conn_two, 200) + assert id == second_id + + conn_three = + conn + |> post("/api/v1/statuses", %{ + "status" => "cofe", + "spoiler_text" => "2hu", + "sensitive" => "false" + }) + + assert %{"id" => third_id} = json_response(conn_three, 200) + refute id == third_id + + # An activity that will expire: + # 2 hours + expires_in = 120 * 60 + + conn_four = + conn + |> post("api/v1/statuses", %{ + "status" => "oolong", + "expires_in" => expires_in + }) + + assert fourth_response = %{"id" => fourth_id} = json_response(conn_four, 200) + assert activity = Activity.get_by_id(fourth_id) + assert expiration = ActivityExpiration.get_by_activity_id(fourth_id) + + estimated_expires_at = + NaiveDateTime.utc_now() + |> NaiveDateTime.add(expires_in) + |> NaiveDateTime.truncate(:second) + + # This assert will fail if the test takes longer than a minute. I sure hope it never does: + assert abs(NaiveDateTime.diff(expiration.scheduled_at, estimated_expires_at, :second)) < 60 + + assert fourth_response["pleroma"]["expires_at"] == + NaiveDateTime.to_iso8601(expiration.scheduled_at) + end + + test "replying to a status", %{conn: conn} do + user = insert(:user) + {:ok, replied_to} = CommonAPI.post(user, %{"status" => "cofe"}) + + conn = + conn + |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id}) + + assert %{"content" => "xD", "id" => id} = json_response(conn, 200) + + activity = Activity.get_by_id(id) + + assert activity.data["context"] == replied_to.data["context"] + assert Activity.get_in_reply_to_activity(activity).id == replied_to.id + end + + test "replying to a direct message with visibility other than direct", %{conn: conn} do + user = insert(:user) + {:ok, replied_to} = CommonAPI.post(user, %{"status" => "suya..", "visibility" => "direct"}) + + Enum.each(["public", "private", "unlisted"], fn visibility -> + conn = + conn + |> post("/api/v1/statuses", %{ + "status" => "@#{user.nickname} hey", + "in_reply_to_id" => replied_to.id, + "visibility" => visibility + }) + + assert json_response(conn, 422) == %{"error" => "The message visibility must be direct"} + end) + end + + test "posting a status with an invalid in_reply_to_id", %{conn: conn} do + conn = + conn + |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""}) + + assert %{"content" => "xD", "id" => id} = json_response(conn, 200) + assert Activity.get_by_id(id) + end + + test "posting a sensitive status", %{conn: conn} do + conn = + conn + |> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true}) + + assert %{"content" => "cofe", "id" => id, "sensitive" => true} = json_response(conn, 200) + assert Activity.get_by_id(id) + end + + test "posting a fake status", %{conn: conn} do + real_conn = + conn + |> post("/api/v1/statuses", %{ + "status" => + "\"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" + }) + + real_status = json_response(real_conn, 200) + + assert real_status + assert Object.get_by_ap_id(real_status["uri"]) + + real_status = + real_status + |> Map.put("id", nil) + |> Map.put("url", nil) + |> Map.put("uri", nil) + |> Map.put("created_at", nil) + |> Kernel.put_in(["pleroma", "conversation_id"], nil) + + fake_conn = + conn + |> post("/api/v1/statuses", %{ + "status" => + "\"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", + "preview" => true + }) + + fake_status = json_response(fake_conn, 200) + + assert fake_status + refute Object.get_by_ap_id(fake_status["uri"]) + + fake_status = + fake_status + |> Map.put("id", nil) + |> Map.put("url", nil) + |> Map.put("uri", nil) + |> Map.put("created_at", nil) + |> Kernel.put_in(["pleroma", "conversation_id"], nil) + + assert real_status == fake_status + end + + test "posting a status with OGP link preview", %{conn: conn} do + Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end) + Config.put([:rich_media, :enabled], true) + + conn = + conn + |> post("/api/v1/statuses", %{ + "status" => "https://example.com/ogp" + }) + + assert %{"id" => id, "card" => %{"title" => "The Rock"}} = json_response(conn, 200) + assert Activity.get_by_id(id) + end + + test "posting a direct status", %{conn: conn} do + user2 = insert(:user) + content = "direct cofe @#{user2.nickname}" + + conn = + conn + |> post("api/v1/statuses", %{"status" => content, "visibility" => "direct"}) + + assert %{"id" => id} = response = json_response(conn, 200) + assert response["visibility"] == "direct" + assert response["pleroma"]["direct_conversation_id"] + assert activity = Activity.get_by_id(id) + assert activity.recipients == [user2.ap_id, conn.assigns[:user].ap_id] + assert activity.data["to"] == [user2.ap_id] + assert activity.data["cc"] == [] + end + end + + describe "posting polls" do + test "posting a poll", %{conn: conn} do + user = insert(:user) + time = NaiveDateTime.utc_now() + + conn = + conn + |> assign(:user, user) + |> post("/api/v1/statuses", %{ + "status" => "Who is the #bestgrill?", + "poll" => %{"options" => ["Rei", "Asuka", "Misato"], "expires_in" => 420} + }) + + response = json_response(conn, 200) + + assert Enum.all?(response["poll"]["options"], fn %{"title" => title} -> + title in ["Rei", "Asuka", "Misato"] + end) + + assert NaiveDateTime.diff(NaiveDateTime.from_iso8601!(response["poll"]["expires_at"]), time) in 420..430 + refute response["poll"]["expred"] + end + + test "option limit is enforced", %{conn: conn} do + user = insert(:user) + limit = Config.get([:instance, :poll_limits, :max_options]) + + conn = + conn + |> assign(:user, user) + |> post("/api/v1/statuses", %{ + "status" => "desu~", + "poll" => %{"options" => Enum.map(0..limit, fn _ -> "desu" end), "expires_in" => 1} + }) + + %{"error" => error} = json_response(conn, 422) + assert error == "Poll can't contain more than #{limit} options" + end + + test "option character limit is enforced", %{conn: conn} do + user = insert(:user) + limit = Config.get([:instance, :poll_limits, :max_option_chars]) + + conn = + conn + |> assign(:user, user) + |> post("/api/v1/statuses", %{ + "status" => "...", + "poll" => %{ + "options" => [Enum.reduce(0..limit, "", fn _, acc -> acc <> "." end)], + "expires_in" => 1 + } + }) + + %{"error" => error} = json_response(conn, 422) + assert error == "Poll options cannot be longer than #{limit} characters each" + end + + test "minimal date limit is enforced", %{conn: conn} do + user = insert(:user) + limit = Config.get([:instance, :poll_limits, :min_expiration]) + + conn = + conn + |> assign(:user, user) + |> post("/api/v1/statuses", %{ + "status" => "imagine arbitrary limits", + "poll" => %{ + "options" => ["this post was made by pleroma gang"], + "expires_in" => limit - 1 + } + }) + + %{"error" => error} = json_response(conn, 422) + assert error == "Expiration date is too soon" + end + + test "maximum date limit is enforced", %{conn: conn} do + user = insert(:user) + limit = Config.get([:instance, :poll_limits, :max_expiration]) + + conn = + conn + |> assign(:user, user) + |> post("/api/v1/statuses", %{ + "status" => "imagine arbitrary limits", + "poll" => %{ + "options" => ["this post was made by pleroma gang"], + "expires_in" => limit + 1 + } + }) + + %{"error" => error} = json_response(conn, 422) + assert error == "Expiration date is too far in the future" + end + end + + test "get a status", %{conn: conn} do + activity = insert(:note_activity) + + conn = + conn + |> get("/api/v1/statuses/#{activity.id}") + + assert %{"id" => id} = json_response(conn, 200) + assert id == to_string(activity.id) + end + + test "get statuses by IDs", %{conn: conn} do + %{id: id1} = insert(:note_activity) + %{id: id2} = insert(:note_activity) + + query_string = "ids[]=#{id1}&ids[]=#{id2}" + conn = get(conn, "/api/v1/statuses/?#{query_string}") + + assert [%{"id" => ^id1}, %{"id" => ^id2}] = Enum.sort_by(json_response(conn, :ok), & &1["id"]) + end + + describe "deleting a status" do + test "when you created it", %{conn: conn} do + activity = insert(:note_activity) + author = User.get_cached_by_ap_id(activity.data["actor"]) + + conn = + conn + |> assign(:user, author) + |> delete("/api/v1/statuses/#{activity.id}") + + assert %{} = json_response(conn, 200) + + refute Activity.get_by_id(activity.id) + end + + test "when you didn't create it", %{conn: conn} do + activity = insert(:note_activity) + user = insert(:user) + + conn = + conn + |> assign(:user, user) + |> delete("/api/v1/statuses/#{activity.id}") + + assert %{"error" => _} = json_response(conn, 403) + + assert Activity.get_by_id(activity.id) == activity + end + + test "when you're an admin or moderator", %{conn: conn} do + activity1 = insert(:note_activity) + activity2 = insert(:note_activity) + admin = insert(:user, info: %{is_admin: true}) + moderator = insert(:user, info: %{is_moderator: true}) + + res_conn = + conn + |> assign(:user, admin) + |> delete("/api/v1/statuses/#{activity1.id}") + + assert %{} = json_response(res_conn, 200) + + res_conn = + conn + |> assign(:user, moderator) + |> delete("/api/v1/statuses/#{activity2.id}") + + assert %{} = json_response(res_conn, 200) + + refute Activity.get_by_id(activity1.id) + refute Activity.get_by_id(activity2.id) + end + end + + describe "reblogging" do + test "reblogs and returns the reblogged status", %{conn: conn} do + activity = insert(:note_activity) + user = insert(:user) + + conn = + conn + |> assign(:user, user) + |> post("/api/v1/statuses/#{activity.id}/reblog") + + assert %{ + "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}, + "reblogged" => true + } = json_response(conn, 200) + + assert to_string(activity.id) == id + end + + test "reblogged status for another user", %{conn: conn} do + activity = insert(:note_activity) + user1 = insert(:user) + user2 = insert(:user) + user3 = insert(:user) + CommonAPI.favorite(activity.id, user2) + {:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id) + {:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1) + {:ok, _, _object} = CommonAPI.repeat(activity.id, user2) + + conn_res = + conn + |> assign(:user, user3) + |> get("/api/v1/statuses/#{reblog_activity1.id}") + + assert %{ + "reblog" => %{"id" => id, "reblogged" => false, "reblogs_count" => 2}, + "reblogged" => false, + "favourited" => false, + "bookmarked" => false + } = json_response(conn_res, 200) + + conn_res = + conn + |> assign(:user, user2) + |> get("/api/v1/statuses/#{reblog_activity1.id}") + + assert %{ + "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 2}, + "reblogged" => true, + "favourited" => true, + "bookmarked" => true + } = json_response(conn_res, 200) + + assert to_string(activity.id) == id + end + + test "returns 400 error when activity is not exist", %{conn: conn} do + user = insert(:user) + + conn = + conn + |> assign(:user, user) + |> post("/api/v1/statuses/foo/reblog") + + assert json_response(conn, 400) == %{"error" => "Could not repeat"} + end + end + + describe "unreblogging" do + test "unreblogs and returns the unreblogged status", %{conn: conn} do + activity = insert(:note_activity) + user = insert(:user) + + {:ok, _, _} = CommonAPI.repeat(activity.id, user) + + conn = + conn + |> assign(:user, user) + |> post("/api/v1/statuses/#{activity.id}/unreblog") + + assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200) + + assert to_string(activity.id) == id + end + + test "returns 400 error when activity is not exist", %{conn: conn} do + user = insert(:user) + + conn = + conn + |> assign(:user, user) + |> post("/api/v1/statuses/foo/unreblog") + + assert json_response(conn, 400) == %{"error" => "Could not unrepeat"} + end + end + + describe "favoriting" do + test "favs a status and returns it", %{conn: conn} do + activity = insert(:note_activity) + user = insert(:user) + + conn = + conn + |> assign(:user, user) + |> post("/api/v1/statuses/#{activity.id}/favourite") + + assert %{"id" => id, "favourites_count" => 1, "favourited" => true} = + json_response(conn, 200) + + assert to_string(activity.id) == id + end + + test "returns 400 error for a wrong id", %{conn: conn} do + user = insert(:user) + + conn = + conn + |> assign(:user, user) + |> post("/api/v1/statuses/1/favourite") + + assert json_response(conn, 400) == %{"error" => "Could not favorite"} + end + end + + describe "unfavoriting" do + test "unfavorites a status and returns it", %{conn: conn} do + activity = insert(:note_activity) + user = insert(:user) + + {:ok, _, _} = CommonAPI.favorite(activity.id, user) + + conn = + conn + |> assign(:user, user) + |> post("/api/v1/statuses/#{activity.id}/unfavourite") + + assert %{"id" => id, "favourites_count" => 0, "favourited" => false} = + json_response(conn, 200) + + assert to_string(activity.id) == id + end + + test "returns 400 error for a wrong id", %{conn: conn} do + user = insert(:user) + + conn = + conn + |> assign(:user, user) + |> post("/api/v1/statuses/1/unfavourite") + + assert json_response(conn, 400) == %{"error" => "Could not unfavorite"} + end + end + + describe "pinned statuses" do + setup do + user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"}) + + [user: user, activity: activity] + end + + clear_config([:instance, :max_pinned_statuses]) do + Config.put([:instance, :max_pinned_statuses], 1) + end + + test "pin status", %{conn: conn, user: user, activity: activity} do + id_str = to_string(activity.id) + + assert %{"id" => ^id_str, "pinned" => true} = + conn + |> assign(:user, user) + |> post("/api/v1/statuses/#{activity.id}/pin") + |> json_response(200) + + assert [%{"id" => ^id_str, "pinned" => true}] = + conn + |> assign(:user, user) + |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true") + |> json_response(200) + end + + test "/pin: returns 400 error when activity is not public", %{conn: conn, user: user} do + {:ok, dm} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"}) + + conn = + conn + |> assign(:user, user) + |> post("/api/v1/statuses/#{dm.id}/pin") + + assert json_response(conn, 400) == %{"error" => "Could not pin"} + end + + test "unpin status", %{conn: conn, user: user, activity: activity} do + {:ok, _} = CommonAPI.pin(activity.id, user) + + id_str = to_string(activity.id) + user = refresh_record(user) + + assert %{"id" => ^id_str, "pinned" => false} = + conn + |> assign(:user, user) + |> post("/api/v1/statuses/#{activity.id}/unpin") + |> json_response(200) + + assert [] = + conn + |> assign(:user, user) + |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true") + |> json_response(200) + end + + test "/unpin: returns 400 error when activity is not exist", %{conn: conn, user: user} do + conn = + conn + |> assign(:user, user) + |> post("/api/v1/statuses/1/unpin") + + assert json_response(conn, 400) == %{"error" => "Could not unpin"} + end + + test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do + {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"}) + + id_str_one = to_string(activity_one.id) + + assert %{"id" => ^id_str_one, "pinned" => true} = + conn + |> assign(:user, user) + |> post("/api/v1/statuses/#{id_str_one}/pin") + |> json_response(200) + + user = refresh_record(user) + + assert %{"error" => "You have already pinned the maximum number of statuses"} = + conn + |> assign(:user, user) + |> post("/api/v1/statuses/#{activity_two.id}/pin") + |> json_response(400) + end + end + + describe "cards" do + setup do + Config.put([:rich_media, :enabled], true) + + user = insert(:user) + %{user: user} + end + + test "returns rich-media card", %{conn: conn, user: user} do + Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end) + + {:ok, activity} = CommonAPI.post(user, %{"status" => "https://example.com/ogp"}) + + card_data = %{ + "image" => "http://ia.media-imdb.com/images/rock.jpg", + "provider_name" => "example.com", + "provider_url" => "https://example.com", + "title" => "The Rock", + "type" => "link", + "url" => "https://example.com/ogp", + "description" => + "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.", + "pleroma" => %{ + "opengraph" => %{ + "image" => "http://ia.media-imdb.com/images/rock.jpg", + "title" => "The Rock", + "type" => "video.movie", + "url" => "https://example.com/ogp", + "description" => + "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer." + } + } + } + + response = + conn + |> get("/api/v1/statuses/#{activity.id}/card") + |> json_response(200) + + assert response == card_data + + # works with private posts + {:ok, activity} = + CommonAPI.post(user, %{"status" => "https://example.com/ogp", "visibility" => "direct"}) + + response_two = + conn + |> assign(:user, user) + |> get("/api/v1/statuses/#{activity.id}/card") + |> json_response(200) + + assert response_two == card_data + end + + test "replaces missing description with an empty string", %{conn: conn, user: user} do + Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end) + + {:ok, activity} = + CommonAPI.post(user, %{"status" => "https://example.com/ogp-missing-data"}) + + response = + conn + |> get("/api/v1/statuses/#{activity.id}/card") + |> json_response(:ok) + + assert response == %{ + "type" => "link", + "title" => "Pleroma", + "description" => "", + "image" => nil, + "provider_name" => "example.com", + "provider_url" => "https://example.com", + "url" => "https://example.com/ogp-missing-data", + "pleroma" => %{ + "opengraph" => %{ + "title" => "Pleroma", + "type" => "website", + "url" => "https://example.com/ogp-missing-data" + } + } + } + end + end + + test "bookmarks" do + user = insert(:user) + for_user = insert(:user) + + {:ok, activity1} = + CommonAPI.post(user, %{ + "status" => "heweoo?" + }) + + {:ok, activity2} = + CommonAPI.post(user, %{ + "status" => "heweoo!" + }) + + response1 = + build_conn() + |> assign(:user, for_user) + |> post("/api/v1/statuses/#{activity1.id}/bookmark") + + assert json_response(response1, 200)["bookmarked"] == true + + response2 = + build_conn() + |> assign(:user, for_user) + |> post("/api/v1/statuses/#{activity2.id}/bookmark") + + assert json_response(response2, 200)["bookmarked"] == true + + bookmarks = + build_conn() + |> assign(:user, for_user) + |> get("/api/v1/bookmarks") + + assert [json_response(response2, 200), json_response(response1, 200)] == + json_response(bookmarks, 200) + + response1 = + build_conn() + |> assign(:user, for_user) + |> post("/api/v1/statuses/#{activity1.id}/unbookmark") + + assert json_response(response1, 200)["bookmarked"] == false + + bookmarks = + build_conn() + |> assign(:user, for_user) + |> get("/api/v1/bookmarks") + + assert [json_response(response2, 200)] == json_response(bookmarks, 200) + end + + describe "conversation muting" do + setup do + post_user = insert(:user) + user = insert(:user) + + {:ok, activity} = CommonAPI.post(post_user, %{"status" => "HIE"}) + + [user: user, activity: activity] + end + + test "mute conversation", %{conn: conn, user: user, activity: activity} do + id_str = to_string(activity.id) + + assert %{"id" => ^id_str, "muted" => true} = + conn + |> assign(:user, user) + |> post("/api/v1/statuses/#{activity.id}/mute") + |> json_response(200) + end + + test "cannot mute already muted conversation", %{conn: conn, user: user, activity: activity} do + {:ok, _} = CommonAPI.add_mute(user, activity) + + conn = + conn + |> assign(:user, user) + |> post("/api/v1/statuses/#{activity.id}/mute") + + assert json_response(conn, 400) == %{"error" => "conversation is already muted"} + end + + test "unmute conversation", %{conn: conn, user: user, activity: activity} do + {:ok, _} = CommonAPI.add_mute(user, activity) + + id_str = to_string(activity.id) + user = refresh_record(user) + + assert %{"id" => ^id_str, "muted" => false} = + conn + |> assign(:user, user) + |> post("/api/v1/statuses/#{activity.id}/unmute") + |> json_response(200) + end + end + + test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do + user1 = insert(:user) + user2 = insert(:user) + user3 = insert(:user) + + {:ok, replied_to} = CommonAPI.post(user1, %{"status" => "cofe"}) + + # Reply to status from another user + conn1 = + conn + |> assign(:user, user2) + |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id}) + + assert %{"content" => "xD", "id" => id} = json_response(conn1, 200) + + activity = Activity.get_by_id_with_object(id) + + assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"] + assert Activity.get_in_reply_to_activity(activity).id == replied_to.id + + # Reblog from the third user + conn2 = + conn + |> assign(:user, user3) + |> post("/api/v1/statuses/#{activity.id}/reblog") + + assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} = + json_response(conn2, 200) + + assert to_string(activity.id) == id + + # Getting third user status + conn3 = + conn + |> assign(:user, user3) + |> get("api/v1/timelines/home") + + [reblogged_activity] = json_response(conn3, 200) + + assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id + + replied_to_user = User.get_by_ap_id(replied_to.data["actor"]) + assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id + end + + describe "GET /api/v1/statuses/:id/favourited_by" do + setup do + user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{"status" => "test"}) + + conn = + build_conn() + |> assign(:user, user) + + [conn: conn, activity: activity, user: user] + end + + test "returns users who have favorited the status", %{conn: conn, activity: activity} do + other_user = insert(:user) + {:ok, _, _} = CommonAPI.favorite(activity.id, other_user) + + response = + conn + |> get("/api/v1/statuses/#{activity.id}/favourited_by") + |> json_response(:ok) + + [%{"id" => id}] = response + + assert id == other_user.id + end + + test "returns empty array when status has not been favorited yet", %{ + conn: conn, + activity: activity + } do + response = + conn + |> get("/api/v1/statuses/#{activity.id}/favourited_by") + |> json_response(:ok) + + assert Enum.empty?(response) + end + + test "does not return users who have favorited the status but are blocked", %{ + conn: %{assigns: %{user: user}} = conn, + activity: activity + } do + other_user = insert(:user) + {:ok, user} = User.block(user, other_user) + + {:ok, _, _} = CommonAPI.favorite(activity.id, other_user) + + response = + conn + |> assign(:user, user) + |> get("/api/v1/statuses/#{activity.id}/favourited_by") + |> json_response(:ok) + + assert Enum.empty?(response) + end + + test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do + other_user = insert(:user) + {:ok, _, _} = CommonAPI.favorite(activity.id, other_user) + + response = + conn + |> assign(:user, nil) + |> get("/api/v1/statuses/#{activity.id}/favourited_by") + |> json_response(:ok) + + [%{"id" => id}] = response + assert id == other_user.id + end + + test "requires authentification for private posts", %{conn: conn, user: user} do + other_user = insert(:user) + + {:ok, activity} = + CommonAPI.post(user, %{ + "status" => "@#{other_user.nickname} wanna get some #cofe together?", + "visibility" => "direct" + }) + + {:ok, _, _} = CommonAPI.favorite(activity.id, other_user) + + conn + |> assign(:user, nil) + |> get("/api/v1/statuses/#{activity.id}/favourited_by") + |> json_response(404) + + response = + build_conn() + |> assign(:user, other_user) + |> get("/api/v1/statuses/#{activity.id}/favourited_by") + |> json_response(200) + + [%{"id" => id}] = response + assert id == other_user.id + end + end + + describe "GET /api/v1/statuses/:id/reblogged_by" do + setup do + user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{"status" => "test"}) + + conn = + build_conn() + |> assign(:user, user) + + [conn: conn, activity: activity, user: user] + end + + test "returns users who have reblogged the status", %{conn: conn, activity: activity} do + other_user = insert(:user) + {:ok, _, _} = CommonAPI.repeat(activity.id, other_user) + + response = + conn + |> get("/api/v1/statuses/#{activity.id}/reblogged_by") + |> json_response(:ok) + + [%{"id" => id}] = response + + assert id == other_user.id + end + + test "returns empty array when status has not been reblogged yet", %{ + conn: conn, + activity: activity + } do + response = + conn + |> get("/api/v1/statuses/#{activity.id}/reblogged_by") + |> json_response(:ok) + + assert Enum.empty?(response) + end + + test "does not return users who have reblogged the status but are blocked", %{ + conn: %{assigns: %{user: user}} = conn, + activity: activity + } do + other_user = insert(:user) + {:ok, user} = User.block(user, other_user) + + {:ok, _, _} = CommonAPI.repeat(activity.id, other_user) + + response = + conn + |> assign(:user, user) + |> get("/api/v1/statuses/#{activity.id}/reblogged_by") + |> json_response(:ok) + + assert Enum.empty?(response) + end + + test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do + other_user = insert(:user) + {:ok, _, _} = CommonAPI.repeat(activity.id, other_user) + + response = + conn + |> assign(:user, nil) + |> get("/api/v1/statuses/#{activity.id}/reblogged_by") + |> json_response(:ok) + + [%{"id" => id}] = response + assert id == other_user.id + end + + test "requires authentification for private posts", %{conn: conn, user: user} do + other_user = insert(:user) + + {:ok, activity} = + CommonAPI.post(user, %{ + "status" => "@#{other_user.nickname} wanna get some #cofe together?", + "visibility" => "direct" + }) + + conn + |> assign(:user, nil) + |> get("/api/v1/statuses/#{activity.id}/reblogged_by") + |> json_response(404) + + response = + build_conn() + |> assign(:user, other_user) + |> get("/api/v1/statuses/#{activity.id}/reblogged_by") + |> json_response(200) + + assert [] == response + end + end +end diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs index 7f7a89516..6435ad7a9 100644 --- a/test/web/mastodon_api/mastodon_api_controller_test.exs +++ b/test/web/mastodon_api/mastodon_api_controller_test.exs @@ -7,7 +7,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do alias Ecto.Changeset alias Pleroma.Activity - alias Pleroma.ActivityExpiration alias Pleroma.Config alias Pleroma.Notification alias Pleroma.Object @@ -37,312 +36,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do clear_config([:instance, :public]) clear_config([:rich_media, :enabled]) - describe "posting statuses" do - setup do - user = insert(:user) - - conn = - build_conn() - |> assign(:user, user) - - [conn: conn] - end - - test "posting a status", %{conn: conn} do - idempotency_key = "Pikachu rocks!" - - conn_one = - conn - |> put_req_header("idempotency-key", idempotency_key) - |> post("/api/v1/statuses", %{ - "status" => "cofe", - "spoiler_text" => "2hu", - "sensitive" => "false" - }) - - {:ok, ttl} = Cachex.ttl(:idempotency_cache, idempotency_key) - # Six hours - assert ttl > :timer.seconds(6 * 60 * 60 - 1) - - assert %{"content" => "cofe", "id" => id, "spoiler_text" => "2hu", "sensitive" => false} = - json_response(conn_one, 200) - - assert Activity.get_by_id(id) - - conn_two = - conn - |> put_req_header("idempotency-key", idempotency_key) - |> post("/api/v1/statuses", %{ - "status" => "cofe", - "spoiler_text" => "2hu", - "sensitive" => "false" - }) - - assert %{"id" => second_id} = json_response(conn_two, 200) - assert id == second_id - - conn_three = - conn - |> post("/api/v1/statuses", %{ - "status" => "cofe", - "spoiler_text" => "2hu", - "sensitive" => "false" - }) - - assert %{"id" => third_id} = json_response(conn_three, 200) - refute id == third_id - - # An activity that will expire: - # 2 hours - expires_in = 120 * 60 - - conn_four = - conn - |> post("api/v1/statuses", %{ - "status" => "oolong", - "expires_in" => expires_in - }) - - assert fourth_response = %{"id" => fourth_id} = json_response(conn_four, 200) - assert activity = Activity.get_by_id(fourth_id) - assert expiration = ActivityExpiration.get_by_activity_id(fourth_id) - - estimated_expires_at = - NaiveDateTime.utc_now() - |> NaiveDateTime.add(expires_in) - |> NaiveDateTime.truncate(:second) - - # This assert will fail if the test takes longer than a minute. I sure hope it never does: - assert abs(NaiveDateTime.diff(expiration.scheduled_at, estimated_expires_at, :second)) < 60 - - assert fourth_response["pleroma"]["expires_at"] == - NaiveDateTime.to_iso8601(expiration.scheduled_at) - end - - test "replying to a status", %{conn: conn} do - user = insert(:user) - {:ok, replied_to} = CommonAPI.post(user, %{"status" => "cofe"}) - - conn = - conn - |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id}) - - assert %{"content" => "xD", "id" => id} = json_response(conn, 200) - - activity = Activity.get_by_id(id) - - assert activity.data["context"] == replied_to.data["context"] - assert Activity.get_in_reply_to_activity(activity).id == replied_to.id - end - - test "replying to a direct message with visibility other than direct", %{conn: conn} do - user = insert(:user) - {:ok, replied_to} = CommonAPI.post(user, %{"status" => "suya..", "visibility" => "direct"}) - - Enum.each(["public", "private", "unlisted"], fn visibility -> - conn = - conn - |> post("/api/v1/statuses", %{ - "status" => "@#{user.nickname} hey", - "in_reply_to_id" => replied_to.id, - "visibility" => visibility - }) - - assert json_response(conn, 422) == %{"error" => "The message visibility must be direct"} - end) - end - - test "posting a status with an invalid in_reply_to_id", %{conn: conn} do - conn = - conn - |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""}) - - assert %{"content" => "xD", "id" => id} = json_response(conn, 200) - assert Activity.get_by_id(id) - end - - test "posting a sensitive status", %{conn: conn} do - conn = - conn - |> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true}) - - assert %{"content" => "cofe", "id" => id, "sensitive" => true} = json_response(conn, 200) - assert Activity.get_by_id(id) - end - - test "posting a fake status", %{conn: conn} do - real_conn = - conn - |> post("/api/v1/statuses", %{ - "status" => - "\"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" - }) - - real_status = json_response(real_conn, 200) - - assert real_status - assert Object.get_by_ap_id(real_status["uri"]) - - real_status = - real_status - |> Map.put("id", nil) - |> Map.put("url", nil) - |> Map.put("uri", nil) - |> Map.put("created_at", nil) - |> Kernel.put_in(["pleroma", "conversation_id"], nil) - - fake_conn = - conn - |> post("/api/v1/statuses", %{ - "status" => - "\"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", - "preview" => true - }) - - fake_status = json_response(fake_conn, 200) - - assert fake_status - refute Object.get_by_ap_id(fake_status["uri"]) - - fake_status = - fake_status - |> Map.put("id", nil) - |> Map.put("url", nil) - |> Map.put("uri", nil) - |> Map.put("created_at", nil) - |> Kernel.put_in(["pleroma", "conversation_id"], nil) - - assert real_status == fake_status - end - - test "posting a status with OGP link preview", %{conn: conn} do - Config.put([:rich_media, :enabled], true) - - conn = - conn - |> post("/api/v1/statuses", %{ - "status" => "https://example.com/ogp" - }) - - assert %{"id" => id, "card" => %{"title" => "The Rock"}} = json_response(conn, 200) - assert Activity.get_by_id(id) - end - - test "posting a direct status", %{conn: conn} do - user2 = insert(:user) - content = "direct cofe @#{user2.nickname}" - - conn = - conn - |> post("api/v1/statuses", %{"status" => content, "visibility" => "direct"}) - - assert %{"id" => id} = response = json_response(conn, 200) - assert response["visibility"] == "direct" - assert response["pleroma"]["direct_conversation_id"] - assert activity = Activity.get_by_id(id) - assert activity.recipients == [user2.ap_id, conn.assigns[:user].ap_id] - assert activity.data["to"] == [user2.ap_id] - assert activity.data["cc"] == [] - end - end - - describe "posting polls" do - test "posting a poll", %{conn: conn} do - user = insert(:user) - time = NaiveDateTime.utc_now() - - conn = - conn - |> assign(:user, user) - |> post("/api/v1/statuses", %{ - "status" => "Who is the #bestgrill?", - "poll" => %{"options" => ["Rei", "Asuka", "Misato"], "expires_in" => 420} - }) - - response = json_response(conn, 200) - - assert Enum.all?(response["poll"]["options"], fn %{"title" => title} -> - title in ["Rei", "Asuka", "Misato"] - end) - - assert NaiveDateTime.diff(NaiveDateTime.from_iso8601!(response["poll"]["expires_at"]), time) in 420..430 - refute response["poll"]["expred"] - end - - test "option limit is enforced", %{conn: conn} do - user = insert(:user) - limit = Config.get([:instance, :poll_limits, :max_options]) - - conn = - conn - |> assign(:user, user) - |> post("/api/v1/statuses", %{ - "status" => "desu~", - "poll" => %{"options" => Enum.map(0..limit, fn _ -> "desu" end), "expires_in" => 1} - }) - - %{"error" => error} = json_response(conn, 422) - assert error == "Poll can't contain more than #{limit} options" - end - - test "option character limit is enforced", %{conn: conn} do - user = insert(:user) - limit = Config.get([:instance, :poll_limits, :max_option_chars]) - - conn = - conn - |> assign(:user, user) - |> post("/api/v1/statuses", %{ - "status" => "...", - "poll" => %{ - "options" => [Enum.reduce(0..limit, "", fn _, acc -> acc <> "." end)], - "expires_in" => 1 - } - }) - - %{"error" => error} = json_response(conn, 422) - assert error == "Poll options cannot be longer than #{limit} characters each" - end - - test "minimal date limit is enforced", %{conn: conn} do - user = insert(:user) - limit = Config.get([:instance, :poll_limits, :min_expiration]) - - conn = - conn - |> assign(:user, user) - |> post("/api/v1/statuses", %{ - "status" => "imagine arbitrary limits", - "poll" => %{ - "options" => ["this post was made by pleroma gang"], - "expires_in" => limit - 1 - } - }) - - %{"error" => error} = json_response(conn, 422) - assert error == "Expiration date is too soon" - end - - test "maximum date limit is enforced", %{conn: conn} do - user = insert(:user) - limit = Config.get([:instance, :poll_limits, :max_expiration]) - - conn = - conn - |> assign(:user, user) - |> post("/api/v1/statuses", %{ - "status" => "imagine arbitrary limits", - "poll" => %{ - "options" => ["this post was made by pleroma gang"], - "expires_in" => limit + 1 - } - }) - - %{"error" => error} = json_response(conn, 422) - assert error == "Expiration date is too far in the future" - end - end - test "Conversations", %{conn: conn} do user_one = insert(:user) user_two = insert(:user) @@ -575,81 +268,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do assert expected == json_response(conn, 200) end - test "get a status", %{conn: conn} do - activity = insert(:note_activity) - - conn = - conn - |> get("/api/v1/statuses/#{activity.id}") - - assert %{"id" => id} = json_response(conn, 200) - assert id == to_string(activity.id) - end - - test "get statuses by IDs", %{conn: conn} do - %{id: id1} = insert(:note_activity) - %{id: id2} = insert(:note_activity) - - query_string = "ids[]=#{id1}&ids[]=#{id2}" - conn = get(conn, "/api/v1/statuses/?#{query_string}") - - assert [%{"id" => ^id1}, %{"id" => ^id2}] = Enum.sort_by(json_response(conn, :ok), & &1["id"]) - end - - describe "deleting a status" do - test "when you created it", %{conn: conn} do - activity = insert(:note_activity) - author = User.get_cached_by_ap_id(activity.data["actor"]) - - conn = - conn - |> assign(:user, author) - |> delete("/api/v1/statuses/#{activity.id}") - - assert %{} = json_response(conn, 200) - - refute Activity.get_by_id(activity.id) - end - - test "when you didn't create it", %{conn: conn} do - activity = insert(:note_activity) - user = insert(:user) - - conn = - conn - |> assign(:user, user) - |> delete("/api/v1/statuses/#{activity.id}") - - assert %{"error" => _} = json_response(conn, 403) - - assert Activity.get_by_id(activity.id) == activity - end - - test "when you're an admin or moderator", %{conn: conn} do - activity1 = insert(:note_activity) - activity2 = insert(:note_activity) - admin = insert(:user, info: %{is_admin: true}) - moderator = insert(:user, info: %{is_moderator: true}) - - res_conn = - conn - |> assign(:user, admin) - |> delete("/api/v1/statuses/#{activity1.id}") - - assert %{} = json_response(res_conn, 200) - - res_conn = - conn - |> assign(:user, moderator) - |> delete("/api/v1/statuses/#{activity2.id}") - - assert %{} = json_response(res_conn, 200) - - refute Activity.get_by_id(activity1.id) - refute Activity.get_by_id(activity2.id) - end - end - describe "filters" do test "creating a filter", %{conn: conn} do user = insert(:user) @@ -778,160 +396,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do end end - describe "reblogging" do - test "reblogs and returns the reblogged status", %{conn: conn} do - activity = insert(:note_activity) - user = insert(:user) - - conn = - conn - |> assign(:user, user) - |> post("/api/v1/statuses/#{activity.id}/reblog") - - assert %{ - "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}, - "reblogged" => true - } = json_response(conn, 200) - - assert to_string(activity.id) == id - end - - test "reblogged status for another user", %{conn: conn} do - activity = insert(:note_activity) - user1 = insert(:user) - user2 = insert(:user) - user3 = insert(:user) - CommonAPI.favorite(activity.id, user2) - {:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id) - {:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1) - {:ok, _, _object} = CommonAPI.repeat(activity.id, user2) - - conn_res = - conn - |> assign(:user, user3) - |> get("/api/v1/statuses/#{reblog_activity1.id}") - - assert %{ - "reblog" => %{"id" => id, "reblogged" => false, "reblogs_count" => 2}, - "reblogged" => false, - "favourited" => false, - "bookmarked" => false - } = json_response(conn_res, 200) - - conn_res = - conn - |> assign(:user, user2) - |> get("/api/v1/statuses/#{reblog_activity1.id}") - - assert %{ - "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 2}, - "reblogged" => true, - "favourited" => true, - "bookmarked" => true - } = json_response(conn_res, 200) - - assert to_string(activity.id) == id - end - - test "returns 400 error when activity is not exist", %{conn: conn} do - user = insert(:user) - - conn = - conn - |> assign(:user, user) - |> post("/api/v1/statuses/foo/reblog") - - assert json_response(conn, 400) == %{"error" => "Could not repeat"} - end - end - - describe "unreblogging" do - test "unreblogs and returns the unreblogged status", %{conn: conn} do - activity = insert(:note_activity) - user = insert(:user) - - {:ok, _, _} = CommonAPI.repeat(activity.id, user) - - conn = - conn - |> assign(:user, user) - |> post("/api/v1/statuses/#{activity.id}/unreblog") - - assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200) - - assert to_string(activity.id) == id - end - - test "returns 400 error when activity is not exist", %{conn: conn} do - user = insert(:user) - - conn = - conn - |> assign(:user, user) - |> post("/api/v1/statuses/foo/unreblog") - - assert json_response(conn, 400) == %{"error" => "Could not unrepeat"} - end - end - - describe "favoriting" do - test "favs a status and returns it", %{conn: conn} do - activity = insert(:note_activity) - user = insert(:user) - - conn = - conn - |> assign(:user, user) - |> post("/api/v1/statuses/#{activity.id}/favourite") - - assert %{"id" => id, "favourites_count" => 1, "favourited" => true} = - json_response(conn, 200) - - assert to_string(activity.id) == id - end - - test "returns 400 error for a wrong id", %{conn: conn} do - user = insert(:user) - - conn = - conn - |> assign(:user, user) - |> post("/api/v1/statuses/1/favourite") - - assert json_response(conn, 400) == %{"error" => "Could not favorite"} - end - end - - describe "unfavoriting" do - test "unfavorites a status and returns it", %{conn: conn} do - activity = insert(:note_activity) - user = insert(:user) - - {:ok, _, _} = CommonAPI.favorite(activity.id, user) - - conn = - conn - |> assign(:user, user) - |> post("/api/v1/statuses/#{activity.id}/unfavourite") - - assert %{"id" => id, "favourites_count" => 0, "favourited" => false} = - json_response(conn, 200) - - assert to_string(activity.id) == id - end - - test "returns 400 error for a wrong id", %{conn: conn} do - user = insert(:user) - - conn = - conn - |> assign(:user, user) - |> post("/api/v1/statuses/1/unfavourite") - - assert json_response(conn, 400) == %{"error" => "Could not unfavorite"} - end - end - describe "user timelines" do test "gets a users statuses", %{conn: conn} do user_one = insert(:user) @@ -2025,346 +1489,91 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do "thumbnail" => _, "languages" => _, "registrations" => _, - "poll_limits" => _ - } = result - - assert email == from_config_email - end - - test "get instance stats", %{conn: conn} do - user = insert(:user, %{local: true}) - - user2 = insert(:user, %{local: true}) - {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated) - - insert(:user, %{local: false, nickname: "u@peer1.com"}) - insert(:user, %{local: false, nickname: "u@peer2.com"}) - - {:ok, _} = CommonAPI.post(user, %{"status" => "cofe"}) - - # Stats should count users with missing or nil `info.deactivated` value - - {:ok, _user} = - user.id - |> User.get_cached_by_id() - |> User.update_info(&Changeset.change(&1, %{deactivated: nil})) - - Pleroma.Stats.force_update() - - conn = get(conn, "/api/v1/instance") - - assert result = json_response(conn, 200) - - stats = result["stats"] - - assert stats - assert stats["user_count"] == 1 - assert stats["status_count"] == 1 - assert stats["domain_count"] == 2 - end - - test "get peers", %{conn: conn} do - insert(:user, %{local: false, nickname: "u@peer1.com"}) - insert(:user, %{local: false, nickname: "u@peer2.com"}) - - Pleroma.Stats.force_update() - - conn = get(conn, "/api/v1/instance/peers") - - assert result = json_response(conn, 200) - - assert ["peer1.com", "peer2.com"] == Enum.sort(result) - end - - test "put settings", %{conn: conn} do - user = insert(:user) - - conn = - conn - |> assign(:user, user) - |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}}) - - assert _result = json_response(conn, 200) - - user = User.get_cached_by_ap_id(user.ap_id) - assert user.info.settings == %{"programming" => "socks"} - end - - describe "pinned statuses" do - setup do - user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"}) - - [user: user, activity: activity] - end - - clear_config([:instance, :max_pinned_statuses]) do - Config.put([:instance, :max_pinned_statuses], 1) - end - - test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do - {:ok, _} = CommonAPI.pin(activity.id, user) - - result = - conn - |> assign(:user, user) - |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true") - |> json_response(200) - - id_str = to_string(activity.id) - - assert [%{"id" => ^id_str, "pinned" => true}] = result - end - - test "pin status", %{conn: conn, user: user, activity: activity} do - id_str = to_string(activity.id) - - assert %{"id" => ^id_str, "pinned" => true} = - conn - |> assign(:user, user) - |> post("/api/v1/statuses/#{activity.id}/pin") - |> json_response(200) - - assert [%{"id" => ^id_str, "pinned" => true}] = - conn - |> assign(:user, user) - |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true") - |> json_response(200) - end - - test "/pin: returns 400 error when activity is not public", %{conn: conn, user: user} do - {:ok, dm} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"}) - - conn = - conn - |> assign(:user, user) - |> post("/api/v1/statuses/#{dm.id}/pin") - - assert json_response(conn, 400) == %{"error" => "Could not pin"} - end - - test "unpin status", %{conn: conn, user: user, activity: activity} do - {:ok, _} = CommonAPI.pin(activity.id, user) - - id_str = to_string(activity.id) - user = refresh_record(user) - - assert %{"id" => ^id_str, "pinned" => false} = - conn - |> assign(:user, user) - |> post("/api/v1/statuses/#{activity.id}/unpin") - |> json_response(200) - - assert [] = - conn - |> assign(:user, user) - |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true") - |> json_response(200) - end + "poll_limits" => _ + } = result - test "/unpin: returns 400 error when activity is not exist", %{conn: conn, user: user} do - conn = - conn - |> assign(:user, user) - |> post("/api/v1/statuses/1/unpin") + assert email == from_config_email + end - assert json_response(conn, 400) == %{"error" => "Could not unpin"} - end + test "get instance stats", %{conn: conn} do + user = insert(:user, %{local: true}) - test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do - {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"}) + user2 = insert(:user, %{local: true}) + {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated) - id_str_one = to_string(activity_one.id) + insert(:user, %{local: false, nickname: "u@peer1.com"}) + insert(:user, %{local: false, nickname: "u@peer2.com"}) - assert %{"id" => ^id_str_one, "pinned" => true} = - conn - |> assign(:user, user) - |> post("/api/v1/statuses/#{id_str_one}/pin") - |> json_response(200) + {:ok, _} = CommonAPI.post(user, %{"status" => "cofe"}) - user = refresh_record(user) + # Stats should count users with missing or nil `info.deactivated` value - assert %{"error" => "You have already pinned the maximum number of statuses"} = - conn - |> assign(:user, user) - |> post("/api/v1/statuses/#{activity_two.id}/pin") - |> json_response(400) - end - end + {:ok, _user} = + user.id + |> User.get_cached_by_id() + |> User.update_info(&Changeset.change(&1, %{deactivated: nil})) - describe "cards" do - setup do - Config.put([:rich_media, :enabled], true) + Pleroma.Stats.force_update() - user = insert(:user) - %{user: user} - end - - test "returns rich-media card", %{conn: conn, user: user} do - {:ok, activity} = CommonAPI.post(user, %{"status" => "https://example.com/ogp"}) - - card_data = %{ - "image" => "http://ia.media-imdb.com/images/rock.jpg", - "provider_name" => "example.com", - "provider_url" => "https://example.com", - "title" => "The Rock", - "type" => "link", - "url" => "https://example.com/ogp", - "description" => - "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.", - "pleroma" => %{ - "opengraph" => %{ - "image" => "http://ia.media-imdb.com/images/rock.jpg", - "title" => "The Rock", - "type" => "video.movie", - "url" => "https://example.com/ogp", - "description" => - "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer." - } - } - } + conn = get(conn, "/api/v1/instance") - response = - conn - |> get("/api/v1/statuses/#{activity.id}/card") - |> json_response(200) + assert result = json_response(conn, 200) - assert response == card_data + stats = result["stats"] - # works with private posts - {:ok, activity} = - CommonAPI.post(user, %{"status" => "https://example.com/ogp", "visibility" => "direct"}) + assert stats + assert stats["user_count"] == 1 + assert stats["status_count"] == 1 + assert stats["domain_count"] == 2 + end - response_two = - conn - |> assign(:user, user) - |> get("/api/v1/statuses/#{activity.id}/card") - |> json_response(200) + test "get peers", %{conn: conn} do + insert(:user, %{local: false, nickname: "u@peer1.com"}) + insert(:user, %{local: false, nickname: "u@peer2.com"}) - assert response_two == card_data - end + Pleroma.Stats.force_update() - test "replaces missing description with an empty string", %{conn: conn, user: user} do - {:ok, activity} = - CommonAPI.post(user, %{"status" => "https://example.com/ogp-missing-data"}) + conn = get(conn, "/api/v1/instance/peers") - response = - conn - |> get("/api/v1/statuses/#{activity.id}/card") - |> json_response(:ok) + assert result = json_response(conn, 200) - assert response == %{ - "type" => "link", - "title" => "Pleroma", - "description" => "", - "image" => nil, - "provider_name" => "example.com", - "provider_url" => "https://example.com", - "url" => "https://example.com/ogp-missing-data", - "pleroma" => %{ - "opengraph" => %{ - "title" => "Pleroma", - "type" => "website", - "url" => "https://example.com/ogp-missing-data" - } - } - } - end + assert ["peer1.com", "peer2.com"] == Enum.sort(result) end - test "bookmarks" do + test "put settings", %{conn: conn} do user = insert(:user) - for_user = insert(:user) - - {:ok, activity1} = - CommonAPI.post(user, %{ - "status" => "heweoo?" - }) - {:ok, activity2} = - CommonAPI.post(user, %{ - "status" => "heweoo!" - }) - - response1 = - build_conn() - |> assign(:user, for_user) - |> post("/api/v1/statuses/#{activity1.id}/bookmark") - - assert json_response(response1, 200)["bookmarked"] == true - - response2 = - build_conn() - |> assign(:user, for_user) - |> post("/api/v1/statuses/#{activity2.id}/bookmark") - - assert json_response(response2, 200)["bookmarked"] == true - - bookmarks = - build_conn() - |> assign(:user, for_user) - |> get("/api/v1/bookmarks") - - assert [json_response(response2, 200), json_response(response1, 200)] == - json_response(bookmarks, 200) - - response1 = - build_conn() - |> assign(:user, for_user) - |> post("/api/v1/statuses/#{activity1.id}/unbookmark") - - assert json_response(response1, 200)["bookmarked"] == false + conn = + conn + |> assign(:user, user) + |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}}) - bookmarks = - build_conn() - |> assign(:user, for_user) - |> get("/api/v1/bookmarks") + assert _result = json_response(conn, 200) - assert [json_response(response2, 200)] == json_response(bookmarks, 200) + user = User.get_cached_by_ap_id(user.ap_id) + assert user.info.settings == %{"programming" => "socks"} end - describe "conversation muting" do + describe "pinned statuses" do setup do - post_user = insert(:user) user = insert(:user) - - {:ok, activity} = CommonAPI.post(post_user, %{"status" => "HIE"}) + {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"}) [user: user, activity: activity] end - test "mute conversation", %{conn: conn, user: user, activity: activity} do - id_str = to_string(activity.id) - - assert %{"id" => ^id_str, "muted" => true} = - conn - |> assign(:user, user) - |> post("/api/v1/statuses/#{activity.id}/mute") - |> json_response(200) - end - - test "cannot mute already muted conversation", %{conn: conn, user: user, activity: activity} do - {:ok, _} = CommonAPI.add_mute(user, activity) + test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do + {:ok, _} = CommonAPI.pin(activity.id, user) - conn = + result = conn |> assign(:user, user) - |> post("/api/v1/statuses/#{activity.id}/mute") - - assert json_response(conn, 400) == %{"error" => "conversation is already muted"} - end - - test "unmute conversation", %{conn: conn, user: user, activity: activity} do - {:ok, _} = CommonAPI.add_mute(user, activity) + |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true") + |> json_response(200) id_str = to_string(activity.id) - user = refresh_record(user) - assert %{"id" => ^id_str, "muted" => false} = - conn - |> assign(:user, user) - |> post("/api/v1/statuses/#{activity.id}/unmute") - |> json_response(200) + assert [%{"id" => ^id_str, "pinned" => true}] = result end end @@ -2811,51 +2020,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do end end - test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do - user1 = insert(:user) - user2 = insert(:user) - user3 = insert(:user) - - {:ok, replied_to} = CommonAPI.post(user1, %{"status" => "cofe"}) - - # Reply to status from another user - conn1 = - conn - |> assign(:user, user2) - |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id}) - - assert %{"content" => "xD", "id" => id} = json_response(conn1, 200) - - activity = Activity.get_by_id_with_object(id) - - assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"] - assert Activity.get_in_reply_to_activity(activity).id == replied_to.id - - # Reblog from the third user - conn2 = - conn - |> assign(:user, user3) - |> post("/api/v1/statuses/#{activity.id}/reblog") - - assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} = - json_response(conn2, 200) - - assert to_string(activity.id) == id - - # Getting third user status - conn3 = - conn - |> assign(:user, user3) - |> get("api/v1/timelines/home") - - [reblogged_activity] = json_response(conn3, 200) - - assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id - - replied_to_user = User.get_by_ap_id(replied_to.data["actor"]) - assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id - end - describe "create account by app" do test "Account registration via Application", %{conn: conn} do conn = @@ -3135,197 +2299,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do end end - describe "GET /api/v1/statuses/:id/favourited_by" do - setup do - user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "test"}) - - conn = - build_conn() - |> assign(:user, user) - - [conn: conn, activity: activity, user: user] - end - - test "returns users who have favorited the status", %{conn: conn, activity: activity} do - other_user = insert(:user) - {:ok, _, _} = CommonAPI.favorite(activity.id, other_user) - - response = - conn - |> get("/api/v1/statuses/#{activity.id}/favourited_by") - |> json_response(:ok) - - [%{"id" => id}] = response - - assert id == other_user.id - end - - test "returns empty array when status has not been favorited yet", %{ - conn: conn, - activity: activity - } do - response = - conn - |> get("/api/v1/statuses/#{activity.id}/favourited_by") - |> json_response(:ok) - - assert Enum.empty?(response) - end - - test "does not return users who have favorited the status but are blocked", %{ - conn: %{assigns: %{user: user}} = conn, - activity: activity - } do - other_user = insert(:user) - {:ok, user} = User.block(user, other_user) - - {:ok, _, _} = CommonAPI.favorite(activity.id, other_user) - - response = - conn - |> assign(:user, user) - |> get("/api/v1/statuses/#{activity.id}/favourited_by") - |> json_response(:ok) - - assert Enum.empty?(response) - end - - test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do - other_user = insert(:user) - {:ok, _, _} = CommonAPI.favorite(activity.id, other_user) - - response = - conn - |> assign(:user, nil) - |> get("/api/v1/statuses/#{activity.id}/favourited_by") - |> json_response(:ok) - - [%{"id" => id}] = response - assert id == other_user.id - end - - test "requires authentification for private posts", %{conn: conn, user: user} do - other_user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{ - "status" => "@#{other_user.nickname} wanna get some #cofe together?", - "visibility" => "direct" - }) - - {:ok, _, _} = CommonAPI.favorite(activity.id, other_user) - - conn - |> assign(:user, nil) - |> get("/api/v1/statuses/#{activity.id}/favourited_by") - |> json_response(404) - - response = - build_conn() - |> assign(:user, other_user) - |> get("/api/v1/statuses/#{activity.id}/favourited_by") - |> json_response(200) - - [%{"id" => id}] = response - assert id == other_user.id - end - end - - describe "GET /api/v1/statuses/:id/reblogged_by" do - setup do - user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "test"}) - - conn = - build_conn() - |> assign(:user, user) - - [conn: conn, activity: activity, user: user] - end - - test "returns users who have reblogged the status", %{conn: conn, activity: activity} do - other_user = insert(:user) - {:ok, _, _} = CommonAPI.repeat(activity.id, other_user) - - response = - conn - |> get("/api/v1/statuses/#{activity.id}/reblogged_by") - |> json_response(:ok) - - [%{"id" => id}] = response - - assert id == other_user.id - end - - test "returns empty array when status has not been reblogged yet", %{ - conn: conn, - activity: activity - } do - response = - conn - |> get("/api/v1/statuses/#{activity.id}/reblogged_by") - |> json_response(:ok) - - assert Enum.empty?(response) - end - - test "does not return users who have reblogged the status but are blocked", %{ - conn: %{assigns: %{user: user}} = conn, - activity: activity - } do - other_user = insert(:user) - {:ok, user} = User.block(user, other_user) - - {:ok, _, _} = CommonAPI.repeat(activity.id, other_user) - - response = - conn - |> assign(:user, user) - |> get("/api/v1/statuses/#{activity.id}/reblogged_by") - |> json_response(:ok) - - assert Enum.empty?(response) - end - - test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do - other_user = insert(:user) - {:ok, _, _} = CommonAPI.repeat(activity.id, other_user) - - response = - conn - |> assign(:user, nil) - |> get("/api/v1/statuses/#{activity.id}/reblogged_by") - |> json_response(:ok) - - [%{"id" => id}] = response - assert id == other_user.id - end - - test "requires authentification for private posts", %{conn: conn, user: user} do - other_user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{ - "status" => "@#{other_user.nickname} wanna get some #cofe together?", - "visibility" => "direct" - }) - - conn - |> assign(:user, nil) - |> get("/api/v1/statuses/#{activity.id}/reblogged_by") - |> json_response(404) - - response = - build_conn() - |> assign(:user, other_user) - |> get("/api/v1/statuses/#{activity.id}/reblogged_by") - |> json_response(200) - - assert [] == response - end - end - describe "POST /auth/password, with valid parameters" do setup %{conn: conn} do user = insert(:user)