diff options
Diffstat (limited to 'test/web')
33 files changed, 5197 insertions, 3448 deletions
diff --git a/test/web/activity_pub/activity_pub_controller_test.exs b/test/web/activity_pub/activity_pub_controller_test.exs index 9e8e420ec..1ffa91b70 100644 --- a/test/web/activity_pub/activity_pub_controller_test.exs +++ b/test/web/activity_pub/activity_pub_controller_test.exs @@ -479,7 +479,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do          conn          |> assign(:user, user)          |> put_req_header("accept", "application/activity+json") -        |> get("/users/#{user.nickname}/inbox") +        |> get("/users/#{user.nickname}/inbox?page=true")        assert response(conn, 200) =~ note_object.data["content"]      end @@ -567,7 +567,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do        conn =          conn          |> put_req_header("accept", "application/activity+json") -        |> get("/users/#{user.nickname}/outbox") +        |> get("/users/#{user.nickname}/outbox?page=true")        assert response(conn, 200) =~ note_object.data["content"]      end @@ -579,7 +579,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do        conn =          conn          |> put_req_header("accept", "application/activity+json") -        |> get("/users/#{user.nickname}/outbox") +        |> get("/users/#{user.nickname}/outbox?page=true")        assert response(conn, 200) =~ announce_activity.data["object"]      end @@ -976,4 +976,44 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do        assert Delivery.get(object.id, other_user.id)      end    end + +  describe "Additionnal ActivityPub C2S endpoints" do +    test "/api/ap/whoami", %{conn: conn} do +      user = insert(:user) + +      conn = +        conn +        |> assign(:user, user) +        |> get("/api/ap/whoami") + +      user = User.get_cached_by_id(user.id) + +      assert UserView.render("user.json", %{user: user}) == json_response(conn, 200) +    end + +    clear_config([:media_proxy]) +    clear_config([Pleroma.Upload]) + +    test "uploadMedia", %{conn: conn} do +      user = insert(:user) + +      desc = "Description of the image" + +      image = %Plug.Upload{ +        content_type: "image/jpg", +        path: Path.absname("test/fixtures/image.jpg"), +        filename: "an_image.jpg" +      } + +      conn = +        conn +        |> assign(:user, user) +        |> post("/api/ap/upload_media", %{"file" => image, "description" => desc}) + +      assert object = json_response(conn, :created) +      assert object["name"] == desc +      assert object["type"] == "Document" +      assert object["actor"] == user.ap_id +    end +  end  end diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs index 4100108a5..a203d1d30 100644 --- a/test/web/activity_pub/activity_pub_test.exs +++ b/test/web/activity_pub/activity_pub_test.exs @@ -257,6 +257,42 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do      end    end +  describe "listen activities" do +    test "does not increase user note count" do +      user = insert(:user) + +      {:ok, activity} = +        ActivityPub.listen(%{ +          to: ["https://www.w3.org/ns/activitystreams#Public"], +          actor: user, +          context: "", +          object: %{ +            "actor" => user.ap_id, +            "to" => ["https://www.w3.org/ns/activitystreams#Public"], +            "artist" => "lain", +            "title" => "lain radio episode 1", +            "length" => 180_000, +            "type" => "Audio" +          } +        }) + +      assert activity.actor == user.ap_id + +      user = User.get_cached_by_id(user.id) +      assert user.info.note_count == 0 +    end + +    test "can be fetched into a timeline" do +      _listen_activity_1 = insert(:listen) +      _listen_activity_2 = insert(:listen) +      _listen_activity_3 = insert(:listen) + +      timeline = ActivityPub.fetch_activities([], %{"type" => ["Listen"]}) + +      assert length(timeline) == 3 +    end +  end +    describe "create activities" do      test "removes doubled 'to' recipients" do        user = insert(:user) @@ -647,6 +683,21 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do        assert last == last_expected      end +    test "paginates via offset/limit" do +      _first_activities = ActivityBuilder.insert_list(10) +      activities = ActivityBuilder.insert_list(10) +      _later_activities = ActivityBuilder.insert_list(10) +      first_expected = List.first(activities) + +      activities = +        ActivityPub.fetch_public_activities(%{"page" => "2", "page_size" => "20"}, :offset) + +      first = List.first(activities) + +      assert length(activities) == 20 +      assert first == first_expected +    end +      test "doesn't return reblogs for users for whom reblogs have been muted" do        activity = insert(:note_activity)        user = insert(:user) diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs index ebed65b7c..193d6d301 100644 --- a/test/web/activity_pub/transmogrifier_test.exs +++ b/test/web/activity_pub/transmogrifier_test.exs @@ -177,6 +177,35 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do               end)      end +    test "it works for incoming listens" do +      data = %{ +        "@context" => "https://www.w3.org/ns/activitystreams", +        "to" => ["https://www.w3.org/ns/activitystreams#Public"], +        "cc" => [], +        "type" => "Listen", +        "id" => "http://mastodon.example.org/users/admin/listens/1234/activity", +        "actor" => "http://mastodon.example.org/users/admin", +        "object" => %{ +          "type" => "Audio", +          "id" => "http://mastodon.example.org/users/admin/listens/1234", +          "attributedTo" => "http://mastodon.example.org/users/admin", +          "title" => "lain radio episode 1", +          "artist" => "lain", +          "album" => "lain radio", +          "length" => 180_000 +        } +      } + +      {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) + +      object = Object.normalize(activity) + +      assert object.data["title"] == "lain radio episode 1" +      assert object.data["artist"] == "lain" +      assert object.data["album"] == "lain radio" +      assert object.data["length"] == 180_000 +    end +      test "it rewrites Note votes to Answers and increments vote counters on question activities" do        user = insert(:user) @@ -1190,6 +1219,20 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do        assert is_nil(modified["bcc"])      end + +    test "it can handle Listen activities" do +      listen_activity = insert(:listen) + +      {:ok, modified} = Transmogrifier.prepare_outgoing(listen_activity.data) + +      assert modified["type"] == "Listen" + +      user = insert(:user) + +      {:ok, activity} = CommonAPI.listen(user, %{"title" => "lain radio episode 1"}) + +      {:ok, _modified} = Transmogrifier.prepare_outgoing(activity.data) +    end    end    describe "user upgrade" do @@ -1455,4 +1498,271 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do        refute recipient.follower_address in fixed_object["to"]      end    end + +  describe "fix_summary/1" do +    test "returns fixed object" do +      assert Transmogrifier.fix_summary(%{"summary" => nil}) == %{"summary" => ""} +      assert Transmogrifier.fix_summary(%{"summary" => "ok"}) == %{"summary" => "ok"} +      assert Transmogrifier.fix_summary(%{}) == %{"summary" => ""} +    end +  end + +  describe "fix_in_reply_to/2" do +    clear_config([:instance, :federation_incoming_replies_max_depth]) + +    setup do +      data = Poison.decode!(File.read!("test/fixtures/mastodon-post-activity.json")) +      [data: data] +    end + +    test "returns not modified object when hasn't containts inReplyTo field", %{data: data} do +      assert Transmogrifier.fix_in_reply_to(data) == data +    end + +    test "returns object with inReplyToAtomUri when denied incoming reply", %{data: data} do +      Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 0) + +      object_with_reply = +        Map.put(data["object"], "inReplyTo", "https://shitposter.club/notice/2827873") + +      modified_object = Transmogrifier.fix_in_reply_to(object_with_reply) +      assert modified_object["inReplyTo"] == "https://shitposter.club/notice/2827873" +      assert modified_object["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873" + +      object_with_reply = +        Map.put(data["object"], "inReplyTo", %{"id" => "https://shitposter.club/notice/2827873"}) + +      modified_object = Transmogrifier.fix_in_reply_to(object_with_reply) +      assert modified_object["inReplyTo"] == %{"id" => "https://shitposter.club/notice/2827873"} +      assert modified_object["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873" + +      object_with_reply = +        Map.put(data["object"], "inReplyTo", ["https://shitposter.club/notice/2827873"]) + +      modified_object = Transmogrifier.fix_in_reply_to(object_with_reply) +      assert modified_object["inReplyTo"] == ["https://shitposter.club/notice/2827873"] +      assert modified_object["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873" + +      object_with_reply = Map.put(data["object"], "inReplyTo", []) +      modified_object = Transmogrifier.fix_in_reply_to(object_with_reply) +      assert modified_object["inReplyTo"] == [] +      assert modified_object["inReplyToAtomUri"] == "" +    end + +    test "returns modified object when allowed incoming reply", %{data: data} do +      object_with_reply = +        Map.put( +          data["object"], +          "inReplyTo", +          "https://shitposter.club/notice/2827873" +        ) + +      Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 5) +      modified_object = Transmogrifier.fix_in_reply_to(object_with_reply) + +      assert modified_object["inReplyTo"] == +               "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment" + +      assert modified_object["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873" + +      assert modified_object["conversation"] == +               "tag:shitposter.club,2017-05-05:objectType=thread:nonce=3c16e9c2681f6d26" + +      assert modified_object["context"] == +               "tag:shitposter.club,2017-05-05:objectType=thread:nonce=3c16e9c2681f6d26" +    end +  end + +  describe "fix_url/1" do +    test "fixes data for object when url is map" do +      object = %{ +        "url" => %{ +          "type" => "Link", +          "mimeType" => "video/mp4", +          "href" => "https://peede8d-46fb-ad81-2d4c2d1630e3-480.mp4" +        } +      } + +      assert Transmogrifier.fix_url(object) == %{ +               "url" => "https://peede8d-46fb-ad81-2d4c2d1630e3-480.mp4" +             } +    end + +    test "fixes data for video object" do +      object = %{ +        "type" => "Video", +        "url" => [ +          %{ +            "type" => "Link", +            "mimeType" => "video/mp4", +            "href" => "https://peede8d-46fb-ad81-2d4c2d1630e3-480.mp4" +          }, +          %{ +            "type" => "Link", +            "mimeType" => "video/mp4", +            "href" => "https://peertube46fb-ad81-2d4c2d1630e3-240.mp4" +          }, +          %{ +            "type" => "Link", +            "mimeType" => "text/html", +            "href" => "https://peertube.-2d4c2d1630e3" +          }, +          %{ +            "type" => "Link", +            "mimeType" => "text/html", +            "href" => "https://peertube.-2d4c2d16377-42" +          } +        ] +      } + +      assert Transmogrifier.fix_url(object) == %{ +               "attachment" => [ +                 %{ +                   "href" => "https://peede8d-46fb-ad81-2d4c2d1630e3-480.mp4", +                   "mimeType" => "video/mp4", +                   "type" => "Link" +                 } +               ], +               "type" => "Video", +               "url" => "https://peertube.-2d4c2d1630e3" +             } +    end + +    test "fixes url for not Video object" do +      object = %{ +        "type" => "Text", +        "url" => [ +          %{ +            "type" => "Link", +            "mimeType" => "text/html", +            "href" => "https://peertube.-2d4c2d1630e3" +          }, +          %{ +            "type" => "Link", +            "mimeType" => "text/html", +            "href" => "https://peertube.-2d4c2d16377-42" +          } +        ] +      } + +      assert Transmogrifier.fix_url(object) == %{ +               "type" => "Text", +               "url" => "https://peertube.-2d4c2d1630e3" +             } + +      assert Transmogrifier.fix_url(%{"type" => "Text", "url" => []}) == %{ +               "type" => "Text", +               "url" => "" +             } +    end + +    test "retunrs not modified object" do +      assert Transmogrifier.fix_url(%{"type" => "Text"}) == %{"type" => "Text"} +    end +  end + +  describe "get_obj_helper/2" do +    test "returns nil when cannot normalize object" do +      refute Transmogrifier.get_obj_helper("test-obj-id") +    end + +    test "returns {:ok, %Object{}} for success case" do +      assert {:ok, %Object{}} = +               Transmogrifier.get_obj_helper("https://shitposter.club/notice/2827873") +    end +  end + +  describe "fix_attachments/1" do +    test "returns not modified object" do +      data = Poison.decode!(File.read!("test/fixtures/mastodon-post-activity.json")) +      assert Transmogrifier.fix_attachments(data) == data +    end + +    test "returns modified object when attachment is map" do +      assert Transmogrifier.fix_attachments(%{ +               "attachment" => %{ +                 "mediaType" => "video/mp4", +                 "url" => "https://peertube.moe/stat-480.mp4" +               } +             }) == %{ +               "attachment" => [ +                 %{ +                   "mediaType" => "video/mp4", +                   "url" => [ +                     %{ +                       "href" => "https://peertube.moe/stat-480.mp4", +                       "mediaType" => "video/mp4", +                       "type" => "Link" +                     } +                   ] +                 } +               ] +             } +    end + +    test "returns modified object when attachment is list" do +      assert Transmogrifier.fix_attachments(%{ +               "attachment" => [ +                 %{"mediaType" => "video/mp4", "url" => "https://pe.er/stat-480.mp4"}, +                 %{"mimeType" => "video/mp4", "href" => "https://pe.er/stat-480.mp4"} +               ] +             }) == %{ +               "attachment" => [ +                 %{ +                   "mediaType" => "video/mp4", +                   "url" => [ +                     %{ +                       "href" => "https://pe.er/stat-480.mp4", +                       "mediaType" => "video/mp4", +                       "type" => "Link" +                     } +                   ] +                 }, +                 %{ +                   "href" => "https://pe.er/stat-480.mp4", +                   "mediaType" => "video/mp4", +                   "mimeType" => "video/mp4", +                   "url" => [ +                     %{ +                       "href" => "https://pe.er/stat-480.mp4", +                       "mediaType" => "video/mp4", +                       "type" => "Link" +                     } +                   ] +                 } +               ] +             } +    end +  end + +  describe "fix_emoji/1" do +    test "returns not modified object when object not contains tags" do +      data = Poison.decode!(File.read!("test/fixtures/mastodon-post-activity.json")) +      assert Transmogrifier.fix_emoji(data) == data +    end + +    test "returns object with emoji when object contains list tags" do +      assert Transmogrifier.fix_emoji(%{ +               "tag" => [ +                 %{"type" => "Emoji", "name" => ":bib:", "icon" => %{"url" => "/test"}}, +                 %{"type" => "Hashtag"} +               ] +             }) == %{ +               "emoji" => %{"bib" => "/test"}, +               "tag" => [ +                 %{"icon" => %{"url" => "/test"}, "name" => ":bib:", "type" => "Emoji"}, +                 %{"type" => "Hashtag"} +               ] +             } +    end + +    test "returns object with emoji when object contains map tag" do +      assert Transmogrifier.fix_emoji(%{ +               "tag" => %{"type" => "Emoji", "name" => ":bib:", "icon" => %{"url" => "/test"}} +             }) == %{ +               "emoji" => %{"bib" => "/test"}, +               "tag" => %{"icon" => %{"url" => "/test"}, "name" => ":bib:", "type" => "Emoji"} +             } +    end +  end  end diff --git a/test/web/activity_pub/views/user_view_test.exs b/test/web/activity_pub/views/user_view_test.exs index 2b4a04afd..3155749aa 100644 --- a/test/web/activity_pub/views/user_view_test.exs +++ b/test/web/activity_pub/views/user_view_test.exs @@ -37,6 +37,22 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do             } = UserView.render("user.json", %{user: user})    end +  test "Renders with emoji tags" do +    user = insert(:user, %{info: %{emoji: [%{"bib" => "/test"}]}}) + +    assert %{ +             "tag" => [ +               %{ +                 "icon" => %{"type" => "Image", "url" => "/test"}, +                 "id" => "/test", +                 "name" => ":bib:", +                 "type" => "Emoji", +                 "updated" => "1970-01-01T00:00:00Z" +               } +             ] +           } = UserView.render("user.json", %{user: user}) +  end +    test "Does not add an avatar image if the user hasn't set one" do      user = insert(:user)      {:ok, user} = User.ensure_keys_present(user) @@ -142,4 +158,35 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do        assert %{"totalItems" => 1} = UserView.render("following.json", %{user: user})      end    end + +  test "activity collection page aginates correctly" do +    user = insert(:user) + +    posts = +      for i <- 0..25 do +        {:ok, activity} = CommonAPI.post(user, %{"status" => "post #{i}"}) +        activity +      end + +    # outbox sorts chronologically, newest first, with ten per page +    posts = Enum.reverse(posts) + +    %{"next" => next_url} = +      UserView.render("activity_collection_page.json", %{ +        iri: "#{user.ap_id}/outbox", +        activities: Enum.take(posts, 10) +      }) + +    next_id = Enum.at(posts, 9).id +    assert next_url =~ next_id + +    %{"next" => next_url} = +      UserView.render("activity_collection_page.json", %{ +        iri: "#{user.ap_id}/outbox", +        activities: Enum.take(Enum.drop(posts, 10), 10) +      }) + +    next_id = Enum.at(posts, 19).id +    assert next_url =~ next_id +  end  end diff --git a/test/web/admin_api/admin_api_controller_test.exs b/test/web/admin_api/admin_api_controller_test.exs index 108143f6a..b5c355e66 100644 --- a/test/web/admin_api/admin_api_controller_test.exs +++ b/test/web/admin_api/admin_api_controller_test.exs @@ -4,11 +4,13 @@  defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do    use Pleroma.Web.ConnCase +  use Oban.Testing, repo: Pleroma.Repo    alias Pleroma.Activity    alias Pleroma.HTML    alias Pleroma.ModerationLog    alias Pleroma.Repo +  alias Pleroma.Tests.ObanHelpers    alias Pleroma.User    alias Pleroma.UserInviteToken    alias Pleroma.Web.CommonAPI @@ -584,7 +586,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do        |> put_req_header("accept", "application/json")        |> get("/api/pleroma/admin/users/#{user.nickname}/password_reset") -    assert conn.status == 200 +    resp = json_response(conn, 200) + +    assert Regex.match?(~r/(http:\/\/|https:\/\/)/, resp["link"])    end    describe "GET /api/pleroma/admin/users" do @@ -2253,8 +2257,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do    describe "GET /api/pleroma/admin/moderation_log" do      setup %{conn: conn} do        admin = insert(:user, info: %{is_admin: true}) +      moderator = insert(:user, info: %{is_moderator: true}) -      %{conn: assign(conn, :user, admin), admin: admin} +      %{conn: assign(conn, :user, admin), admin: admin, moderator: moderator}      end      test "returns the log", %{conn: conn, admin: admin} do @@ -2287,9 +2292,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do        conn = get(conn, "/api/pleroma/admin/moderation_log")        response = json_response(conn, 200) -      [first_entry, second_entry] = response +      [first_entry, second_entry] = response["items"] -      assert response |> length() == 2 +      assert response["total"] == 2        assert first_entry["data"]["action"] == "relay_unfollow"        assert first_entry["message"] == @@ -2331,9 +2336,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do        conn1 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=1")        response1 = json_response(conn1, 200) -      [first_entry] = response1 +      [first_entry] = response1["items"] -      assert response1 |> length() == 1 +      assert response1["total"] == 2 +      assert response1["items"] |> length() == 1        assert first_entry["data"]["action"] == "relay_unfollow"        assert first_entry["message"] == @@ -2342,14 +2348,143 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do        conn2 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=2")        response2 = json_response(conn2, 200) -      [second_entry] = response2 +      [second_entry] = response2["items"] -      assert response2 |> length() == 1 +      assert response2["total"] == 2 +      assert response2["items"] |> length() == 1        assert second_entry["data"]["action"] == "relay_follow"        assert second_entry["message"] ==                 "@#{admin.nickname} followed relay: https://example.org/relay"      end + +    test "filters log by date", %{conn: conn, admin: admin} do +      first_date = "2017-08-15T15:47:06Z" +      second_date = "2017-08-20T15:47:06Z" + +      Repo.insert(%ModerationLog{ +        data: %{ +          actor: %{ +            "id" => admin.id, +            "nickname" => admin.nickname, +            "type" => "user" +          }, +          action: "relay_follow", +          target: "https://example.org/relay" +        }, +        inserted_at: NaiveDateTime.from_iso8601!(first_date) +      }) + +      Repo.insert(%ModerationLog{ +        data: %{ +          actor: %{ +            "id" => admin.id, +            "nickname" => admin.nickname, +            "type" => "user" +          }, +          action: "relay_unfollow", +          target: "https://example.org/relay" +        }, +        inserted_at: NaiveDateTime.from_iso8601!(second_date) +      }) + +      conn1 = +        get( +          conn, +          "/api/pleroma/admin/moderation_log?start_date=#{second_date}" +        ) + +      response1 = json_response(conn1, 200) +      [first_entry] = response1["items"] + +      assert response1["total"] == 1 +      assert first_entry["data"]["action"] == "relay_unfollow" + +      assert first_entry["message"] == +               "@#{admin.nickname} unfollowed relay: https://example.org/relay" +    end + +    test "returns log filtered by user", %{conn: conn, admin: admin, moderator: moderator} do +      Repo.insert(%ModerationLog{ +        data: %{ +          actor: %{ +            "id" => admin.id, +            "nickname" => admin.nickname, +            "type" => "user" +          }, +          action: "relay_follow", +          target: "https://example.org/relay" +        } +      }) + +      Repo.insert(%ModerationLog{ +        data: %{ +          actor: %{ +            "id" => moderator.id, +            "nickname" => moderator.nickname, +            "type" => "user" +          }, +          action: "relay_unfollow", +          target: "https://example.org/relay" +        } +      }) + +      conn1 = get(conn, "/api/pleroma/admin/moderation_log?user_id=#{moderator.id}") + +      response1 = json_response(conn1, 200) +      [first_entry] = response1["items"] + +      assert response1["total"] == 1 +      assert get_in(first_entry, ["data", "actor", "id"]) == moderator.id +    end + +    test "returns log filtered by search", %{conn: conn, moderator: moderator} do +      ModerationLog.insert_log(%{ +        actor: moderator, +        action: "relay_follow", +        target: "https://example.org/relay" +      }) + +      ModerationLog.insert_log(%{ +        actor: moderator, +        action: "relay_unfollow", +        target: "https://example.org/relay" +      }) + +      conn1 = get(conn, "/api/pleroma/admin/moderation_log?search=unfo") + +      response1 = json_response(conn1, 200) +      [first_entry] = response1["items"] + +      assert response1["total"] == 1 + +      assert get_in(first_entry, ["data", "message"]) == +               "@#{moderator.nickname} unfollowed relay: https://example.org/relay" +    end +  end + +  describe "PATCH /users/:nickname/force_password_reset" do +    setup %{conn: conn} do +      admin = insert(:user, info: %{is_admin: true}) +      user = insert(:user) + +      %{conn: assign(conn, :user, admin), admin: admin, user: user} +    end + +    test "sets password_reset_pending to true", %{admin: admin, user: user} do +      assert user.info.password_reset_pending == false + +      conn = +        build_conn() +        |> assign(:user, admin) +        |> patch("/api/pleroma/admin/users/#{user.nickname}/force_password_reset") + +      assert json_response(conn, 204) == "" + +      ObanHelpers.perform_all() + +      assert User.get_by_id(user.id).info.password_reset_pending == true +    end    end  end diff --git a/test/web/admin_api/views/report_view_test.exs b/test/web/admin_api/views/report_view_test.exs index a00c9c579..475705857 100644 --- a/test/web/admin_api/views/report_view_test.exs +++ b/test/web/admin_api/views/report_view_test.exs @@ -5,6 +5,7 @@  defmodule Pleroma.Web.AdminAPI.ReportViewTest do    use Pleroma.DataCase    import Pleroma.Factory +  alias Pleroma.Web.AdminAPI.Report    alias Pleroma.Web.AdminAPI.ReportView    alias Pleroma.Web.CommonAPI    alias Pleroma.Web.MastodonAPI.AccountView @@ -20,12 +21,12 @@ defmodule Pleroma.Web.AdminAPI.ReportViewTest do        content: nil,        actor:          Map.merge( -          AccountView.render("account.json", %{user: user}), +          AccountView.render("show.json", %{user: user}),            Pleroma.Web.AdminAPI.AccountView.render("show.json", %{user: user})          ),        account:          Map.merge( -          AccountView.render("account.json", %{user: other_user}), +          AccountView.render("show.json", %{user: other_user}),            Pleroma.Web.AdminAPI.AccountView.render("show.json", %{user: other_user})          ),        statuses: [], @@ -34,7 +35,7 @@ defmodule Pleroma.Web.AdminAPI.ReportViewTest do      }      result = -      ReportView.render("show.json", %{report: activity}) +      ReportView.render("show.json", Report.extract_report_info(activity))        |> Map.delete(:created_at)      assert result == expected @@ -52,21 +53,21 @@ defmodule Pleroma.Web.AdminAPI.ReportViewTest do        content: nil,        actor:          Map.merge( -          AccountView.render("account.json", %{user: user}), +          AccountView.render("show.json", %{user: user}),            Pleroma.Web.AdminAPI.AccountView.render("show.json", %{user: user})          ),        account:          Map.merge( -          AccountView.render("account.json", %{user: other_user}), +          AccountView.render("show.json", %{user: other_user}),            Pleroma.Web.AdminAPI.AccountView.render("show.json", %{user: other_user})          ), -      statuses: [StatusView.render("status.json", %{activity: activity})], +      statuses: [StatusView.render("show.json", %{activity: activity})],        state: "open",        id: report_activity.id      }      result = -      ReportView.render("show.json", %{report: report_activity}) +      ReportView.render("show.json", Report.extract_report_info(report_activity))        |> Map.delete(:created_at)      assert result == expected @@ -78,7 +79,9 @@ defmodule Pleroma.Web.AdminAPI.ReportViewTest do      {:ok, activity} = CommonAPI.report(user, %{"account_id" => other_user.id})      {:ok, activity} = CommonAPI.update_report_state(activity.id, "closed") -    assert %{state: "closed"} = ReportView.render("show.json", %{report: activity}) + +    assert %{state: "closed"} = +             ReportView.render("show.json", Report.extract_report_info(activity))    end    test "renders report description" do @@ -92,7 +95,7 @@ defmodule Pleroma.Web.AdminAPI.ReportViewTest do        })      assert %{content: "posts are too good for this instance"} = -             ReportView.render("show.json", %{report: activity}) +             ReportView.render("show.json", Report.extract_report_info(activity))    end    test "sanitizes report description" do @@ -109,7 +112,7 @@ defmodule Pleroma.Web.AdminAPI.ReportViewTest do      activity = Map.put(activity, :data, data)      refute "<script> alert('hecked :D:D:D:D:D:D:D') </script>" == -             ReportView.render("show.json", %{report: activity})[:content] +             ReportView.render("show.json", Report.extract_report_info(activity))[:content]    end    test "doesn't error out when the user doesn't exists" do @@ -125,6 +128,6 @@ defmodule Pleroma.Web.AdminAPI.ReportViewTest do      Pleroma.User.delete(other_user)      Pleroma.User.invalidate_cache(other_user) -    assert %{} = ReportView.render("show.json", %{report: activity}) +    assert %{} = ReportView.render("show.json", Report.extract_report_info(activity))    end  end diff --git a/test/web/common_api/common_api_test.exs b/test/web/common_api/common_api_test.exs index f28a66090..0f4a5eb25 100644 --- a/test/web/common_api/common_api_test.exs +++ b/test/web/common_api/common_api_test.exs @@ -510,4 +510,43 @@ defmodule Pleroma.Web.CommonAPITest do        assert {:error, "Already voted"} == CommonAPI.vote(other_user, object, [1])      end    end + +  describe "listen/2" do +    test "returns a valid activity" do +      user = insert(:user) + +      {:ok, activity} = +        CommonAPI.listen(user, %{ +          "title" => "lain radio episode 1", +          "album" => "lain radio", +          "artist" => "lain", +          "length" => 180_000 +        }) + +      object = Object.normalize(activity) + +      assert object.data["title"] == "lain radio episode 1" + +      assert Visibility.get_visibility(activity) == "public" +    end + +    test "respects visibility=private" do +      user = insert(:user) + +      {:ok, activity} = +        CommonAPI.listen(user, %{ +          "title" => "lain radio episode 1", +          "album" => "lain radio", +          "artist" => "lain", +          "length" => 180_000, +          "visibility" => "private" +        }) + +      object = Object.normalize(activity) + +      assert object.data["title"] == "lain radio episode 1" + +      assert Visibility.get_visibility(activity) == "private" +    end +  end  end diff --git a/test/web/common_api/common_api_utils_test.exs b/test/web/common_api/common_api_utils_test.exs index 230146451..2588898d0 100644 --- a/test/web/common_api/common_api_utils_test.exs +++ b/test/web/common_api/common_api_utils_test.exs @@ -157,11 +157,11 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do        text = "**hello world**\n\n*another @user__test and @user__test google.com paragraph*"        expected = -        "<p><strong>hello world</strong></p>\n<p><em>another <span class=\"h-card\"><a data-user=\"#{ +        ~s(<p><strong>hello world</strong></p>\n<p><em>another <span class="h-card"><a data-user="#{            user.id -        }\" class=\"u-url mention\" href=\"http://foo.com/user__test\">@<span>user__test</span></a></span> and <span class=\"h-card\"><a data-user=\"#{ +        }" class="u-url mention" href="http://foo.com/user__test" rel="ugc">@<span>user__test</span></a></span> and <span class="h-card"><a data-user="#{            user.id -        }\" class=\"u-url mention\" href=\"http://foo.com/user__test\">@<span>user__test</span></a></span> <a href=\"http://google.com\">google.com</a> paragraph</em></p>\n" +        }" class="u-url mention" href="http://foo.com/user__test" rel="ugc">@<span>user__test</span></a></span> <a href="http://google.com" rel="ugc">google.com</a> paragraph</em></p>\n)        {output, _, _} = Utils.format_input(text, "text/markdown") diff --git a/test/web/mastodon_api/controllers/mastodon_api_controller/update_credentials_test.exs b/test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs index 99d534348..f6c9f5028 100644 --- a/test/web/mastodon_api/controllers/mastodon_api_controller/update_credentials_test.exs +++ b/test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs @@ -86,10 +86,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do        assert user = json_response(conn, 200)        assert user["note"] == -               ~s(I drink <a class="hashtag" data-tag="cofe" href="http://localhost:4001/tag/cofe" rel="tag">#cofe</a> with <span class="h-card"><a data-user=") <> -                 user2.id <> -                 ~s(" class="u-url mention" href=") <> -                 user2.ap_id <> ~s(">@<span>) <> user2.nickname <> ~s(</span></a></span>) +               ~s(I drink <a class="hashtag" data-tag="cofe" href="http://localhost:4001/tag/cofe">#cofe</a> with <span class="h-card"><a data-user="#{ +                 user2.id +               }" class="u-url mention" href="#{user2.ap_id}" rel="ugc">@<span>#{user2.nickname}</span></a></span>)      end      test "updates the user's locking status", %{conn: conn} do @@ -335,7 +334,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do        assert account["fields"] == [                 %{"name" => "foo", "value" => "bar"}, -               %{"name" => "link", "value" => "<a href=\"http://cofe.io\">cofe.io</a>"} +               %{"name" => "link", "value" => ~S(<a href="http://cofe.io" rel="ugc">cofe.io</a>)}               ]        assert account["source"]["fields"] == [ diff --git a/test/web/mastodon_api/controllers/account_controller_test.exs b/test/web/mastodon_api/controllers/account_controller_test.exs new file mode 100644 index 000000000..8c8017838 --- /dev/null +++ b/test/web/mastodon_api/controllers/account_controller_test.exs @@ -0,0 +1,852 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do +  use Pleroma.Web.ConnCase + +  alias Pleroma.Repo +  alias Pleroma.User +  alias Pleroma.Web.ActivityPub.ActivityPub +  alias Pleroma.Web.CommonAPI +  alias Pleroma.Web.OAuth.Token + +  import Pleroma.Factory + +  describe "account fetching" do +    test "works by id" do +      user = insert(:user) + +      conn = +        build_conn() +        |> get("/api/v1/accounts/#{user.id}") + +      assert %{"id" => id} = json_response(conn, 200) +      assert id == to_string(user.id) + +      conn = +        build_conn() +        |> get("/api/v1/accounts/-1") + +      assert %{"error" => "Can't find user"} = json_response(conn, 404) +    end + +    test "works by nickname" do +      user = insert(:user) + +      conn = +        build_conn() +        |> get("/api/v1/accounts/#{user.nickname}") + +      assert %{"id" => id} = json_response(conn, 200) +      assert id == user.id +    end + +    test "works by nickname for remote users" do +      limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content]) +      Pleroma.Config.put([:instance, :limit_to_local_content], false) +      user = insert(:user, nickname: "user@example.com", local: false) + +      conn = +        build_conn() +        |> get("/api/v1/accounts/#{user.nickname}") + +      Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local) +      assert %{"id" => id} = json_response(conn, 200) +      assert id == user.id +    end + +    test "respects limit_to_local_content == :all for remote user nicknames" do +      limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content]) +      Pleroma.Config.put([:instance, :limit_to_local_content], :all) + +      user = insert(:user, nickname: "user@example.com", local: false) + +      conn = +        build_conn() +        |> get("/api/v1/accounts/#{user.nickname}") + +      Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local) +      assert json_response(conn, 404) +    end + +    test "respects limit_to_local_content == :unauthenticated for remote user nicknames" do +      limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content]) +      Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated) + +      user = insert(:user, nickname: "user@example.com", local: false) +      reading_user = insert(:user) + +      conn = +        build_conn() +        |> get("/api/v1/accounts/#{user.nickname}") + +      assert json_response(conn, 404) + +      conn = +        build_conn() +        |> assign(:user, reading_user) +        |> get("/api/v1/accounts/#{user.nickname}") + +      Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local) +      assert %{"id" => id} = json_response(conn, 200) +      assert id == user.id +    end + +    test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do +      # Need to set an old-style integer ID to reproduce the problem +      # (these are no longer assigned to new accounts but were preserved +      # for existing accounts during the migration to flakeIDs) +      user_one = insert(:user, %{id: 1212}) +      user_two = insert(:user, %{nickname: "#{user_one.id}garbage"}) + +      resp_one = +        conn +        |> get("/api/v1/accounts/#{user_one.id}") + +      resp_two = +        conn +        |> get("/api/v1/accounts/#{user_two.nickname}") + +      resp_three = +        conn +        |> get("/api/v1/accounts/#{user_two.id}") + +      acc_one = json_response(resp_one, 200) +      acc_two = json_response(resp_two, 200) +      acc_three = json_response(resp_three, 200) +      refute acc_one == acc_two +      assert acc_two == acc_three +    end +  end + +  describe "user timelines" do +    test "gets a users statuses", %{conn: conn} do +      user_one = insert(:user) +      user_two = insert(:user) +      user_three = insert(:user) + +      {:ok, user_three} = User.follow(user_three, user_one) + +      {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"}) + +      {:ok, direct_activity} = +        CommonAPI.post(user_one, %{ +          "status" => "Hi, @#{user_two.nickname}.", +          "visibility" => "direct" +        }) + +      {:ok, private_activity} = +        CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"}) + +      resp = +        conn +        |> get("/api/v1/accounts/#{user_one.id}/statuses") + +      assert [%{"id" => id}] = json_response(resp, 200) +      assert id == to_string(activity.id) + +      resp = +        conn +        |> assign(:user, user_two) +        |> get("/api/v1/accounts/#{user_one.id}/statuses") + +      assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200) +      assert id_one == to_string(direct_activity.id) +      assert id_two == to_string(activity.id) + +      resp = +        conn +        |> assign(:user, user_three) +        |> get("/api/v1/accounts/#{user_one.id}/statuses") + +      assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200) +      assert id_one == to_string(private_activity.id) +      assert id_two == to_string(activity.id) +    end + +    test "unimplemented pinned statuses feature", %{conn: conn} do +      note = insert(:note_activity) +      user = User.get_cached_by_ap_id(note.data["actor"]) + +      conn = +        conn +        |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true") + +      assert json_response(conn, 200) == [] +    end + +    test "gets an users media", %{conn: conn} do +      note = insert(:note_activity) +      user = User.get_cached_by_ap_id(note.data["actor"]) + +      file = %Plug.Upload{ +        content_type: "image/jpg", +        path: Path.absname("test/fixtures/image.jpg"), +        filename: "an_image.jpg" +      } + +      {:ok, %{id: media_id}} = ActivityPub.upload(file, actor: user.ap_id) + +      {:ok, image_post} = CommonAPI.post(user, %{"status" => "cofe", "media_ids" => [media_id]}) + +      conn = +        conn +        |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"}) + +      assert [%{"id" => id}] = json_response(conn, 200) +      assert id == to_string(image_post.id) + +      conn = +        build_conn() +        |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"}) + +      assert [%{"id" => id}] = json_response(conn, 200) +      assert id == to_string(image_post.id) +    end + +    test "gets a user's statuses without reblogs", %{conn: conn} do +      user = insert(:user) +      {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"}) +      {:ok, _, _} = CommonAPI.repeat(post.id, user) + +      conn = +        conn +        |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"}) + +      assert [%{"id" => id}] = json_response(conn, 200) +      assert id == to_string(post.id) + +      conn = +        conn +        |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"}) + +      assert [%{"id" => id}] = json_response(conn, 200) +      assert id == to_string(post.id) +    end + +    test "filters user's statuses by a hashtag", %{conn: conn} do +      user = insert(:user) +      {:ok, post} = CommonAPI.post(user, %{"status" => "#hashtag"}) +      {:ok, _post} = CommonAPI.post(user, %{"status" => "hashtag"}) + +      conn = +        conn +        |> get("/api/v1/accounts/#{user.id}/statuses", %{"tagged" => "hashtag"}) + +      assert [%{"id" => id}] = json_response(conn, 200) +      assert id == to_string(post.id) +    end +  end + +  describe "followers" do +    test "getting followers", %{conn: conn} do +      user = insert(:user) +      other_user = insert(:user) +      {:ok, user} = User.follow(user, other_user) + +      conn = +        conn +        |> get("/api/v1/accounts/#{other_user.id}/followers") + +      assert [%{"id" => id}] = json_response(conn, 200) +      assert id == to_string(user.id) +    end + +    test "getting followers, hide_followers", %{conn: conn} do +      user = insert(:user) +      other_user = insert(:user, %{info: %{hide_followers: true}}) +      {:ok, _user} = User.follow(user, other_user) + +      conn = +        conn +        |> get("/api/v1/accounts/#{other_user.id}/followers") + +      assert [] == json_response(conn, 200) +    end + +    test "getting followers, hide_followers, same user requesting", %{conn: conn} do +      user = insert(:user) +      other_user = insert(:user, %{info: %{hide_followers: true}}) +      {:ok, _user} = User.follow(user, other_user) + +      conn = +        conn +        |> assign(:user, other_user) +        |> get("/api/v1/accounts/#{other_user.id}/followers") + +      refute [] == json_response(conn, 200) +    end + +    test "getting followers, pagination", %{conn: conn} do +      user = insert(:user) +      follower1 = insert(:user) +      follower2 = insert(:user) +      follower3 = insert(:user) +      {:ok, _} = User.follow(follower1, user) +      {:ok, _} = User.follow(follower2, user) +      {:ok, _} = User.follow(follower3, user) + +      conn = +        conn +        |> assign(:user, user) + +      res_conn = +        conn +        |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}") + +      assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200) +      assert id3 == follower3.id +      assert id2 == follower2.id + +      res_conn = +        conn +        |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}") + +      assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200) +      assert id2 == follower2.id +      assert id1 == follower1.id + +      res_conn = +        conn +        |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}") + +      assert [%{"id" => id2}] = json_response(res_conn, 200) +      assert id2 == follower2.id + +      assert [link_header] = get_resp_header(res_conn, "link") +      assert link_header =~ ~r/min_id=#{follower2.id}/ +      assert link_header =~ ~r/max_id=#{follower2.id}/ +    end +  end + +  describe "following" do +    test "getting following", %{conn: conn} do +      user = insert(:user) +      other_user = insert(:user) +      {:ok, user} = User.follow(user, other_user) + +      conn = +        conn +        |> get("/api/v1/accounts/#{user.id}/following") + +      assert [%{"id" => id}] = json_response(conn, 200) +      assert id == to_string(other_user.id) +    end + +    test "getting following, hide_follows", %{conn: conn} do +      user = insert(:user, %{info: %{hide_follows: true}}) +      other_user = insert(:user) +      {:ok, user} = User.follow(user, other_user) + +      conn = +        conn +        |> get("/api/v1/accounts/#{user.id}/following") + +      assert [] == json_response(conn, 200) +    end + +    test "getting following, hide_follows, same user requesting", %{conn: conn} do +      user = insert(:user, %{info: %{hide_follows: true}}) +      other_user = insert(:user) +      {:ok, user} = User.follow(user, other_user) + +      conn = +        conn +        |> assign(:user, user) +        |> get("/api/v1/accounts/#{user.id}/following") + +      refute [] == json_response(conn, 200) +    end + +    test "getting following, pagination", %{conn: conn} do +      user = insert(:user) +      following1 = insert(:user) +      following2 = insert(:user) +      following3 = insert(:user) +      {:ok, _} = User.follow(user, following1) +      {:ok, _} = User.follow(user, following2) +      {:ok, _} = User.follow(user, following3) + +      conn = +        conn +        |> assign(:user, user) + +      res_conn = +        conn +        |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}") + +      assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200) +      assert id3 == following3.id +      assert id2 == following2.id + +      res_conn = +        conn +        |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}") + +      assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200) +      assert id2 == following2.id +      assert id1 == following1.id + +      res_conn = +        conn +        |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}") + +      assert [%{"id" => id2}] = json_response(res_conn, 200) +      assert id2 == following2.id + +      assert [link_header] = get_resp_header(res_conn, "link") +      assert link_header =~ ~r/min_id=#{following2.id}/ +      assert link_header =~ ~r/max_id=#{following2.id}/ +    end +  end + +  describe "follow/unfollow" do +    test "following / unfollowing a user", %{conn: conn} do +      user = insert(:user) +      other_user = insert(:user) + +      conn = +        conn +        |> assign(:user, user) +        |> post("/api/v1/accounts/#{other_user.id}/follow") + +      assert %{"id" => _id, "following" => true} = json_response(conn, 200) + +      user = User.get_cached_by_id(user.id) + +      conn = +        build_conn() +        |> assign(:user, user) +        |> post("/api/v1/accounts/#{other_user.id}/unfollow") + +      assert %{"id" => _id, "following" => false} = json_response(conn, 200) + +      user = User.get_cached_by_id(user.id) + +      conn = +        build_conn() +        |> assign(:user, user) +        |> post("/api/v1/follows", %{"uri" => other_user.nickname}) + +      assert %{"id" => id} = json_response(conn, 200) +      assert id == to_string(other_user.id) +    end + +    test "following without reblogs" do +      follower = insert(:user) +      followed = insert(:user) +      other_user = insert(:user) + +      conn = +        build_conn() +        |> assign(:user, follower) +        |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false") + +      assert %{"showing_reblogs" => false} = json_response(conn, 200) + +      {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"}) +      {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed) + +      conn = +        build_conn() +        |> assign(:user, User.get_cached_by_id(follower.id)) +        |> get("/api/v1/timelines/home") + +      assert [] == json_response(conn, 200) + +      conn = +        build_conn() +        |> assign(:user, follower) +        |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true") + +      assert %{"showing_reblogs" => true} = json_response(conn, 200) + +      conn = +        build_conn() +        |> assign(:user, User.get_cached_by_id(follower.id)) +        |> get("/api/v1/timelines/home") + +      expected_activity_id = reblog.id +      assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200) +    end + +    test "following / unfollowing errors" do +      user = insert(:user) + +      conn = +        build_conn() +        |> assign(:user, user) + +      # self follow +      conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow") +      assert %{"error" => "Record not found"} = json_response(conn_res, 404) + +      # self unfollow +      user = User.get_cached_by_id(user.id) +      conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow") +      assert %{"error" => "Record not found"} = json_response(conn_res, 404) + +      # self follow via uri +      user = User.get_cached_by_id(user.id) +      conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname}) +      assert %{"error" => "Record not found"} = json_response(conn_res, 404) + +      # follow non existing user +      conn_res = post(conn, "/api/v1/accounts/doesntexist/follow") +      assert %{"error" => "Record not found"} = json_response(conn_res, 404) + +      # follow non existing user via uri +      conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"}) +      assert %{"error" => "Record not found"} = json_response(conn_res, 404) + +      # unfollow non existing user +      conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow") +      assert %{"error" => "Record not found"} = json_response(conn_res, 404) +    end +  end + +  describe "mute/unmute" do +    test "with notifications", %{conn: conn} do +      user = insert(:user) +      other_user = insert(:user) + +      conn = +        conn +        |> assign(:user, user) +        |> post("/api/v1/accounts/#{other_user.id}/mute") + +      response = json_response(conn, 200) + +      assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response +      user = User.get_cached_by_id(user.id) + +      conn = +        build_conn() +        |> assign(:user, user) +        |> post("/api/v1/accounts/#{other_user.id}/unmute") + +      response = json_response(conn, 200) +      assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response +    end + +    test "without notifications", %{conn: conn} do +      user = insert(:user) +      other_user = insert(:user) + +      conn = +        conn +        |> assign(:user, user) +        |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"}) + +      response = json_response(conn, 200) + +      assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response +      user = User.get_cached_by_id(user.id) + +      conn = +        build_conn() +        |> assign(:user, user) +        |> post("/api/v1/accounts/#{other_user.id}/unmute") + +      response = json_response(conn, 200) +      assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response +    end +  end + +  describe "pinned statuses" do +    setup do +      user = insert(:user) +      {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"}) + +      [user: user, activity: activity] +    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 +  end + +  test "blocking / unblocking a user", %{conn: conn} do +    user = insert(:user) +    other_user = insert(:user) + +    conn = +      conn +      |> assign(:user, user) +      |> post("/api/v1/accounts/#{other_user.id}/block") + +    assert %{"id" => _id, "blocking" => true} = json_response(conn, 200) + +    user = User.get_cached_by_id(user.id) + +    conn = +      build_conn() +      |> assign(:user, user) +      |> post("/api/v1/accounts/#{other_user.id}/unblock") + +    assert %{"id" => _id, "blocking" => false} = json_response(conn, 200) +  end + +  describe "create account by app" do +    setup do +      valid_params = %{ +        username: "lain", +        email: "lain@example.org", +        password: "PlzDontHackLain", +        agreement: true +      } + +      [valid_params: valid_params] +    end + +    test "Account registration via Application", %{conn: conn} do +      conn = +        conn +        |> post("/api/v1/apps", %{ +          client_name: "client_name", +          redirect_uris: "urn:ietf:wg:oauth:2.0:oob", +          scopes: "read, write, follow" +        }) + +      %{ +        "client_id" => client_id, +        "client_secret" => client_secret, +        "id" => _, +        "name" => "client_name", +        "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob", +        "vapid_key" => _, +        "website" => nil +      } = json_response(conn, 200) + +      conn = +        conn +        |> post("/oauth/token", %{ +          grant_type: "client_credentials", +          client_id: client_id, +          client_secret: client_secret +        }) + +      assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} = +               json_response(conn, 200) + +      assert token +      token_from_db = Repo.get_by(Token, token: token) +      assert token_from_db +      assert refresh +      assert scope == "read write follow" + +      conn = +        build_conn() +        |> put_req_header("authorization", "Bearer " <> token) +        |> post("/api/v1/accounts", %{ +          username: "lain", +          email: "lain@example.org", +          password: "PlzDontHackLain", +          bio: "Test Bio", +          agreement: true +        }) + +      %{ +        "access_token" => token, +        "created_at" => _created_at, +        "scope" => _scope, +        "token_type" => "Bearer" +      } = json_response(conn, 200) + +      token_from_db = Repo.get_by(Token, token: token) +      assert token_from_db +      token_from_db = Repo.preload(token_from_db, :user) +      assert token_from_db.user + +      assert token_from_db.user.info.confirmation_pending +    end + +    test "returns error when user already registred", %{conn: conn, valid_params: valid_params} do +      _user = insert(:user, email: "lain@example.org") +      app_token = insert(:oauth_token, user: nil) + +      conn = +        conn +        |> put_req_header("authorization", "Bearer " <> app_token.token) + +      res = post(conn, "/api/v1/accounts", valid_params) +      assert json_response(res, 400) == %{"error" => "{\"email\":[\"has already been taken\"]}"} +    end + +    test "rate limit", %{conn: conn} do +      app_token = insert(:oauth_token, user: nil) + +      conn = +        put_req_header(conn, "authorization", "Bearer " <> app_token.token) +        |> Map.put(:remote_ip, {15, 15, 15, 15}) + +      for i <- 1..5 do +        conn = +          conn +          |> post("/api/v1/accounts", %{ +            username: "#{i}lain", +            email: "#{i}lain@example.org", +            password: "PlzDontHackLain", +            agreement: true +          }) + +        %{ +          "access_token" => token, +          "created_at" => _created_at, +          "scope" => _scope, +          "token_type" => "Bearer" +        } = json_response(conn, 200) + +        token_from_db = Repo.get_by(Token, token: token) +        assert token_from_db +        token_from_db = Repo.preload(token_from_db, :user) +        assert token_from_db.user + +        assert token_from_db.user.info.confirmation_pending +      end + +      conn = +        conn +        |> post("/api/v1/accounts", %{ +          username: "6lain", +          email: "6lain@example.org", +          password: "PlzDontHackLain", +          agreement: true +        }) + +      assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"} +    end + +    test "returns bad_request if missing required params", %{ +      conn: conn, +      valid_params: valid_params +    } do +      app_token = insert(:oauth_token, user: nil) + +      conn = +        conn +        |> put_req_header("authorization", "Bearer " <> app_token.token) + +      res = post(conn, "/api/v1/accounts", valid_params) +      assert json_response(res, 200) + +      [{127, 0, 0, 1}, {127, 0, 0, 2}, {127, 0, 0, 3}, {127, 0, 0, 4}] +      |> Stream.zip(valid_params) +      |> Enum.each(fn {ip, {attr, _}} -> +        res = +          conn +          |> Map.put(:remote_ip, ip) +          |> post("/api/v1/accounts", Map.delete(valid_params, attr)) +          |> json_response(400) + +        assert res == %{"error" => "Missing parameters"} +      end) +    end + +    test "returns forbidden if token is invalid", %{conn: conn, valid_params: valid_params} do +      conn = +        conn +        |> put_req_header("authorization", "Bearer " <> "invalid-token") + +      res = post(conn, "/api/v1/accounts", valid_params) +      assert json_response(res, 403) == %{"error" => "Invalid credentials"} +    end +  end + +  describe "GET /api/v1/accounts/:id/lists - account_lists" do +    test "returns lists to which the account belongs", %{conn: conn} do +      user = insert(:user) +      other_user = insert(:user) +      assert {:ok, %Pleroma.List{} = list} = Pleroma.List.create("Test List", user) +      {:ok, %{following: _following}} = Pleroma.List.follow(list, other_user) + +      res = +        conn +        |> assign(:user, user) +        |> get("/api/v1/accounts/#{other_user.id}/lists") +        |> json_response(200) + +      assert res == [%{"id" => to_string(list.id), "title" => "Test List"}] +    end +  end + +  describe "verify_credentials" do +    test "verify_credentials", %{conn: conn} do +      user = insert(:user) + +      conn = +        conn +        |> assign(:user, user) +        |> get("/api/v1/accounts/verify_credentials") + +      response = json_response(conn, 200) + +      assert %{"id" => id, "source" => %{"privacy" => "public"}} = response +      assert response["pleroma"]["chat_token"] +      assert id == to_string(user.id) +    end + +    test "verify_credentials default scope unlisted", %{conn: conn} do +      user = insert(:user, %{info: %User.Info{default_scope: "unlisted"}}) + +      conn = +        conn +        |> assign(:user, user) +        |> get("/api/v1/accounts/verify_credentials") + +      assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200) +      assert id == to_string(user.id) +    end + +    test "locked accounts", %{conn: conn} do +      user = insert(:user, %{info: %User.Info{default_scope: "private"}}) + +      conn = +        conn +        |> assign(:user, user) +        |> get("/api/v1/accounts/verify_credentials") + +      assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200) +      assert id == to_string(user.id) +    end +  end + +  describe "user relationships" do +    test "returns the relationships for the current user", %{conn: conn} do +      user = insert(:user) +      other_user = insert(:user) +      {:ok, user} = User.follow(user, other_user) + +      conn = +        conn +        |> assign(:user, user) +        |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]}) + +      assert [relationship] = json_response(conn, 200) + +      assert to_string(other_user.id) == relationship["id"] +    end + +    test "returns an empty list on a bad request", %{conn: conn} do +      user = insert(:user) + +      conn = +        conn +        |> assign(:user, user) +        |> get("/api/v1/accounts/relationships", %{}) + +      assert [] = json_response(conn, 200) +    end +  end +end diff --git a/test/web/mastodon_api/controllers/conversation_controller_test.exs b/test/web/mastodon_api/controllers/conversation_controller_test.exs new file mode 100644 index 000000000..7117fc76a --- /dev/null +++ b/test/web/mastodon_api/controllers/conversation_controller_test.exs @@ -0,0 +1,75 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do +  use Pleroma.Web.ConnCase + +  alias Pleroma.User +  alias Pleroma.Web.CommonAPI + +  import Pleroma.Factory + +  test "Conversations", %{conn: conn} do +    user_one = insert(:user) +    user_two = insert(:user) +    user_three = insert(:user) + +    {:ok, user_two} = User.follow(user_two, user_one) + +    {:ok, direct} = +      CommonAPI.post(user_one, %{ +        "status" => "Hi @#{user_two.nickname}, @#{user_three.nickname}!", +        "visibility" => "direct" +      }) + +    {:ok, _follower_only} = +      CommonAPI.post(user_one, %{ +        "status" => "Hi @#{user_two.nickname}!", +        "visibility" => "private" +      }) + +    res_conn = +      conn +      |> assign(:user, user_one) +      |> get("/api/v1/conversations") + +    assert response = json_response(res_conn, 200) + +    assert [ +             %{ +               "id" => res_id, +               "accounts" => res_accounts, +               "last_status" => res_last_status, +               "unread" => unread +             } +           ] = response + +    account_ids = Enum.map(res_accounts, & &1["id"]) +    assert length(res_accounts) == 2 +    assert user_two.id in account_ids +    assert user_three.id in account_ids +    assert is_binary(res_id) +    assert unread == true +    assert res_last_status["id"] == direct.id + +    # Apparently undocumented API endpoint +    res_conn = +      conn +      |> assign(:user, user_one) +      |> post("/api/v1/conversations/#{res_id}/read") + +    assert response = json_response(res_conn, 200) +    assert length(response["accounts"]) == 2 +    assert response["last_status"]["id"] == direct.id +    assert response["unread"] == false + +    # (vanilla) Mastodon frontend behaviour +    res_conn = +      conn +      |> assign(:user, user_one) +      |> get("/api/v1/statuses/#{res_last_status["id"]}/context") + +    assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200) +  end +end diff --git a/test/web/mastodon_api/controllers/domain_block_controller_test.exs b/test/web/mastodon_api/controllers/domain_block_controller_test.exs new file mode 100644 index 000000000..25a279cdc --- /dev/null +++ b/test/web/mastodon_api/controllers/domain_block_controller_test.exs @@ -0,0 +1,51 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.DomainBlockControllerTest do +  use Pleroma.Web.ConnCase + +  alias Pleroma.User + +  import Pleroma.Factory + +  test "blocking / unblocking a domain", %{conn: conn} do +    user = insert(:user) +    other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"}) + +    conn = +      conn +      |> assign(:user, user) +      |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"}) + +    assert %{} = json_response(conn, 200) +    user = User.get_cached_by_ap_id(user.ap_id) +    assert User.blocks?(user, other_user) + +    conn = +      build_conn() +      |> assign(:user, user) +      |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"}) + +    assert %{} = json_response(conn, 200) +    user = User.get_cached_by_ap_id(user.ap_id) +    refute User.blocks?(user, other_user) +  end + +  test "getting a list of domain blocks", %{conn: conn} do +    user = insert(:user) + +    {:ok, user} = User.block_domain(user, "bad.site") +    {:ok, user} = User.block_domain(user, "even.worse.site") + +    conn = +      conn +      |> assign(:user, user) +      |> get("/api/v1/domain_blocks") + +    domain_blocks = json_response(conn, 200) + +    assert "bad.site" in domain_blocks +    assert "even.worse.site" in domain_blocks +  end +end diff --git a/test/web/mastodon_api/controllers/filter_controller_test.exs b/test/web/mastodon_api/controllers/filter_controller_test.exs new file mode 100644 index 000000000..5d5b56c8e --- /dev/null +++ b/test/web/mastodon_api/controllers/filter_controller_test.exs @@ -0,0 +1,137 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.FilterControllerTest do +  use Pleroma.Web.ConnCase, async: true + +  alias Pleroma.Web.MastodonAPI.FilterView + +  import Pleroma.Factory + +  test "creating a filter", %{conn: conn} do +    user = insert(:user) + +    filter = %Pleroma.Filter{ +      phrase: "knights", +      context: ["home"] +    } + +    conn = +      conn +      |> assign(:user, user) +      |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context}) + +    assert response = json_response(conn, 200) +    assert response["phrase"] == filter.phrase +    assert response["context"] == filter.context +    assert response["irreversible"] == false +    assert response["id"] != nil +    assert response["id"] != "" +  end + +  test "fetching a list of filters", %{conn: conn} do +    user = insert(:user) + +    query_one = %Pleroma.Filter{ +      user_id: user.id, +      filter_id: 1, +      phrase: "knights", +      context: ["home"] +    } + +    query_two = %Pleroma.Filter{ +      user_id: user.id, +      filter_id: 2, +      phrase: "who", +      context: ["home"] +    } + +    {:ok, filter_one} = Pleroma.Filter.create(query_one) +    {:ok, filter_two} = Pleroma.Filter.create(query_two) + +    response = +      conn +      |> assign(:user, user) +      |> get("/api/v1/filters") +      |> json_response(200) + +    assert response == +             render_json( +               FilterView, +               "filters.json", +               filters: [filter_two, filter_one] +             ) +  end + +  test "get a filter", %{conn: conn} do +    user = insert(:user) + +    query = %Pleroma.Filter{ +      user_id: user.id, +      filter_id: 2, +      phrase: "knight", +      context: ["home"] +    } + +    {:ok, filter} = Pleroma.Filter.create(query) + +    conn = +      conn +      |> assign(:user, user) +      |> get("/api/v1/filters/#{filter.filter_id}") + +    assert _response = json_response(conn, 200) +  end + +  test "update a filter", %{conn: conn} do +    user = insert(:user) + +    query = %Pleroma.Filter{ +      user_id: user.id, +      filter_id: 2, +      phrase: "knight", +      context: ["home"] +    } + +    {:ok, _filter} = Pleroma.Filter.create(query) + +    new = %Pleroma.Filter{ +      phrase: "nii", +      context: ["home"] +    } + +    conn = +      conn +      |> assign(:user, user) +      |> put("/api/v1/filters/#{query.filter_id}", %{ +        phrase: new.phrase, +        context: new.context +      }) + +    assert response = json_response(conn, 200) +    assert response["phrase"] == new.phrase +    assert response["context"] == new.context +  end + +  test "delete a filter", %{conn: conn} do +    user = insert(:user) + +    query = %Pleroma.Filter{ +      user_id: user.id, +      filter_id: 2, +      phrase: "knight", +      context: ["home"] +    } + +    {:ok, filter} = Pleroma.Filter.create(query) + +    conn = +      conn +      |> assign(:user, user) +      |> delete("/api/v1/filters/#{filter.filter_id}") + +    assert response = json_response(conn, 200) +    assert response == %{} +  end +end diff --git a/test/web/mastodon_api/controllers/follow_request_controller_test.exs b/test/web/mastodon_api/controllers/follow_request_controller_test.exs new file mode 100644 index 000000000..4bf292df5 --- /dev/null +++ b/test/web/mastodon_api/controllers/follow_request_controller_test.exs @@ -0,0 +1,81 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.FollowRequestControllerTest do +  use Pleroma.Web.ConnCase + +  alias Pleroma.User +  alias Pleroma.Web.ActivityPub.ActivityPub + +  import Pleroma.Factory + +  describe "locked accounts" do +    test "/api/v1/follow_requests works" do +      user = insert(:user, %{info: %User.Info{locked: true}}) +      other_user = insert(:user) + +      {:ok, _activity} = ActivityPub.follow(other_user, user) + +      user = User.get_cached_by_id(user.id) +      other_user = User.get_cached_by_id(other_user.id) + +      assert User.following?(other_user, user) == false + +      conn = +        build_conn() +        |> assign(:user, user) +        |> get("/api/v1/follow_requests") + +      assert [relationship] = json_response(conn, 200) +      assert to_string(other_user.id) == relationship["id"] +    end + +    test "/api/v1/follow_requests/:id/authorize works" do +      user = insert(:user, %{info: %User.Info{locked: true}}) +      other_user = insert(:user) + +      {:ok, _activity} = ActivityPub.follow(other_user, user) + +      user = User.get_cached_by_id(user.id) +      other_user = User.get_cached_by_id(other_user.id) + +      assert User.following?(other_user, user) == false + +      conn = +        build_conn() +        |> assign(:user, user) +        |> post("/api/v1/follow_requests/#{other_user.id}/authorize") + +      assert relationship = json_response(conn, 200) +      assert to_string(other_user.id) == relationship["id"] + +      user = User.get_cached_by_id(user.id) +      other_user = User.get_cached_by_id(other_user.id) + +      assert User.following?(other_user, user) == true +    end + +    test "/api/v1/follow_requests/:id/reject works" do +      user = insert(:user, %{info: %User.Info{locked: true}}) +      other_user = insert(:user) + +      {:ok, _activity} = ActivityPub.follow(other_user, user) + +      user = User.get_cached_by_id(user.id) + +      conn = +        build_conn() +        |> assign(:user, user) +        |> post("/api/v1/follow_requests/#{other_user.id}/reject") + +      assert relationship = json_response(conn, 200) +      assert to_string(other_user.id) == relationship["id"] + +      user = User.get_cached_by_id(user.id) +      other_user = User.get_cached_by_id(other_user.id) + +      assert User.following?(other_user, user) == false +    end +  end +end diff --git a/test/web/mastodon_api/controllers/notification_controller_test.exs b/test/web/mastodon_api/controllers/notification_controller_test.exs new file mode 100644 index 000000000..e4137e92c --- /dev/null +++ b/test/web/mastodon_api/controllers/notification_controller_test.exs @@ -0,0 +1,299 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do +  use Pleroma.Web.ConnCase + +  alias Pleroma.Notification +  alias Pleroma.Repo +  alias Pleroma.User +  alias Pleroma.Web.CommonAPI + +  import Pleroma.Factory + +  test "list of notifications", %{conn: conn} do +    user = insert(:user) +    other_user = insert(:user) + +    {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"}) + +    {:ok, [_notification]} = Notification.create_notifications(activity) + +    conn = +      conn +      |> assign(:user, user) +      |> get("/api/v1/notifications") + +    expected_response = +      "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{ +        user.ap_id +      }\" rel=\"ugc\">@<span>#{user.nickname}</span></a></span>" + +    assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200) +    assert response == expected_response +  end + +  test "getting a single notification", %{conn: conn} do +    user = insert(:user) +    other_user = insert(:user) + +    {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"}) + +    {:ok, [notification]} = Notification.create_notifications(activity) + +    conn = +      conn +      |> assign(:user, user) +      |> get("/api/v1/notifications/#{notification.id}") + +    expected_response = +      "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{ +        user.ap_id +      }\" rel=\"ugc\">@<span>#{user.nickname}</span></a></span>" + +    assert %{"status" => %{"content" => response}} = json_response(conn, 200) +    assert response == expected_response +  end + +  test "dismissing a single notification", %{conn: conn} do +    user = insert(:user) +    other_user = insert(:user) + +    {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"}) + +    {:ok, [notification]} = Notification.create_notifications(activity) + +    conn = +      conn +      |> assign(:user, user) +      |> post("/api/v1/notifications/dismiss", %{"id" => notification.id}) + +    assert %{} = json_response(conn, 200) +  end + +  test "clearing all notifications", %{conn: conn} do +    user = insert(:user) +    other_user = insert(:user) + +    {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"}) + +    {:ok, [_notification]} = Notification.create_notifications(activity) + +    conn = +      conn +      |> assign(:user, user) +      |> post("/api/v1/notifications/clear") + +    assert %{} = json_response(conn, 200) + +    conn = +      build_conn() +      |> assign(:user, user) +      |> get("/api/v1/notifications") + +    assert all = json_response(conn, 200) +    assert all == [] +  end + +  test "paginates notifications using min_id, since_id, max_id, and limit", %{conn: conn} do +    user = insert(:user) +    other_user = insert(:user) + +    {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"}) +    {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"}) +    {:ok, activity3} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"}) +    {:ok, activity4} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"}) + +    notification1_id = get_notification_id_by_activity(activity1) +    notification2_id = get_notification_id_by_activity(activity2) +    notification3_id = get_notification_id_by_activity(activity3) +    notification4_id = get_notification_id_by_activity(activity4) + +    conn = assign(conn, :user, user) + +    # min_id +    result = +      conn +      |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}") +      |> json_response(:ok) + +    assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result + +    # since_id +    result = +      conn +      |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}") +      |> json_response(:ok) + +    assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result + +    # max_id +    result = +      conn +      |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}") +      |> json_response(:ok) + +    assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result +  end + +  test "filters notifications using exclude_types", %{conn: conn} do +    user = insert(:user) +    other_user = insert(:user) + +    {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"}) +    {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"}) +    {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user) +    {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user) +    {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user) + +    mention_notification_id = get_notification_id_by_activity(mention_activity) +    favorite_notification_id = get_notification_id_by_activity(favorite_activity) +    reblog_notification_id = get_notification_id_by_activity(reblog_activity) +    follow_notification_id = get_notification_id_by_activity(follow_activity) + +    conn = assign(conn, :user, user) + +    conn_res = +      get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]}) + +    assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200) + +    conn_res = +      get(conn, "/api/v1/notifications", %{exclude_types: ["favourite", "reblog", "follow"]}) + +    assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200) + +    conn_res = +      get(conn, "/api/v1/notifications", %{exclude_types: ["reblog", "follow", "mention"]}) + +    assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200) + +    conn_res = +      get(conn, "/api/v1/notifications", %{exclude_types: ["follow", "mention", "favourite"]}) + +    assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200) +  end + +  test "destroy multiple", %{conn: conn} do +    user = insert(:user) +    other_user = insert(:user) + +    {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"}) +    {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"}) +    {:ok, activity3} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"}) +    {:ok, activity4} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"}) + +    notification1_id = get_notification_id_by_activity(activity1) +    notification2_id = get_notification_id_by_activity(activity2) +    notification3_id = get_notification_id_by_activity(activity3) +    notification4_id = get_notification_id_by_activity(activity4) + +    conn = assign(conn, :user, user) + +    result = +      conn +      |> get("/api/v1/notifications") +      |> json_response(:ok) + +    assert [%{"id" => ^notification2_id}, %{"id" => ^notification1_id}] = result + +    conn2 = +      conn +      |> assign(:user, other_user) + +    result = +      conn2 +      |> get("/api/v1/notifications") +      |> json_response(:ok) + +    assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result + +    conn_destroy = +      conn +      |> delete("/api/v1/notifications/destroy_multiple", %{ +        "ids" => [notification1_id, notification2_id] +      }) + +    assert json_response(conn_destroy, 200) == %{} + +    result = +      conn2 +      |> get("/api/v1/notifications") +      |> json_response(:ok) + +    assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result +  end + +  test "doesn't see notifications after muting user with notifications", %{conn: conn} do +    user = insert(:user) +    user2 = insert(:user) + +    {:ok, _, _, _} = CommonAPI.follow(user, user2) +    {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"}) + +    conn = assign(conn, :user, user) + +    conn = get(conn, "/api/v1/notifications") + +    assert length(json_response(conn, 200)) == 1 + +    {:ok, user} = User.mute(user, user2) + +    conn = assign(build_conn(), :user, user) +    conn = get(conn, "/api/v1/notifications") + +    assert json_response(conn, 200) == [] +  end + +  test "see notifications after muting user without notifications", %{conn: conn} do +    user = insert(:user) +    user2 = insert(:user) + +    {:ok, _, _, _} = CommonAPI.follow(user, user2) +    {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"}) + +    conn = assign(conn, :user, user) + +    conn = get(conn, "/api/v1/notifications") + +    assert length(json_response(conn, 200)) == 1 + +    {:ok, user} = User.mute(user, user2, false) + +    conn = assign(build_conn(), :user, user) +    conn = get(conn, "/api/v1/notifications") + +    assert length(json_response(conn, 200)) == 1 +  end + +  test "see notifications after muting user with notifications and with_muted parameter", %{ +    conn: conn +  } do +    user = insert(:user) +    user2 = insert(:user) + +    {:ok, _, _, _} = CommonAPI.follow(user, user2) +    {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"}) + +    conn = assign(conn, :user, user) + +    conn = get(conn, "/api/v1/notifications") + +    assert length(json_response(conn, 200)) == 1 + +    {:ok, user} = User.mute(user, user2) + +    conn = assign(build_conn(), :user, user) +    conn = get(conn, "/api/v1/notifications", %{"with_muted" => "true"}) + +    assert length(json_response(conn, 200)) == 1 +  end + +  defp get_notification_id_by_activity(%{id: id}) do +    Notification +    |> Repo.get_by(activity_id: id) +    |> Map.get(:id) +    |> to_string() +  end +end diff --git a/test/web/mastodon_api/controllers/report_controller_test.exs b/test/web/mastodon_api/controllers/report_controller_test.exs new file mode 100644 index 000000000..979ca48f3 --- /dev/null +++ b/test/web/mastodon_api/controllers/report_controller_test.exs @@ -0,0 +1,88 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.ReportControllerTest do +  use Pleroma.Web.ConnCase + +  alias Pleroma.Web.CommonAPI + +  import Pleroma.Factory + +  setup do +    reporter = insert(:user) +    target_user = insert(:user) + +    {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"}) + +    [reporter: reporter, target_user: target_user, activity: activity] +  end + +  test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do +    assert %{"action_taken" => false, "id" => _} = +             conn +             |> assign(:user, reporter) +             |> post("/api/v1/reports", %{"account_id" => target_user.id}) +             |> json_response(200) +  end + +  test "submit a report with statuses and comment", %{ +    conn: conn, +    reporter: reporter, +    target_user: target_user, +    activity: activity +  } do +    assert %{"action_taken" => false, "id" => _} = +             conn +             |> assign(:user, reporter) +             |> post("/api/v1/reports", %{ +               "account_id" => target_user.id, +               "status_ids" => [activity.id], +               "comment" => "bad status!", +               "forward" => "false" +             }) +             |> json_response(200) +  end + +  test "account_id is required", %{ +    conn: conn, +    reporter: reporter, +    activity: activity +  } do +    assert %{"error" => "Valid `account_id` required"} = +             conn +             |> assign(:user, reporter) +             |> post("/api/v1/reports", %{"status_ids" => [activity.id]}) +             |> json_response(400) +  end + +  test "comment must be up to the size specified in the config", %{ +    conn: conn, +    reporter: reporter, +    target_user: target_user +  } do +    max_size = Pleroma.Config.get([:instance, :max_report_comment_size], 1000) +    comment = String.pad_trailing("a", max_size + 1, "a") + +    error = %{"error" => "Comment must be up to #{max_size} characters"} + +    assert ^error = +             conn +             |> assign(:user, reporter) +             |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment}) +             |> json_response(400) +  end + +  test "returns error when account is not exist", %{ +    conn: conn, +    reporter: reporter, +    activity: activity +  } do +    conn = +      conn +      |> assign(:user, reporter) +      |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"}) + +    assert json_response(conn, 400) == %{"error" => "Account not found"} +  end +end diff --git a/test/web/mastodon_api/controllers/scheduled_activity_controller_test.exs b/test/web/mastodon_api/controllers/scheduled_activity_controller_test.exs new file mode 100644 index 000000000..9ad6a4fa7 --- /dev/null +++ b/test/web/mastodon_api/controllers/scheduled_activity_controller_test.exs @@ -0,0 +1,113 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.ScheduledActivityControllerTest do +  use Pleroma.Web.ConnCase, async: true + +  alias Pleroma.Repo +  alias Pleroma.ScheduledActivity + +  import Pleroma.Factory + +  test "shows scheduled activities", %{conn: conn} do +    user = insert(:user) +    scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string() +    scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string() +    scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string() +    scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string() + +    conn = +      conn +      |> assign(:user, user) + +    # min_id +    conn_res = +      conn +      |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}") + +    result = json_response(conn_res, 200) +    assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result + +    # since_id +    conn_res = +      conn +      |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}") + +    result = json_response(conn_res, 200) +    assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result + +    # max_id +    conn_res = +      conn +      |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}") + +    result = json_response(conn_res, 200) +    assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result +  end + +  test "shows a scheduled activity", %{conn: conn} do +    user = insert(:user) +    scheduled_activity = insert(:scheduled_activity, user: user) + +    res_conn = +      conn +      |> assign(:user, user) +      |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}") + +    assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200) +    assert scheduled_activity_id == scheduled_activity.id |> to_string() + +    res_conn = +      conn +      |> assign(:user, user) +      |> get("/api/v1/scheduled_statuses/404") + +    assert %{"error" => "Record not found"} = json_response(res_conn, 404) +  end + +  test "updates a scheduled activity", %{conn: conn} do +    user = insert(:user) +    scheduled_activity = insert(:scheduled_activity, user: user) + +    new_scheduled_at = +      NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond) + +    res_conn = +      conn +      |> assign(:user, user) +      |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{ +        scheduled_at: new_scheduled_at +      }) + +    assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200) +    assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at) + +    res_conn = +      conn +      |> assign(:user, user) +      |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at}) + +    assert %{"error" => "Record not found"} = json_response(res_conn, 404) +  end + +  test "deletes a scheduled activity", %{conn: conn} do +    user = insert(:user) +    scheduled_activity = insert(:scheduled_activity, user: user) + +    res_conn = +      conn +      |> assign(:user, user) +      |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}") + +    assert %{} = json_response(res_conn, 200) +    assert nil == Repo.get(ScheduledActivity, scheduled_activity.id) + +    res_conn = +      conn +      |> assign(:user, user) +      |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}") + +    assert %{"error" => "Record not found"} = json_response(res_conn, 404) +  end +end 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..b194feae6 --- /dev/null +++ b/test/web/mastodon_api/controllers/status_controller_test.exs @@ -0,0 +1,1210 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# 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.Repo +  alias Pleroma.ScheduledActivity +  alias Pleroma.User +  alias Pleroma.Web.ActivityPub.ActivityPub +  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 "posting an undefined status with an attachment", %{conn: conn} do +      user = insert(:user) + +      file = %Plug.Upload{ +        content_type: "image/jpg", +        path: Path.absname("test/fixtures/image.jpg"), +        filename: "an_image.jpg" +      } + +      {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id) + +      conn = +        conn +        |> assign(:user, user) +        |> post("/api/v1/statuses", %{ +          "media_ids" => [to_string(upload.id)] +        }) + +      assert json_response(conn, 200) +    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 scheduled statuses" do +    test "creates a scheduled activity", %{conn: conn} do +      user = insert(:user) +      scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond) + +      conn = +        conn +        |> assign(:user, user) +        |> post("/api/v1/statuses", %{ +          "status" => "scheduled", +          "scheduled_at" => scheduled_at +        }) + +      assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200) +      assert expected_scheduled_at == CommonAPI.Utils.to_masto_date(scheduled_at) +      assert [] == Repo.all(Activity) +    end + +    test "creates a scheduled activity with a media attachment", %{conn: conn} do +      user = insert(:user) +      scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond) + +      file = %Plug.Upload{ +        content_type: "image/jpg", +        path: Path.absname("test/fixtures/image.jpg"), +        filename: "an_image.jpg" +      } + +      {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id) + +      conn = +        conn +        |> assign(:user, user) +        |> post("/api/v1/statuses", %{ +          "media_ids" => [to_string(upload.id)], +          "status" => "scheduled", +          "scheduled_at" => scheduled_at +        }) + +      assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200) +      assert %{"type" => "image"} = media_attachment +    end + +    test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now", +         %{conn: conn} do +      user = insert(:user) + +      scheduled_at = +        NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond) + +      conn = +        conn +        |> assign(:user, user) +        |> post("/api/v1/statuses", %{ +          "status" => "not scheduled", +          "scheduled_at" => scheduled_at +        }) + +      assert %{"content" => "not scheduled"} = json_response(conn, 200) +      assert [] == Repo.all(ScheduledActivity) +    end + +    test "returns error when daily user limit is exceeded", %{conn: conn} do +      user = insert(:user) + +      today = +        NaiveDateTime.utc_now() +        |> NaiveDateTime.add(:timer.minutes(6), :millisecond) +        |> NaiveDateTime.to_iso8601() + +      attrs = %{params: %{}, scheduled_at: today} +      {:ok, _} = ScheduledActivity.create(user, attrs) +      {:ok, _} = ScheduledActivity.create(user, attrs) + +      conn = +        conn +        |> assign(:user, user) +        |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today}) + +      assert %{"error" => "daily limit exceeded"} == json_response(conn, 422) +    end + +    test "returns error when total user limit is exceeded", %{conn: conn} do +      user = insert(:user) + +      today = +        NaiveDateTime.utc_now() +        |> NaiveDateTime.add(:timer.minutes(6), :millisecond) +        |> NaiveDateTime.to_iso8601() + +      tomorrow = +        NaiveDateTime.utc_now() +        |> NaiveDateTime.add(:timer.hours(36), :millisecond) +        |> NaiveDateTime.to_iso8601() + +      attrs = %{params: %{}, scheduled_at: today} +      {:ok, _} = ScheduledActivity.create(user, attrs) +      {:ok, _} = ScheduledActivity.create(user, attrs) +      {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow}) + +      conn = +        conn +        |> assign(:user, user) +        |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow}) + +      assert %{"error" => "total limit exceeded"} == json_response(conn, 422) +    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 + +  test "context" do +    user = insert(:user) + +    {:ok, %{id: id1}} = CommonAPI.post(user, %{"status" => "1"}) +    {:ok, %{id: id2}} = CommonAPI.post(user, %{"status" => "2", "in_reply_to_status_id" => id1}) +    {:ok, %{id: id3}} = CommonAPI.post(user, %{"status" => "3", "in_reply_to_status_id" => id2}) +    {:ok, %{id: id4}} = CommonAPI.post(user, %{"status" => "4", "in_reply_to_status_id" => id3}) +    {:ok, %{id: id5}} = CommonAPI.post(user, %{"status" => "5", "in_reply_to_status_id" => id4}) + +    response = +      build_conn() +      |> assign(:user, nil) +      |> get("/api/v1/statuses/#{id3}/context") +      |> json_response(:ok) + +    assert %{ +             "ancestors" => [%{"id" => ^id1}, %{"id" => ^id2}], +             "descendants" => [%{"id" => ^id4}, %{"id" => ^id5}] +           } = response +  end +end diff --git a/test/web/mastodon_api/controllers/timeline_controller_test.exs b/test/web/mastodon_api/controllers/timeline_controller_test.exs new file mode 100644 index 000000000..d3652d964 --- /dev/null +++ b/test/web/mastodon_api/controllers/timeline_controller_test.exs @@ -0,0 +1,291 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do +  use Pleroma.Web.ConnCase + +  import Pleroma.Factory +  import Tesla.Mock + +  alias Pleroma.Config +  alias Pleroma.User +  alias Pleroma.Web.CommonAPI +  alias Pleroma.Web.OStatus + +  clear_config([:instance, :public]) + +  setup do +    mock(fn env -> apply(HttpRequestMock, :request, [env]) end) +    :ok +  end + +  test "the home timeline", %{conn: conn} do +    user = insert(:user) +    following = insert(:user) + +    {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"}) + +    conn = +      conn +      |> assign(:user, user) +      |> get("/api/v1/timelines/home") + +    assert Enum.empty?(json_response(conn, :ok)) + +    {:ok, user} = User.follow(user, following) + +    conn = +      build_conn() +      |> assign(:user, user) +      |> get("/api/v1/timelines/home") + +    assert [%{"content" => "test"}] = json_response(conn, :ok) +  end + +  describe "public" do +    @tag capture_log: true +    test "the public timeline", %{conn: conn} do +      following = insert(:user) + +      {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"}) + +      {:ok, [_activity]} = +        OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873") + +      conn = get(conn, "/api/v1/timelines/public", %{"local" => "False"}) + +      assert length(json_response(conn, :ok)) == 2 + +      conn = get(build_conn(), "/api/v1/timelines/public", %{"local" => "True"}) + +      assert [%{"content" => "test"}] = json_response(conn, :ok) + +      conn = get(build_conn(), "/api/v1/timelines/public", %{"local" => "1"}) + +      assert [%{"content" => "test"}] = json_response(conn, :ok) +    end + +    test "the public timeline when public is set to false", %{conn: conn} do +      Config.put([:instance, :public], false) + +      assert %{"error" => "This resource requires authentication."} == +               conn +               |> get("/api/v1/timelines/public", %{"local" => "False"}) +               |> json_response(:forbidden) +    end + +    test "the public timeline includes only public statuses for an authenticated user" do +      user = insert(:user) + +      conn = +        build_conn() +        |> assign(:user, user) + +      {:ok, _activity} = CommonAPI.post(user, %{"status" => "test"}) +      {:ok, _activity} = CommonAPI.post(user, %{"status" => "test", "visibility" => "private"}) +      {:ok, _activity} = CommonAPI.post(user, %{"status" => "test", "visibility" => "unlisted"}) +      {:ok, _activity} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"}) + +      res_conn = get(conn, "/api/v1/timelines/public") +      assert length(json_response(res_conn, 200)) == 1 +    end +  end + +  describe "direct" do +    test "direct timeline", %{conn: conn} do +      user_one = insert(:user) +      user_two = insert(:user) + +      {:ok, user_two} = User.follow(user_two, user_one) + +      {:ok, direct} = +        CommonAPI.post(user_one, %{ +          "status" => "Hi @#{user_two.nickname}!", +          "visibility" => "direct" +        }) + +      {:ok, _follower_only} = +        CommonAPI.post(user_one, %{ +          "status" => "Hi @#{user_two.nickname}!", +          "visibility" => "private" +        }) + +      # Only direct should be visible here +      res_conn = +        conn +        |> assign(:user, user_two) +        |> get("api/v1/timelines/direct") + +      [status] = json_response(res_conn, :ok) + +      assert %{"visibility" => "direct"} = status +      assert status["url"] != direct.data["id"] + +      # User should be able to see their own direct message +      res_conn = +        build_conn() +        |> assign(:user, user_one) +        |> get("api/v1/timelines/direct") + +      [status] = json_response(res_conn, :ok) + +      assert %{"visibility" => "direct"} = status + +      # Both should be visible here +      res_conn = +        conn +        |> assign(:user, user_two) +        |> get("api/v1/timelines/home") + +      [_s1, _s2] = json_response(res_conn, :ok) + +      # Test pagination +      Enum.each(1..20, fn _ -> +        {:ok, _} = +          CommonAPI.post(user_one, %{ +            "status" => "Hi @#{user_two.nickname}!", +            "visibility" => "direct" +          }) +      end) + +      res_conn = +        conn +        |> assign(:user, user_two) +        |> get("api/v1/timelines/direct") + +      statuses = json_response(res_conn, :ok) +      assert length(statuses) == 20 + +      res_conn = +        conn +        |> assign(:user, user_two) +        |> get("api/v1/timelines/direct", %{max_id: List.last(statuses)["id"]}) + +      [status] = json_response(res_conn, :ok) + +      assert status["url"] != direct.data["id"] +    end + +    test "doesn't include DMs from blocked users", %{conn: conn} do +      blocker = insert(:user) +      blocked = insert(:user) +      user = insert(:user) +      {:ok, blocker} = User.block(blocker, blocked) + +      {:ok, _blocked_direct} = +        CommonAPI.post(blocked, %{ +          "status" => "Hi @#{blocker.nickname}!", +          "visibility" => "direct" +        }) + +      {:ok, direct} = +        CommonAPI.post(user, %{ +          "status" => "Hi @#{blocker.nickname}!", +          "visibility" => "direct" +        }) + +      res_conn = +        conn +        |> assign(:user, user) +        |> get("api/v1/timelines/direct") + +      [status] = json_response(res_conn, :ok) +      assert status["id"] == direct.id +    end +  end + +  describe "list" do +    test "list timeline", %{conn: conn} do +      user = insert(:user) +      other_user = insert(:user) +      {:ok, _activity_one} = CommonAPI.post(user, %{"status" => "Marisa is cute."}) +      {:ok, activity_two} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."}) +      {:ok, list} = Pleroma.List.create("name", user) +      {:ok, list} = Pleroma.List.follow(list, other_user) + +      conn = +        conn +        |> assign(:user, user) +        |> get("/api/v1/timelines/list/#{list.id}") + +      assert [%{"id" => id}] = json_response(conn, :ok) + +      assert id == to_string(activity_two.id) +    end + +    test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do +      user = insert(:user) +      other_user = insert(:user) +      {:ok, activity_one} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."}) + +      {:ok, _activity_two} = +        CommonAPI.post(other_user, %{ +          "status" => "Marisa is cute.", +          "visibility" => "private" +        }) + +      {:ok, list} = Pleroma.List.create("name", user) +      {:ok, list} = Pleroma.List.follow(list, other_user) + +      conn = +        conn +        |> assign(:user, user) +        |> get("/api/v1/timelines/list/#{list.id}") + +      assert [%{"id" => id}] = json_response(conn, :ok) + +      assert id == to_string(activity_one.id) +    end +  end + +  describe "hashtag" do +    @tag capture_log: true +    test "hashtag timeline", %{conn: conn} do +      following = insert(:user) + +      {:ok, activity} = CommonAPI.post(following, %{"status" => "test #2hu"}) + +      {:ok, [_activity]} = +        OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873") + +      nconn = get(conn, "/api/v1/timelines/tag/2hu") + +      assert [%{"id" => id}] = json_response(nconn, :ok) + +      assert id == to_string(activity.id) + +      # works for different capitalization too +      nconn = get(conn, "/api/v1/timelines/tag/2HU") + +      assert [%{"id" => id}] = json_response(nconn, :ok) + +      assert id == to_string(activity.id) +    end + +    test "multi-hashtag timeline", %{conn: conn} do +      user = insert(:user) + +      {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"}) +      {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"}) +      {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"}) + +      any_test = get(conn, "/api/v1/timelines/tag/test", %{"any" => ["test1"]}) + +      [status_none, status_test1, status_test] = json_response(any_test, :ok) + +      assert to_string(activity_test.id) == status_test["id"] +      assert to_string(activity_test1.id) == status_test1["id"] +      assert to_string(activity_none.id) == status_none["id"] + +      restricted_test = +        get(conn, "/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]}) + +      assert [status_test1] == json_response(restricted_test, :ok) + +      all_test = get(conn, "/api/v1/timelines/tag/test", %{"all" => ["none"]}) + +      assert [status_none] == json_response(all_test, :ok) +    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 fb04748bb..feeaf079b 100644 --- a/test/web/mastodon_api/mastodon_api_controller_test.exs +++ b/test/web/mastodon_api/mastodon_api_controller_test.exs @@ -6,28 +6,21 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do    use Pleroma.Web.ConnCase    alias Ecto.Changeset -  alias Pleroma.Activity -  alias Pleroma.ActivityExpiration    alias Pleroma.Config    alias Pleroma.Notification    alias Pleroma.Object    alias Pleroma.Repo -  alias Pleroma.ScheduledActivity    alias Pleroma.Tests.ObanHelpers    alias Pleroma.User    alias Pleroma.Web.ActivityPub.ActivityPub    alias Pleroma.Web.CommonAPI -  alias Pleroma.Web.MastodonAPI.FilterView    alias Pleroma.Web.OAuth.App -  alias Pleroma.Web.OAuth.Token -  alias Pleroma.Web.OStatus    alias Pleroma.Web.Push -  import Pleroma.Factory +    import ExUnit.CaptureLog -  import Tesla.Mock +  import Pleroma.Factory    import Swoosh.TestAssertions - -  @image "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7" +  import Tesla.Mock    setup do      mock(fn env -> apply(HttpRequestMock, :request, [env]) end) @@ -37,561 +30,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do    clear_config([:instance, :public])    clear_config([:rich_media, :enabled]) -  test "the home timeline", %{conn: conn} do -    user = insert(:user) -    following = insert(:user) - -    {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"}) - -    conn = -      conn -      |> assign(:user, user) -      |> get("/api/v1/timelines/home") - -    assert Enum.empty?(json_response(conn, 200)) - -    {:ok, user} = User.follow(user, following) - -    conn = -      build_conn() -      |> assign(:user, user) -      |> get("/api/v1/timelines/home") - -    assert [%{"content" => "test"}] = json_response(conn, 200) -  end - -  test "the public timeline", %{conn: conn} do -    following = insert(:user) - -    capture_log(fn -> -      {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"}) - -      {:ok, [_activity]} = -        OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873") - -      conn = -        conn -        |> get("/api/v1/timelines/public", %{"local" => "False"}) - -      assert length(json_response(conn, 200)) == 2 - -      conn = -        build_conn() -        |> get("/api/v1/timelines/public", %{"local" => "True"}) - -      assert [%{"content" => "test"}] = json_response(conn, 200) - -      conn = -        build_conn() -        |> get("/api/v1/timelines/public", %{"local" => "1"}) - -      assert [%{"content" => "test"}] = json_response(conn, 200) -    end) -  end - -  test "the public timeline when public is set to false", %{conn: conn} do -    Config.put([:instance, :public], false) - -    assert conn -           |> get("/api/v1/timelines/public", %{"local" => "False"}) -           |> json_response(403) == %{"error" => "This resource requires authentication."} -  end - -  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, "visibility" => "direct"} = json_response(conn, 200) -      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 "direct timeline", %{conn: conn} do -    user_one = insert(:user) -    user_two = insert(:user) - -    {:ok, user_two} = User.follow(user_two, user_one) - -    {:ok, direct} = -      CommonAPI.post(user_one, %{ -        "status" => "Hi @#{user_two.nickname}!", -        "visibility" => "direct" -      }) - -    {:ok, _follower_only} = -      CommonAPI.post(user_one, %{ -        "status" => "Hi @#{user_two.nickname}!", -        "visibility" => "private" -      }) - -    # Only direct should be visible here -    res_conn = -      conn -      |> assign(:user, user_two) -      |> get("api/v1/timelines/direct") - -    [status] = json_response(res_conn, 200) - -    assert %{"visibility" => "direct"} = status -    assert status["url"] != direct.data["id"] - -    # User should be able to see their own direct message -    res_conn = -      build_conn() -      |> assign(:user, user_one) -      |> get("api/v1/timelines/direct") - -    [status] = json_response(res_conn, 200) - -    assert %{"visibility" => "direct"} = status - -    # Both should be visible here -    res_conn = -      conn -      |> assign(:user, user_two) -      |> get("api/v1/timelines/home") - -    [_s1, _s2] = json_response(res_conn, 200) - -    # Test pagination -    Enum.each(1..20, fn _ -> -      {:ok, _} = -        CommonAPI.post(user_one, %{ -          "status" => "Hi @#{user_two.nickname}!", -          "visibility" => "direct" -        }) -    end) - -    res_conn = -      conn -      |> assign(:user, user_two) -      |> get("api/v1/timelines/direct") - -    statuses = json_response(res_conn, 200) -    assert length(statuses) == 20 - -    res_conn = -      conn -      |> assign(:user, user_two) -      |> get("api/v1/timelines/direct", %{max_id: List.last(statuses)["id"]}) - -    [status] = json_response(res_conn, 200) - -    assert status["url"] != direct.data["id"] -  end - -  test "Conversations", %{conn: conn} do -    user_one = insert(:user) -    user_two = insert(:user) -    user_three = insert(:user) - -    {:ok, user_two} = User.follow(user_two, user_one) - -    {:ok, direct} = -      CommonAPI.post(user_one, %{ -        "status" => "Hi @#{user_two.nickname}, @#{user_three.nickname}!", -        "visibility" => "direct" -      }) - -    {:ok, _follower_only} = -      CommonAPI.post(user_one, %{ -        "status" => "Hi @#{user_two.nickname}!", -        "visibility" => "private" -      }) - -    res_conn = -      conn -      |> assign(:user, user_one) -      |> get("/api/v1/conversations") - -    assert response = json_response(res_conn, 200) - -    assert [ -             %{ -               "id" => res_id, -               "accounts" => res_accounts, -               "last_status" => res_last_status, -               "unread" => unread -             } -           ] = response - -    account_ids = Enum.map(res_accounts, & &1["id"]) -    assert length(res_accounts) == 2 -    assert user_two.id in account_ids -    assert user_three.id in account_ids -    assert is_binary(res_id) -    assert unread == true -    assert res_last_status["id"] == direct.id - -    # Apparently undocumented API endpoint -    res_conn = -      conn -      |> assign(:user, user_one) -      |> post("/api/v1/conversations/#{res_id}/read") - -    assert response = json_response(res_conn, 200) -    assert length(response["accounts"]) == 2 -    assert response["last_status"]["id"] == direct.id -    assert response["unread"] == false - -    # (vanilla) Mastodon frontend behaviour -    res_conn = -      conn -      |> assign(:user, user_one) -      |> get("/api/v1/statuses/#{res_last_status["id"]}/context") - -    assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200) -  end - -  test "doesn't include DMs from blocked users", %{conn: conn} do -    blocker = insert(:user) -    blocked = insert(:user) -    user = insert(:user) -    {:ok, blocker} = User.block(blocker, blocked) - -    {:ok, _blocked_direct} = -      CommonAPI.post(blocked, %{ -        "status" => "Hi @#{blocker.nickname}!", -        "visibility" => "direct" -      }) - -    {:ok, direct} = -      CommonAPI.post(user, %{ -        "status" => "Hi @#{blocker.nickname}!", -        "visibility" => "direct" -      }) - -    res_conn = -      conn -      |> assign(:user, user) -      |> get("api/v1/timelines/direct") - -    [status] = json_response(res_conn, 200) -    assert status["id"] == direct.id -  end - -  test "verify_credentials", %{conn: conn} do -    user = insert(:user) - -    conn = -      conn -      |> assign(:user, user) -      |> get("/api/v1/accounts/verify_credentials") - -    response = json_response(conn, 200) - -    assert %{"id" => id, "source" => %{"privacy" => "public"}} = response -    assert response["pleroma"]["chat_token"] -    assert id == to_string(user.id) -  end - -  test "verify_credentials default scope unlisted", %{conn: conn} do -    user = insert(:user, %{info: %User.Info{default_scope: "unlisted"}}) - -    conn = -      conn -      |> assign(:user, user) -      |> get("/api/v1/accounts/verify_credentials") - -    assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200) -    assert id == to_string(user.id) -  end -    test "apps/verify_credentials", %{conn: conn} do      token = insert(:oauth_token) @@ -612,101 +50,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do      assert expected == json_response(conn, 200)    end -  test "user avatar can be set", %{conn: conn} do -    user = insert(:user) -    avatar_image = File.read!("test/fixtures/avatar_data_uri") - -    conn = -      conn -      |> assign(:user, user) -      |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: avatar_image}) - -    user = refresh_record(user) - -    assert %{ -             "name" => _, -             "type" => _, -             "url" => [ -               %{ -                 "href" => _, -                 "mediaType" => _, -                 "type" => _ -               } -             ] -           } = user.avatar - -    assert %{"url" => _} = json_response(conn, 200) -  end - -  test "user avatar can be reset", %{conn: conn} do -    user = insert(:user) - -    conn = -      conn -      |> assign(:user, user) -      |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: ""}) - -    user = User.get_cached_by_id(user.id) - -    assert user.avatar == nil - -    assert %{"url" => nil} = json_response(conn, 200) -  end - -  test "can set profile banner", %{conn: conn} do -    user = insert(:user) - -    conn = -      conn -      |> assign(:user, user) -      |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => @image}) - -    user = refresh_record(user) -    assert user.info.banner["type"] == "Image" - -    assert %{"url" => _} = json_response(conn, 200) -  end - -  test "can reset profile banner", %{conn: conn} do -    user = insert(:user) - -    conn = -      conn -      |> assign(:user, user) -      |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => ""}) - -    user = refresh_record(user) -    assert user.info.banner == %{} - -    assert %{"url" => nil} = json_response(conn, 200) -  end - -  test "background image can be set", %{conn: conn} do -    user = insert(:user) - -    conn = -      conn -      |> assign(:user, user) -      |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => @image}) - -    user = refresh_record(user) -    assert user.info.background["type"] == "Image" -    assert %{"url" => _} = json_response(conn, 200) -  end - -  test "background image can be reset", %{conn: conn} do -    user = insert(:user) - -    conn = -      conn -      |> assign(:user, user) -      |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => ""}) - -    user = refresh_record(user) -    assert user.info.background == %{} -    assert %{"url" => nil} = json_response(conn, 200) -  end -    test "creates an oauth app", %{conn: conn} do      user = insert(:user)      app_attrs = build(:oauth_app) @@ -734,836 +77,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) - -      filter = %Pleroma.Filter{ -        phrase: "knights", -        context: ["home"] -      } - -      conn = -        conn -        |> assign(:user, user) -        |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context}) - -      assert response = json_response(conn, 200) -      assert response["phrase"] == filter.phrase -      assert response["context"] == filter.context -      assert response["irreversible"] == false -      assert response["id"] != nil -      assert response["id"] != "" -    end - -    test "fetching a list of filters", %{conn: conn} do -      user = insert(:user) - -      query_one = %Pleroma.Filter{ -        user_id: user.id, -        filter_id: 1, -        phrase: "knights", -        context: ["home"] -      } - -      query_two = %Pleroma.Filter{ -        user_id: user.id, -        filter_id: 2, -        phrase: "who", -        context: ["home"] -      } - -      {:ok, filter_one} = Pleroma.Filter.create(query_one) -      {:ok, filter_two} = Pleroma.Filter.create(query_two) - -      response = -        conn -        |> assign(:user, user) -        |> get("/api/v1/filters") -        |> json_response(200) - -      assert response == -               render_json( -                 FilterView, -                 "filters.json", -                 filters: [filter_two, filter_one] -               ) -    end - -    test "get a filter", %{conn: conn} do -      user = insert(:user) - -      query = %Pleroma.Filter{ -        user_id: user.id, -        filter_id: 2, -        phrase: "knight", -        context: ["home"] -      } - -      {:ok, filter} = Pleroma.Filter.create(query) - -      conn = -        conn -        |> assign(:user, user) -        |> get("/api/v1/filters/#{filter.filter_id}") - -      assert _response = json_response(conn, 200) -    end - -    test "update a filter", %{conn: conn} do -      user = insert(:user) - -      query = %Pleroma.Filter{ -        user_id: user.id, -        filter_id: 2, -        phrase: "knight", -        context: ["home"] -      } - -      {:ok, _filter} = Pleroma.Filter.create(query) - -      new = %Pleroma.Filter{ -        phrase: "nii", -        context: ["home"] -      } - -      conn = -        conn -        |> assign(:user, user) -        |> put("/api/v1/filters/#{query.filter_id}", %{ -          phrase: new.phrase, -          context: new.context -        }) - -      assert response = json_response(conn, 200) -      assert response["phrase"] == new.phrase -      assert response["context"] == new.context -    end - -    test "delete a filter", %{conn: conn} do -      user = insert(:user) - -      query = %Pleroma.Filter{ -        user_id: user.id, -        filter_id: 2, -        phrase: "knight", -        context: ["home"] -      } - -      {:ok, filter} = Pleroma.Filter.create(query) - -      conn = -        conn -        |> assign(:user, user) -        |> delete("/api/v1/filters/#{filter.filter_id}") - -      assert response = json_response(conn, 200) -      assert response == %{} -    end -  end - -  describe "list timelines" do -    test "list timeline", %{conn: conn} do -      user = insert(:user) -      other_user = insert(:user) -      {:ok, _activity_one} = CommonAPI.post(user, %{"status" => "Marisa is cute."}) -      {:ok, activity_two} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."}) -      {:ok, list} = Pleroma.List.create("name", user) -      {:ok, list} = Pleroma.List.follow(list, other_user) - -      conn = -        conn -        |> assign(:user, user) -        |> get("/api/v1/timelines/list/#{list.id}") - -      assert [%{"id" => id}] = json_response(conn, 200) - -      assert id == to_string(activity_two.id) -    end - -    test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do -      user = insert(:user) -      other_user = insert(:user) -      {:ok, activity_one} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."}) - -      {:ok, _activity_two} = -        CommonAPI.post(other_user, %{ -          "status" => "Marisa is cute.", -          "visibility" => "private" -        }) - -      {:ok, list} = Pleroma.List.create("name", user) -      {:ok, list} = Pleroma.List.follow(list, other_user) - -      conn = -        conn -        |> assign(:user, user) -        |> get("/api/v1/timelines/list/#{list.id}") - -      assert [%{"id" => id}] = json_response(conn, 200) - -      assert id == to_string(activity_one.id) -    end -  end - -  describe "notifications" do -    test "list of notifications", %{conn: conn} do -      user = insert(:user) -      other_user = insert(:user) - -      {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"}) - -      {:ok, [_notification]} = Notification.create_notifications(activity) - -      conn = -        conn -        |> assign(:user, user) -        |> get("/api/v1/notifications") - -      expected_response = -        "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{ -          user.ap_id -        }\">@<span>#{user.nickname}</span></a></span>" - -      assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200) -      assert response == expected_response -    end - -    test "getting a single notification", %{conn: conn} do -      user = insert(:user) -      other_user = insert(:user) - -      {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"}) - -      {:ok, [notification]} = Notification.create_notifications(activity) - -      conn = -        conn -        |> assign(:user, user) -        |> get("/api/v1/notifications/#{notification.id}") - -      expected_response = -        "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{ -          user.ap_id -        }\">@<span>#{user.nickname}</span></a></span>" - -      assert %{"status" => %{"content" => response}} = json_response(conn, 200) -      assert response == expected_response -    end - -    test "dismissing a single notification", %{conn: conn} do -      user = insert(:user) -      other_user = insert(:user) - -      {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"}) - -      {:ok, [notification]} = Notification.create_notifications(activity) - -      conn = -        conn -        |> assign(:user, user) -        |> post("/api/v1/notifications/dismiss", %{"id" => notification.id}) - -      assert %{} = json_response(conn, 200) -    end - -    test "clearing all notifications", %{conn: conn} do -      user = insert(:user) -      other_user = insert(:user) - -      {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"}) - -      {:ok, [_notification]} = Notification.create_notifications(activity) - -      conn = -        conn -        |> assign(:user, user) -        |> post("/api/v1/notifications/clear") - -      assert %{} = json_response(conn, 200) - -      conn = -        build_conn() -        |> assign(:user, user) -        |> get("/api/v1/notifications") - -      assert all = json_response(conn, 200) -      assert all == [] -    end - -    test "paginates notifications using min_id, since_id, max_id, and limit", %{conn: conn} do -      user = insert(:user) -      other_user = insert(:user) - -      {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"}) -      {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"}) -      {:ok, activity3} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"}) -      {:ok, activity4} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"}) - -      notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string() -      notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string() -      notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string() -      notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string() - -      conn = -        conn -        |> assign(:user, user) - -      # min_id -      conn_res = -        conn -        |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}") - -      result = json_response(conn_res, 200) -      assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result - -      # since_id -      conn_res = -        conn -        |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}") - -      result = json_response(conn_res, 200) -      assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result - -      # max_id -      conn_res = -        conn -        |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}") - -      result = json_response(conn_res, 200) -      assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result -    end - -    test "filters notifications using exclude_types", %{conn: conn} do -      user = insert(:user) -      other_user = insert(:user) - -      {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"}) -      {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"}) -      {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user) -      {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user) -      {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user) - -      mention_notification_id = -        Repo.get_by(Notification, activity_id: mention_activity.id).id |> to_string() - -      favorite_notification_id = -        Repo.get_by(Notification, activity_id: favorite_activity.id).id |> to_string() - -      reblog_notification_id = -        Repo.get_by(Notification, activity_id: reblog_activity.id).id |> to_string() - -      follow_notification_id = -        Repo.get_by(Notification, activity_id: follow_activity.id).id |> to_string() - -      conn = -        conn -        |> assign(:user, user) - -      conn_res = -        get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]}) - -      assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200) - -      conn_res = -        get(conn, "/api/v1/notifications", %{exclude_types: ["favourite", "reblog", "follow"]}) - -      assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200) - -      conn_res = -        get(conn, "/api/v1/notifications", %{exclude_types: ["reblog", "follow", "mention"]}) - -      assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200) - -      conn_res = -        get(conn, "/api/v1/notifications", %{exclude_types: ["follow", "mention", "favourite"]}) - -      assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200) -    end - -    test "destroy multiple", %{conn: conn} do -      user = insert(:user) -      other_user = insert(:user) - -      {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"}) -      {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"}) -      {:ok, activity3} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"}) -      {:ok, activity4} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"}) - -      notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string() -      notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string() -      notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string() -      notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string() - -      conn = -        conn -        |> assign(:user, user) - -      conn_res = -        conn -        |> get("/api/v1/notifications") - -      result = json_response(conn_res, 200) -      assert [%{"id" => ^notification2_id}, %{"id" => ^notification1_id}] = result - -      conn2 = -        conn -        |> assign(:user, other_user) - -      conn_res = -        conn2 -        |> get("/api/v1/notifications") - -      result = json_response(conn_res, 200) -      assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result - -      conn_destroy = -        conn -        |> delete("/api/v1/notifications/destroy_multiple", %{ -          "ids" => [notification1_id, notification2_id] -        }) - -      assert json_response(conn_destroy, 200) == %{} - -      conn_res = -        conn2 -        |> get("/api/v1/notifications") - -      result = json_response(conn_res, 200) -      assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result -    end - -    test "doesn't see notifications after muting user with notifications", %{conn: conn} do -      user = insert(:user) -      user2 = insert(:user) - -      {:ok, _, _, _} = CommonAPI.follow(user, user2) -      {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"}) - -      conn = assign(conn, :user, user) - -      conn = get(conn, "/api/v1/notifications") - -      assert length(json_response(conn, 200)) == 1 - -      {:ok, user} = User.mute(user, user2) - -      conn = assign(build_conn(), :user, user) -      conn = get(conn, "/api/v1/notifications") - -      assert json_response(conn, 200) == [] -    end - -    test "see notifications after muting user without notifications", %{conn: conn} do -      user = insert(:user) -      user2 = insert(:user) - -      {:ok, _, _, _} = CommonAPI.follow(user, user2) -      {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"}) - -      conn = assign(conn, :user, user) - -      conn = get(conn, "/api/v1/notifications") - -      assert length(json_response(conn, 200)) == 1 - -      {:ok, user} = User.mute(user, user2, false) - -      conn = assign(build_conn(), :user, user) -      conn = get(conn, "/api/v1/notifications") - -      assert length(json_response(conn, 200)) == 1 -    end - -    test "see notifications after muting user with notifications and with_muted parameter", %{ -      conn: conn -    } do -      user = insert(:user) -      user2 = insert(:user) - -      {:ok, _, _, _} = CommonAPI.follow(user, user2) -      {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"}) - -      conn = assign(conn, :user, user) - -      conn = get(conn, "/api/v1/notifications") - -      assert length(json_response(conn, 200)) == 1 - -      {:ok, user} = User.mute(user, user2) - -      conn = assign(build_conn(), :user, user) -      conn = get(conn, "/api/v1/notifications", %{"with_muted" => "true"}) - -      assert length(json_response(conn, 200)) == 1 -    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) -      user_two = insert(:user) -      user_three = insert(:user) - -      {:ok, user_three} = User.follow(user_three, user_one) - -      {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"}) - -      {:ok, direct_activity} = -        CommonAPI.post(user_one, %{ -          "status" => "Hi, @#{user_two.nickname}.", -          "visibility" => "direct" -        }) - -      {:ok, private_activity} = -        CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"}) - -      resp = -        conn -        |> get("/api/v1/accounts/#{user_one.id}/statuses") - -      assert [%{"id" => id}] = json_response(resp, 200) -      assert id == to_string(activity.id) - -      resp = -        conn -        |> assign(:user, user_two) -        |> get("/api/v1/accounts/#{user_one.id}/statuses") - -      assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200) -      assert id_one == to_string(direct_activity.id) -      assert id_two == to_string(activity.id) - -      resp = -        conn -        |> assign(:user, user_three) -        |> get("/api/v1/accounts/#{user_one.id}/statuses") - -      assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200) -      assert id_one == to_string(private_activity.id) -      assert id_two == to_string(activity.id) -    end - -    test "unimplemented pinned statuses feature", %{conn: conn} do -      note = insert(:note_activity) -      user = User.get_cached_by_ap_id(note.data["actor"]) - -      conn = -        conn -        |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true") - -      assert json_response(conn, 200) == [] -    end - -    test "gets an users media", %{conn: conn} do -      note = insert(:note_activity) -      user = User.get_cached_by_ap_id(note.data["actor"]) - -      file = %Plug.Upload{ -        content_type: "image/jpg", -        path: Path.absname("test/fixtures/image.jpg"), -        filename: "an_image.jpg" -      } - -      {:ok, %{id: media_id}} = ActivityPub.upload(file, actor: user.ap_id) - -      {:ok, image_post} = CommonAPI.post(user, %{"status" => "cofe", "media_ids" => [media_id]}) - -      conn = -        conn -        |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"}) - -      assert [%{"id" => id}] = json_response(conn, 200) -      assert id == to_string(image_post.id) - -      conn = -        build_conn() -        |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"}) - -      assert [%{"id" => id}] = json_response(conn, 200) -      assert id == to_string(image_post.id) -    end - -    test "gets a user's statuses without reblogs", %{conn: conn} do -      user = insert(:user) -      {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"}) -      {:ok, _, _} = CommonAPI.repeat(post.id, user) - -      conn = -        conn -        |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"}) - -      assert [%{"id" => id}] = json_response(conn, 200) -      assert id == to_string(post.id) - -      conn = -        conn -        |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"}) - -      assert [%{"id" => id}] = json_response(conn, 200) -      assert id == to_string(post.id) -    end - -    test "filters user's statuses by a hashtag", %{conn: conn} do -      user = insert(:user) -      {:ok, post} = CommonAPI.post(user, %{"status" => "#hashtag"}) -      {:ok, _post} = CommonAPI.post(user, %{"status" => "hashtag"}) - -      conn = -        conn -        |> get("/api/v1/accounts/#{user.id}/statuses", %{"tagged" => "hashtag"}) - -      assert [%{"id" => id}] = json_response(conn, 200) -      assert id == to_string(post.id) -    end -  end - -  describe "user relationships" do -    test "returns the relationships for the current user", %{conn: conn} do -      user = insert(:user) -      other_user = insert(:user) -      {:ok, user} = User.follow(user, other_user) - -      conn = -        conn -        |> assign(:user, user) -        |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]}) - -      assert [relationship] = json_response(conn, 200) - -      assert to_string(other_user.id) == relationship["id"] -    end -  end -    describe "media upload" do      setup do        user = insert(:user) @@ -1601,618 +114,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do      end    end -  describe "locked accounts" do -    test "/api/v1/follow_requests works" do -      user = insert(:user, %{info: %User.Info{locked: true}}) -      other_user = insert(:user) - -      {:ok, _activity} = ActivityPub.follow(other_user, user) - -      user = User.get_cached_by_id(user.id) -      other_user = User.get_cached_by_id(other_user.id) - -      assert User.following?(other_user, user) == false - -      conn = -        build_conn() -        |> assign(:user, user) -        |> get("/api/v1/follow_requests") - -      assert [relationship] = json_response(conn, 200) -      assert to_string(other_user.id) == relationship["id"] -    end - -    test "/api/v1/follow_requests/:id/authorize works" do -      user = insert(:user, %{info: %User.Info{locked: true}}) -      other_user = insert(:user) - -      {:ok, _activity} = ActivityPub.follow(other_user, user) - -      user = User.get_cached_by_id(user.id) -      other_user = User.get_cached_by_id(other_user.id) - -      assert User.following?(other_user, user) == false - -      conn = -        build_conn() -        |> assign(:user, user) -        |> post("/api/v1/follow_requests/#{other_user.id}/authorize") - -      assert relationship = json_response(conn, 200) -      assert to_string(other_user.id) == relationship["id"] - -      user = User.get_cached_by_id(user.id) -      other_user = User.get_cached_by_id(other_user.id) - -      assert User.following?(other_user, user) == true -    end - -    test "verify_credentials", %{conn: conn} do -      user = insert(:user, %{info: %User.Info{default_scope: "private"}}) - -      conn = -        conn -        |> assign(:user, user) -        |> get("/api/v1/accounts/verify_credentials") - -      assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200) -      assert id == to_string(user.id) -    end - -    test "/api/v1/follow_requests/:id/reject works" do -      user = insert(:user, %{info: %User.Info{locked: true}}) -      other_user = insert(:user) - -      {:ok, _activity} = ActivityPub.follow(other_user, user) - -      user = User.get_cached_by_id(user.id) - -      conn = -        build_conn() -        |> assign(:user, user) -        |> post("/api/v1/follow_requests/#{other_user.id}/reject") - -      assert relationship = json_response(conn, 200) -      assert to_string(other_user.id) == relationship["id"] - -      user = User.get_cached_by_id(user.id) -      other_user = User.get_cached_by_id(other_user.id) - -      assert User.following?(other_user, user) == false -    end -  end - -  describe "account fetching" do -    test "works by id" do -      user = insert(:user) - -      conn = -        build_conn() -        |> get("/api/v1/accounts/#{user.id}") - -      assert %{"id" => id} = json_response(conn, 200) -      assert id == to_string(user.id) - -      conn = -        build_conn() -        |> get("/api/v1/accounts/-1") - -      assert %{"error" => "Can't find user"} = json_response(conn, 404) -    end - -    test "works by nickname" do -      user = insert(:user) - -      conn = -        build_conn() -        |> get("/api/v1/accounts/#{user.nickname}") - -      assert %{"id" => id} = json_response(conn, 200) -      assert id == user.id -    end - -    test "works by nickname for remote users" do -      limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content]) -      Pleroma.Config.put([:instance, :limit_to_local_content], false) -      user = insert(:user, nickname: "user@example.com", local: false) - -      conn = -        build_conn() -        |> get("/api/v1/accounts/#{user.nickname}") - -      Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local) -      assert %{"id" => id} = json_response(conn, 200) -      assert id == user.id -    end - -    test "respects limit_to_local_content == :all for remote user nicknames" do -      limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content]) -      Pleroma.Config.put([:instance, :limit_to_local_content], :all) - -      user = insert(:user, nickname: "user@example.com", local: false) - -      conn = -        build_conn() -        |> get("/api/v1/accounts/#{user.nickname}") - -      Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local) -      assert json_response(conn, 404) -    end - -    test "respects limit_to_local_content == :unauthenticated for remote user nicknames" do -      limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content]) -      Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated) - -      user = insert(:user, nickname: "user@example.com", local: false) -      reading_user = insert(:user) - -      conn = -        build_conn() -        |> get("/api/v1/accounts/#{user.nickname}") - -      assert json_response(conn, 404) - -      conn = -        build_conn() -        |> assign(:user, reading_user) -        |> get("/api/v1/accounts/#{user.nickname}") - -      Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local) -      assert %{"id" => id} = json_response(conn, 200) -      assert id == user.id -    end -  end - -  test "mascot upload", %{conn: conn} do -    user = insert(:user) - -    non_image_file = %Plug.Upload{ -      content_type: "audio/mpeg", -      path: Path.absname("test/fixtures/sound.mp3"), -      filename: "sound.mp3" -    } - -    conn = -      conn -      |> assign(:user, user) -      |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file}) - -    assert json_response(conn, 415) - -    file = %Plug.Upload{ -      content_type: "image/jpg", -      path: Path.absname("test/fixtures/image.jpg"), -      filename: "an_image.jpg" -    } - -    conn = -      build_conn() -      |> assign(:user, user) -      |> put("/api/v1/pleroma/mascot", %{"file" => file}) - -    assert %{"id" => _, "type" => image} = json_response(conn, 200) -  end - -  test "mascot retrieving", %{conn: conn} do -    user = insert(:user) -    # When user hasn't set a mascot, we should just get pleroma tan back -    conn = -      conn -      |> assign(:user, user) -      |> get("/api/v1/pleroma/mascot") - -    assert %{"url" => url} = json_response(conn, 200) -    assert url =~ "pleroma-fox-tan-smol" - -    # When a user sets their mascot, we should get that back -    file = %Plug.Upload{ -      content_type: "image/jpg", -      path: Path.absname("test/fixtures/image.jpg"), -      filename: "an_image.jpg" -    } - -    conn = -      build_conn() -      |> assign(:user, user) -      |> put("/api/v1/pleroma/mascot", %{"file" => file}) - -    assert json_response(conn, 200) - -    user = User.get_cached_by_id(user.id) - -    conn = -      build_conn() -      |> assign(:user, user) -      |> get("/api/v1/pleroma/mascot") - -    assert %{"url" => url, "type" => "image"} = json_response(conn, 200) -    assert url =~ "an_image" -  end - -  test "hashtag timeline", %{conn: conn} do -    following = insert(:user) - -    capture_log(fn -> -      {:ok, activity} = CommonAPI.post(following, %{"status" => "test #2hu"}) - -      {:ok, [_activity]} = -        OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873") - -      nconn = -        conn -        |> get("/api/v1/timelines/tag/2hu") - -      assert [%{"id" => id}] = json_response(nconn, 200) - -      assert id == to_string(activity.id) - -      # works for different capitalization too -      nconn = -        conn -        |> get("/api/v1/timelines/tag/2HU") - -      assert [%{"id" => id}] = json_response(nconn, 200) - -      assert id == to_string(activity.id) -    end) -  end - -  test "multi-hashtag timeline", %{conn: conn} do -    user = insert(:user) - -    {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"}) -    {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"}) -    {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"}) - -    any_test = -      conn -      |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]}) - -    [status_none, status_test1, status_test] = json_response(any_test, 200) - -    assert to_string(activity_test.id) == status_test["id"] -    assert to_string(activity_test1.id) == status_test1["id"] -    assert to_string(activity_none.id) == status_none["id"] - -    restricted_test = -      conn -      |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]}) - -    assert [status_test1] == json_response(restricted_test, 200) - -    all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]}) - -    assert [status_none] == json_response(all_test, 200) -  end - -  test "getting followers", %{conn: conn} do -    user = insert(:user) -    other_user = insert(:user) -    {:ok, user} = User.follow(user, other_user) - -    conn = -      conn -      |> get("/api/v1/accounts/#{other_user.id}/followers") - -    assert [%{"id" => id}] = json_response(conn, 200) -    assert id == to_string(user.id) -  end - -  test "getting followers, hide_followers", %{conn: conn} do -    user = insert(:user) -    other_user = insert(:user, %{info: %{hide_followers: true}}) -    {:ok, _user} = User.follow(user, other_user) - -    conn = -      conn -      |> get("/api/v1/accounts/#{other_user.id}/followers") - -    assert [] == json_response(conn, 200) -  end - -  test "getting followers, hide_followers, same user requesting", %{conn: conn} do -    user = insert(:user) -    other_user = insert(:user, %{info: %{hide_followers: true}}) -    {:ok, _user} = User.follow(user, other_user) - -    conn = -      conn -      |> assign(:user, other_user) -      |> get("/api/v1/accounts/#{other_user.id}/followers") - -    refute [] == json_response(conn, 200) -  end - -  test "getting followers, pagination", %{conn: conn} do -    user = insert(:user) -    follower1 = insert(:user) -    follower2 = insert(:user) -    follower3 = insert(:user) -    {:ok, _} = User.follow(follower1, user) -    {:ok, _} = User.follow(follower2, user) -    {:ok, _} = User.follow(follower3, user) - -    conn = -      conn -      |> assign(:user, user) - -    res_conn = -      conn -      |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}") - -    assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200) -    assert id3 == follower3.id -    assert id2 == follower2.id - -    res_conn = -      conn -      |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}") - -    assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200) -    assert id2 == follower2.id -    assert id1 == follower1.id - -    res_conn = -      conn -      |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}") - -    assert [%{"id" => id2}] = json_response(res_conn, 200) -    assert id2 == follower2.id - -    assert [link_header] = get_resp_header(res_conn, "link") -    assert link_header =~ ~r/min_id=#{follower2.id}/ -    assert link_header =~ ~r/max_id=#{follower2.id}/ -  end - -  test "getting following", %{conn: conn} do -    user = insert(:user) -    other_user = insert(:user) -    {:ok, user} = User.follow(user, other_user) - -    conn = -      conn -      |> get("/api/v1/accounts/#{user.id}/following") - -    assert [%{"id" => id}] = json_response(conn, 200) -    assert id == to_string(other_user.id) -  end - -  test "getting following, hide_follows", %{conn: conn} do -    user = insert(:user, %{info: %{hide_follows: true}}) -    other_user = insert(:user) -    {:ok, user} = User.follow(user, other_user) - -    conn = -      conn -      |> get("/api/v1/accounts/#{user.id}/following") - -    assert [] == json_response(conn, 200) -  end - -  test "getting following, hide_follows, same user requesting", %{conn: conn} do -    user = insert(:user, %{info: %{hide_follows: true}}) -    other_user = insert(:user) -    {:ok, user} = User.follow(user, other_user) - -    conn = -      conn -      |> assign(:user, user) -      |> get("/api/v1/accounts/#{user.id}/following") - -    refute [] == json_response(conn, 200) -  end - -  test "getting following, pagination", %{conn: conn} do -    user = insert(:user) -    following1 = insert(:user) -    following2 = insert(:user) -    following3 = insert(:user) -    {:ok, _} = User.follow(user, following1) -    {:ok, _} = User.follow(user, following2) -    {:ok, _} = User.follow(user, following3) - -    conn = -      conn -      |> assign(:user, user) - -    res_conn = -      conn -      |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}") - -    assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200) -    assert id3 == following3.id -    assert id2 == following2.id - -    res_conn = -      conn -      |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}") - -    assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200) -    assert id2 == following2.id -    assert id1 == following1.id - -    res_conn = -      conn -      |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}") - -    assert [%{"id" => id2}] = json_response(res_conn, 200) -    assert id2 == following2.id - -    assert [link_header] = get_resp_header(res_conn, "link") -    assert link_header =~ ~r/min_id=#{following2.id}/ -    assert link_header =~ ~r/max_id=#{following2.id}/ -  end - -  test "following / unfollowing a user", %{conn: conn} do -    user = insert(:user) -    other_user = insert(:user) - -    conn = -      conn -      |> assign(:user, user) -      |> post("/api/v1/accounts/#{other_user.id}/follow") - -    assert %{"id" => _id, "following" => true} = json_response(conn, 200) - -    user = User.get_cached_by_id(user.id) - -    conn = -      build_conn() -      |> assign(:user, user) -      |> post("/api/v1/accounts/#{other_user.id}/unfollow") - -    assert %{"id" => _id, "following" => false} = json_response(conn, 200) - -    user = User.get_cached_by_id(user.id) - -    conn = -      build_conn() -      |> assign(:user, user) -      |> post("/api/v1/follows", %{"uri" => other_user.nickname}) - -    assert %{"id" => id} = json_response(conn, 200) -    assert id == to_string(other_user.id) -  end - -  test "following without reblogs" do -    follower = insert(:user) -    followed = insert(:user) -    other_user = insert(:user) - -    conn = -      build_conn() -      |> assign(:user, follower) -      |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false") - -    assert %{"showing_reblogs" => false} = json_response(conn, 200) - -    {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"}) -    {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed) - -    conn = -      build_conn() -      |> assign(:user, User.get_cached_by_id(follower.id)) -      |> get("/api/v1/timelines/home") - -    assert [] == json_response(conn, 200) - -    conn = -      build_conn() -      |> assign(:user, follower) -      |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true") - -    assert %{"showing_reblogs" => true} = json_response(conn, 200) - -    conn = -      build_conn() -      |> assign(:user, User.get_cached_by_id(follower.id)) -      |> get("/api/v1/timelines/home") - -    expected_activity_id = reblog.id -    assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200) -  end - -  test "following / unfollowing errors" do -    user = insert(:user) - -    conn = -      build_conn() -      |> assign(:user, user) - -    # self follow -    conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow") -    assert %{"error" => "Record not found"} = json_response(conn_res, 404) - -    # self unfollow -    user = User.get_cached_by_id(user.id) -    conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow") -    assert %{"error" => "Record not found"} = json_response(conn_res, 404) - -    # self follow via uri -    user = User.get_cached_by_id(user.id) -    conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname}) -    assert %{"error" => "Record not found"} = json_response(conn_res, 404) - -    # follow non existing user -    conn_res = post(conn, "/api/v1/accounts/doesntexist/follow") -    assert %{"error" => "Record not found"} = json_response(conn_res, 404) - -    # follow non existing user via uri -    conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"}) -    assert %{"error" => "Record not found"} = json_response(conn_res, 404) - -    # unfollow non existing user -    conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow") -    assert %{"error" => "Record not found"} = json_response(conn_res, 404) -  end - -  describe "mute/unmute" do -    test "with notifications", %{conn: conn} do -      user = insert(:user) -      other_user = insert(:user) - -      conn = -        conn -        |> assign(:user, user) -        |> post("/api/v1/accounts/#{other_user.id}/mute") - -      response = json_response(conn, 200) - -      assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response -      user = User.get_cached_by_id(user.id) - -      conn = -        build_conn() -        |> assign(:user, user) -        |> post("/api/v1/accounts/#{other_user.id}/unmute") - -      response = json_response(conn, 200) -      assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response -    end - -    test "without notifications", %{conn: conn} do -      user = insert(:user) -      other_user = insert(:user) - -      conn = -        conn -        |> assign(:user, user) -        |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"}) - -      response = json_response(conn, 200) - -      assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response -      user = User.get_cached_by_id(user.id) - -      conn = -        build_conn() -        |> assign(:user, user) -        |> post("/api/v1/accounts/#{other_user.id}/unmute") - -      response = json_response(conn, 200) -      assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response -    end -  end - -  test "subscribing / unsubscribing to a user", %{conn: conn} do -    user = insert(:user) -    subscription_target = insert(:user) - -    conn = -      conn -      |> assign(:user, user) -      |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe") - -    assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200) - -    conn = -      build_conn() -      |> assign(:user, user) -      |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe") - -    assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200) -  end -    test "getting a list of mutes", %{conn: conn} do      user = insert(:user)      other_user = insert(:user) @@ -2228,27 +129,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do      assert [%{"id" => ^other_user_id}] = json_response(conn, 200)    end -  test "blocking / unblocking a user", %{conn: conn} do -    user = insert(:user) -    other_user = insert(:user) - -    conn = -      conn -      |> assign(:user, user) -      |> post("/api/v1/accounts/#{other_user.id}/block") - -    assert %{"id" => _id, "blocking" => true} = json_response(conn, 200) - -    user = User.get_cached_by_id(user.id) - -    conn = -      build_conn() -      |> assign(:user, user) -      |> post("/api/v1/accounts/#{other_user.id}/unblock") - -    assert %{"id" => _id, "blocking" => false} = json_response(conn, 200) -  end -    test "getting a list of blocks", %{conn: conn} do      user = insert(:user)      other_user = insert(:user) @@ -2264,46 +144,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do      assert [%{"id" => ^other_user_id}] = json_response(conn, 200)    end -  test "blocking / unblocking a domain", %{conn: conn} do -    user = insert(:user) -    other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"}) - -    conn = -      conn -      |> assign(:user, user) -      |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"}) - -    assert %{} = json_response(conn, 200) -    user = User.get_cached_by_ap_id(user.ap_id) -    assert User.blocks?(user, other_user) - -    conn = -      build_conn() -      |> assign(:user, user) -      |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"}) - -    assert %{} = json_response(conn, 200) -    user = User.get_cached_by_ap_id(user.ap_id) -    refute User.blocks?(user, other_user) -  end - -  test "getting a list of domain blocks", %{conn: conn} do -    user = insert(:user) - -    {:ok, user} = User.block_domain(user, "bad.site") -    {:ok, user} = User.block_domain(user, "even.worse.site") - -    conn = -      conn -      |> assign(:user, user) -      |> get("/api/v1/domain_blocks") - -    domain_blocks = json_response(conn, 200) - -    assert "bad.site" in domain_blocks -    assert "even.worse.site" in domain_blocks -  end -    test "unimplemented follow_requests, blocks, domain blocks" do      user = insert(:user) @@ -2365,199 +205,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do      assert [] = json_response(third_conn, 200)    end -  describe "getting favorites timeline of specified user" do -    setup do -      [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}}) -      [current_user: current_user, user: user] -    end - -    test "returns list of statuses favorited by specified user", %{ -      conn: conn, -      current_user: current_user, -      user: user -    } do -      [activity | _] = insert_pair(:note_activity) -      CommonAPI.favorite(activity.id, user) - -      response = -        conn -        |> assign(:user, current_user) -        |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") -        |> json_response(:ok) - -      [like] = response - -      assert length(response) == 1 -      assert like["id"] == activity.id -    end - -    test "returns favorites for specified user_id when user is not logged in", %{ -      conn: conn, -      user: user -    } do -      activity = insert(:note_activity) -      CommonAPI.favorite(activity.id, user) - -      response = -        conn -        |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") -        |> json_response(:ok) - -      assert length(response) == 1 -    end - -    test "returns favorited DM only when user is logged in and he is one of recipients", %{ -      conn: conn, -      current_user: current_user, -      user: user -    } do -      {:ok, direct} = -        CommonAPI.post(current_user, %{ -          "status" => "Hi @#{user.nickname}!", -          "visibility" => "direct" -        }) - -      CommonAPI.favorite(direct.id, user) - -      response = -        conn -        |> assign(:user, current_user) -        |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") -        |> json_response(:ok) - -      assert length(response) == 1 - -      anonymous_response = -        conn -        |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") -        |> json_response(:ok) - -      assert Enum.empty?(anonymous_response) -    end - -    test "does not return others' favorited DM when user is not one of recipients", %{ -      conn: conn, -      current_user: current_user, -      user: user -    } do -      user_two = insert(:user) - -      {:ok, direct} = -        CommonAPI.post(user_two, %{ -          "status" => "Hi @#{user.nickname}!", -          "visibility" => "direct" -        }) - -      CommonAPI.favorite(direct.id, user) - -      response = -        conn -        |> assign(:user, current_user) -        |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") -        |> json_response(:ok) - -      assert Enum.empty?(response) -    end - -    test "paginates favorites using since_id and max_id", %{ -      conn: conn, -      current_user: current_user, -      user: user -    } do -      activities = insert_list(10, :note_activity) - -      Enum.each(activities, fn activity -> -        CommonAPI.favorite(activity.id, user) -      end) - -      third_activity = Enum.at(activities, 2) -      seventh_activity = Enum.at(activities, 6) - -      response = -        conn -        |> assign(:user, current_user) -        |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{ -          since_id: third_activity.id, -          max_id: seventh_activity.id -        }) -        |> json_response(:ok) - -      assert length(response) == 3 -      refute third_activity in response -      refute seventh_activity in response -    end - -    test "limits favorites using limit parameter", %{ -      conn: conn, -      current_user: current_user, -      user: user -    } do -      7 -      |> insert_list(:note_activity) -      |> Enum.each(fn activity -> -        CommonAPI.favorite(activity.id, user) -      end) - -      response = -        conn -        |> assign(:user, current_user) -        |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"}) -        |> json_response(:ok) - -      assert length(response) == 3 -    end - -    test "returns empty response when user does not have any favorited statuses", %{ -      conn: conn, -      current_user: current_user, -      user: user -    } do -      response = -        conn -        |> assign(:user, current_user) -        |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") -        |> json_response(:ok) - -      assert Enum.empty?(response) -    end - -    test "returns 404 error when specified user is not exist", %{conn: conn} do -      conn = get(conn, "/api/v1/pleroma/accounts/test/favourites") - -      assert json_response(conn, 404) == %{"error" => "Record not found"} -    end - -    test "returns 403 error when user has hidden own favorites", %{ -      conn: conn, -      current_user: current_user -    } do -      user = insert(:user, %{info: %{hide_favorites: true}}) -      activity = insert(:note_activity) -      CommonAPI.favorite(activity.id, user) - -      conn = -        conn -        |> assign(:user, current_user) -        |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") - -      assert json_response(conn, 403) == %{"error" => "Can't get favorites"} -    end - -    test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do -      user = insert(:user) -      activity = insert(:note_activity) -      CommonAPI.favorite(activity.id, user) - -      conn = -        conn -        |> assign(:user, current_user) -        |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") - -      assert user.info.hide_favorites -      assert json_response(conn, 403) == %{"error" => "Can't get favorites"} -    end -  end -    test "get instance information", %{conn: conn} do      conn = get(conn, "/api/v1/instance")      assert result = json_response(conn, 200) @@ -2595,14 +242,11 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do      {:ok, _} = CommonAPI.post(user, %{"status" => "cofe"})      # Stats should count users with missing or nil `info.deactivated` value -    user = User.get_cached_by_id(user.id) -    info_change = Changeset.change(user.info, %{deactivated: nil})      {:ok, _user} = -      user -      |> Changeset.change() -      |> Changeset.put_embed(:info, info_change) -      |> User.update_and_set_cache() +      user.id +      |> User.get_cached_by_id() +      |> User.update_info(&Changeset.change(&1, %{deactivated: nil}))      Pleroma.Stats.force_update() @@ -2645,363 +289,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do      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 - -    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 -      {: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 -      {: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 - -  describe "reports" do -    setup do -      reporter = insert(:user) -      target_user = insert(:user) - -      {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"}) - -      [reporter: reporter, target_user: target_user, activity: activity] -    end - -    test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do -      assert %{"action_taken" => false, "id" => _} = -               conn -               |> assign(:user, reporter) -               |> post("/api/v1/reports", %{"account_id" => target_user.id}) -               |> json_response(200) -    end - -    test "submit a report with statuses and comment", %{ -      conn: conn, -      reporter: reporter, -      target_user: target_user, -      activity: activity -    } do -      assert %{"action_taken" => false, "id" => _} = -               conn -               |> assign(:user, reporter) -               |> post("/api/v1/reports", %{ -                 "account_id" => target_user.id, -                 "status_ids" => [activity.id], -                 "comment" => "bad status!", -                 "forward" => "false" -               }) -               |> json_response(200) -    end - -    test "account_id is required", %{ -      conn: conn, -      reporter: reporter, -      activity: activity -    } do -      assert %{"error" => "Valid `account_id` required"} = -               conn -               |> assign(:user, reporter) -               |> post("/api/v1/reports", %{"status_ids" => [activity.id]}) -               |> json_response(400) -    end - -    test "comment must be up to the size specified in the config", %{ -      conn: conn, -      reporter: reporter, -      target_user: target_user -    } do -      max_size = Config.get([:instance, :max_report_comment_size], 1000) -      comment = String.pad_trailing("a", max_size + 1, "a") - -      error = %{"error" => "Comment must be up to #{max_size} characters"} - -      assert ^error = -               conn -               |> assign(:user, reporter) -               |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment}) -               |> json_response(400) -    end - -    test "returns error when account is not exist", %{ -      conn: conn, -      reporter: reporter, -      activity: activity -    } do -      conn = -        conn -        |> assign(:user, reporter) -        |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"}) - -      assert json_response(conn, 400) == %{"error" => "Account not found"} -    end -  end -    describe "link headers" do      test "preserves parameters in link headers", %{conn: conn} do        user = insert(:user) @@ -3034,32 +321,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do      end    end -  test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do -    # Need to set an old-style integer ID to reproduce the problem -    # (these are no longer assigned to new accounts but were preserved -    # for existing accounts during the migration to flakeIDs) -    user_one = insert(:user, %{id: 1212}) -    user_two = insert(:user, %{nickname: "#{user_one.id}garbage"}) - -    resp_one = -      conn -      |> get("/api/v1/accounts/#{user_one.id}") - -    resp_two = -      conn -      |> get("/api/v1/accounts/#{user_two.nickname}") - -    resp_three = -      conn -      |> get("/api/v1/accounts/#{user_two.id}") - -    acc_one = json_response(resp_one, 200) -    acc_two = json_response(resp_two, 200) -    acc_three = json_response(resp_three, 200) -    refute acc_one == acc_two -    assert acc_two == acc_three -  end -    describe "custom emoji" do      test "with tags", %{conn: conn} do        [emoji | _body] = @@ -3156,368 +417,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do      end    end -  describe "scheduled activities" do -    test "creates a scheduled activity", %{conn: conn} do -      user = insert(:user) -      scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond) - -      conn = -        conn -        |> assign(:user, user) -        |> post("/api/v1/statuses", %{ -          "status" => "scheduled", -          "scheduled_at" => scheduled_at -        }) - -      assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200) -      assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at) -      assert [] == Repo.all(Activity) -    end - -    test "creates a scheduled activity with a media attachment", %{conn: conn} do -      user = insert(:user) -      scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond) - -      file = %Plug.Upload{ -        content_type: "image/jpg", -        path: Path.absname("test/fixtures/image.jpg"), -        filename: "an_image.jpg" -      } - -      {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id) - -      conn = -        conn -        |> assign(:user, user) -        |> post("/api/v1/statuses", %{ -          "media_ids" => [to_string(upload.id)], -          "status" => "scheduled", -          "scheduled_at" => scheduled_at -        }) - -      assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200) -      assert %{"type" => "image"} = media_attachment -    end - -    test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now", -         %{conn: conn} do -      user = insert(:user) - -      scheduled_at = -        NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond) - -      conn = -        conn -        |> assign(:user, user) -        |> post("/api/v1/statuses", %{ -          "status" => "not scheduled", -          "scheduled_at" => scheduled_at -        }) - -      assert %{"content" => "not scheduled"} = json_response(conn, 200) -      assert [] == Repo.all(ScheduledActivity) -    end - -    test "returns error when daily user limit is exceeded", %{conn: conn} do -      user = insert(:user) - -      today = -        NaiveDateTime.utc_now() -        |> NaiveDateTime.add(:timer.minutes(6), :millisecond) -        |> NaiveDateTime.to_iso8601() - -      attrs = %{params: %{}, scheduled_at: today} -      {:ok, _} = ScheduledActivity.create(user, attrs) -      {:ok, _} = ScheduledActivity.create(user, attrs) - -      conn = -        conn -        |> assign(:user, user) -        |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today}) - -      assert %{"error" => "daily limit exceeded"} == json_response(conn, 422) -    end - -    test "returns error when total user limit is exceeded", %{conn: conn} do -      user = insert(:user) - -      today = -        NaiveDateTime.utc_now() -        |> NaiveDateTime.add(:timer.minutes(6), :millisecond) -        |> NaiveDateTime.to_iso8601() - -      tomorrow = -        NaiveDateTime.utc_now() -        |> NaiveDateTime.add(:timer.hours(36), :millisecond) -        |> NaiveDateTime.to_iso8601() - -      attrs = %{params: %{}, scheduled_at: today} -      {:ok, _} = ScheduledActivity.create(user, attrs) -      {:ok, _} = ScheduledActivity.create(user, attrs) -      {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow}) - -      conn = -        conn -        |> assign(:user, user) -        |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow}) - -      assert %{"error" => "total limit exceeded"} == json_response(conn, 422) -    end - -    test "shows scheduled activities", %{conn: conn} do -      user = insert(:user) -      scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string() -      scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string() -      scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string() -      scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string() - -      conn = -        conn -        |> assign(:user, user) - -      # min_id -      conn_res = -        conn -        |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}") - -      result = json_response(conn_res, 200) -      assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result - -      # since_id -      conn_res = -        conn -        |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}") - -      result = json_response(conn_res, 200) -      assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result - -      # max_id -      conn_res = -        conn -        |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}") - -      result = json_response(conn_res, 200) -      assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result -    end - -    test "shows a scheduled activity", %{conn: conn} do -      user = insert(:user) -      scheduled_activity = insert(:scheduled_activity, user: user) - -      res_conn = -        conn -        |> assign(:user, user) -        |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}") - -      assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200) -      assert scheduled_activity_id == scheduled_activity.id |> to_string() - -      res_conn = -        conn -        |> assign(:user, user) -        |> get("/api/v1/scheduled_statuses/404") - -      assert %{"error" => "Record not found"} = json_response(res_conn, 404) -    end - -    test "updates a scheduled activity", %{conn: conn} do -      user = insert(:user) -      scheduled_activity = insert(:scheduled_activity, user: user) - -      new_scheduled_at = -        NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond) - -      res_conn = -        conn -        |> assign(:user, user) -        |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{ -          scheduled_at: new_scheduled_at -        }) - -      assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200) -      assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at) - -      res_conn = -        conn -        |> assign(:user, user) -        |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at}) - -      assert %{"error" => "Record not found"} = json_response(res_conn, 404) -    end - -    test "deletes a scheduled activity", %{conn: conn} do -      user = insert(:user) -      scheduled_activity = insert(:scheduled_activity, user: user) - -      res_conn = -        conn -        |> assign(:user, user) -        |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}") - -      assert %{} = json_response(res_conn, 200) -      assert nil == Repo.get(ScheduledActivity, scheduled_activity.id) - -      res_conn = -        conn -        |> assign(:user, user) -        |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}") - -      assert %{"error" => "Record not found"} = json_response(res_conn, 404) -    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 = -        conn -        |> post("/api/v1/apps", %{ -          client_name: "client_name", -          redirect_uris: "urn:ietf:wg:oauth:2.0:oob", -          scopes: "read, write, follow" -        }) - -      %{ -        "client_id" => client_id, -        "client_secret" => client_secret, -        "id" => _, -        "name" => "client_name", -        "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob", -        "vapid_key" => _, -        "website" => nil -      } = json_response(conn, 200) - -      conn = -        conn -        |> post("/oauth/token", %{ -          grant_type: "client_credentials", -          client_id: client_id, -          client_secret: client_secret -        }) - -      assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} = -               json_response(conn, 200) - -      assert token -      token_from_db = Repo.get_by(Token, token: token) -      assert token_from_db -      assert refresh -      assert scope == "read write follow" - -      conn = -        build_conn() -        |> put_req_header("authorization", "Bearer " <> token) -        |> post("/api/v1/accounts", %{ -          username: "lain", -          email: "lain@example.org", -          password: "PlzDontHackLain", -          agreement: true -        }) - -      %{ -        "access_token" => token, -        "created_at" => _created_at, -        "scope" => _scope, -        "token_type" => "Bearer" -      } = json_response(conn, 200) - -      token_from_db = Repo.get_by(Token, token: token) -      assert token_from_db -      token_from_db = Repo.preload(token_from_db, :user) -      assert token_from_db.user - -      assert token_from_db.user.info.confirmation_pending -    end - -    test "rate limit", %{conn: conn} do -      app_token = insert(:oauth_token, user: nil) - -      conn = -        put_req_header(conn, "authorization", "Bearer " <> app_token.token) -        |> Map.put(:remote_ip, {15, 15, 15, 15}) - -      for i <- 1..5 do -        conn = -          conn -          |> post("/api/v1/accounts", %{ -            username: "#{i}lain", -            email: "#{i}lain@example.org", -            password: "PlzDontHackLain", -            agreement: true -          }) - -        %{ -          "access_token" => token, -          "created_at" => _created_at, -          "scope" => _scope, -          "token_type" => "Bearer" -        } = json_response(conn, 200) - -        token_from_db = Repo.get_by(Token, token: token) -        assert token_from_db -        token_from_db = Repo.preload(token_from_db, :user) -        assert token_from_db.user - -        assert token_from_db.user.info.confirmation_pending -      end - -      conn = -        conn -        |> post("/api/v1/accounts", %{ -          username: "6lain", -          email: "6lain@example.org", -          password: "PlzDontHackLain", -          agreement: true -        }) - -      assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"} -    end -  end -    describe "GET /api/v1/polls/:id" do      test "returns poll entity for object id", %{conn: conn} do        user = insert(:user) @@ -3690,197 +589,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) @@ -3933,46 +641,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do      end    end -  describe "POST /api/v1/pleroma/accounts/confirmation_resend" do -    setup do -      user = insert(:user) -      info_change = User.Info.confirmation_changeset(user.info, need_confirmation: true) - -      {:ok, user} = -        user -        |> Changeset.change() -        |> Changeset.put_embed(:info, info_change) -        |> Repo.update() - -      assert user.info.confirmation_pending - -      [user: user] -    end - -    clear_config([:instance, :account_activation_required]) do -      Config.put([:instance, :account_activation_required], true) -    end - -    test "resend account confirmation email", %{conn: conn, user: user} do -      conn -      |> assign(:user, user) -      |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}") -      |> json_response(:no_content) - -      ObanHelpers.perform_all() - -      email = Pleroma.Emails.UserEmail.account_confirmation_email(user) -      notify_email = Config.get([:instance, :notify_email]) -      instance_name = Config.get([:instance, :name]) - -      assert_email_sent( -        from: {instance_name, notify_email}, -        to: {user.name, user.email}, -        html_body: email.html_body -      ) -    end -  end -    describe "GET /api/v1/suggestions" do      setup do        user = insert(:user) @@ -4053,4 +721,98 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do               ]      end    end + +  describe "PUT /api/v1/media/:id" do +    setup do +      actor = insert(:user) + +      file = %Plug.Upload{ +        content_type: "image/jpg", +        path: Path.absname("test/fixtures/image.jpg"), +        filename: "an_image.jpg" +      } + +      {:ok, %Object{} = object} = +        ActivityPub.upload( +          file, +          actor: User.ap_id(actor), +          description: "test-m" +        ) + +      [actor: actor, object: object] +    end + +    test "updates name of media", %{conn: conn, actor: actor, object: object} do +      media = +        conn +        |> assign(:user, actor) +        |> put("/api/v1/media/#{object.id}", %{"description" => "test-media"}) +        |> json_response(:ok) + +      assert media["description"] == "test-media" +      assert refresh_record(object).data["name"] == "test-media" +    end + +    test "returns error wheb request is bad", %{conn: conn, actor: actor, object: object} do +      media = +        conn +        |> assign(:user, actor) +        |> put("/api/v1/media/#{object.id}", %{}) +        |> json_response(400) + +      assert media == %{"error" => "bad_request"} +    end +  end + +  describe "DELETE /auth/sign_out" do +    test "redirect to root page", %{conn: conn} do +      user = insert(:user) + +      conn = +        conn +        |> assign(:user, user) +        |> delete("/auth/sign_out") + +      assert conn.status == 302 +      assert redirected_to(conn) == "/" +    end +  end + +  describe "empty_array, stubs for mastodon api" do +    test "GET /api/v1/accounts/:id/identity_proofs", %{conn: conn} do +      user = insert(:user) + +      res = +        conn +        |> assign(:user, user) +        |> get("/api/v1/accounts/#{user.id}/identity_proofs") +        |> json_response(200) + +      assert res == [] +    end + +    test "GET /api/v1/endorsements", %{conn: conn} do +      user = insert(:user) + +      res = +        conn +        |> assign(:user, user) +        |> get("/api/v1/endorsements") +        |> json_response(200) + +      assert res == [] +    end + +    test "GET /api/v1/trends", %{conn: conn} do +      user = insert(:user) + +      res = +        conn +        |> assign(:user, user) +        |> get("/api/v1/trends") +        |> json_response(200) + +      assert res == [] +    end +  end  end diff --git a/test/web/mastodon_api/views/account_view_test.exs b/test/web/mastodon_api/views/account_view_test.exs index 6206107f7..62b2ab7e3 100644 --- a/test/web/mastodon_api/views/account_view_test.exs +++ b/test/web/mastodon_api/views/account_view_test.exs @@ -67,7 +67,9 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do        source: %{          note: "valid html",          sensitive: false, -        pleroma: %{}, +        pleroma: %{ +          discoverable: false +        },          fields: []        },        pleroma: %{ @@ -86,7 +88,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do        }      } -    assert expected == AccountView.render("account.json", %{user: user}) +    assert expected == AccountView.render("show.json", %{user: user})    end    test "Represent the user account for the account owner" do @@ -104,7 +106,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do      assert %{               pleroma: %{notification_settings: ^notification_settings},               source: %{privacy: ^privacy} -           } = AccountView.render("account.json", %{user: user, for: user}) +           } = AccountView.render("show.json", %{user: user, for: user})    end    test "Represent a Service(bot) account" do @@ -137,7 +139,9 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do        source: %{          note: user.bio,          sensitive: false, -        pleroma: %{}, +        pleroma: %{ +          discoverable: false +        },          fields: []        },        pleroma: %{ @@ -156,13 +160,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do        }      } -    assert expected == AccountView.render("account.json", %{user: user}) +    assert expected == AccountView.render("show.json", %{user: user})    end    test "Represent a deactivated user for an admin" do      admin = insert(:user, %{info: %{is_admin: true}})      deactivated_user = insert(:user, %{info: %{deactivated: true}}) -    represented = AccountView.render("account.json", %{user: deactivated_user, for: admin}) +    represented = AccountView.render("show.json", %{user: deactivated_user, for: admin})      assert represented[:pleroma][:deactivated] == true    end @@ -310,7 +314,9 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do        source: %{          note: user.bio,          sensitive: false, -        pleroma: %{}, +        pleroma: %{ +          discoverable: false +        },          fields: []        },        pleroma: %{ @@ -342,27 +348,27 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do        }      } -    assert expected == AccountView.render("account.json", %{user: user, for: other_user}) +    assert expected == AccountView.render("show.json", %{user: user, for: other_user})    end    test "returns the settings store if the requesting user is the represented user and it's requested specifically" do      user = insert(:user, %{info: %User.Info{pleroma_settings_store: %{fe: "test"}}})      result = -      AccountView.render("account.json", %{user: user, for: user, with_pleroma_settings: true}) +      AccountView.render("show.json", %{user: user, for: user, with_pleroma_settings: true})      assert result.pleroma.settings_store == %{:fe => "test"} -    result = AccountView.render("account.json", %{user: user, with_pleroma_settings: true}) +    result = AccountView.render("show.json", %{user: user, with_pleroma_settings: true})      assert result.pleroma[:settings_store] == nil -    result = AccountView.render("account.json", %{user: user, for: user}) +    result = AccountView.render("show.json", %{user: user, for: user})      assert result.pleroma[:settings_store] == nil    end    test "sanitizes display names" do      user = insert(:user, name: "<marquee> username </marquee>") -    result = AccountView.render("account.json", %{user: user}) +    result = AccountView.render("show.json", %{user: user})      refute result.display_name == "<marquee> username </marquee>"    end @@ -385,7 +391,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do                 followers_count: 0,                 following_count: 0,                 pleroma: %{hide_follows_count: true, hide_followers_count: true} -             } = AccountView.render("account.json", %{user: user}) +             } = AccountView.render("show.json", %{user: user})      end      test "shows when follows/followers are hidden" do @@ -398,7 +404,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do                 followers_count: 1,                 following_count: 1,                 pleroma: %{hide_follows: true, hide_followers: true} -             } = AccountView.render("account.json", %{user: user}) +             } = AccountView.render("show.json", %{user: user})      end      test "shows actual follower/following count to the account owner" do @@ -410,7 +416,82 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do        assert %{                 followers_count: 1,                 following_count: 1 -             } = AccountView.render("account.json", %{user: user, for: user}) +             } = AccountView.render("show.json", %{user: user, for: user}) +    end +  end + +  describe "follow requests counter" do +    test "shows zero when no follow requests are pending" do +      user = insert(:user) + +      assert %{follow_requests_count: 0} = +               AccountView.render("show.json", %{user: user, for: user}) + +      other_user = insert(:user) +      {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user) + +      assert %{follow_requests_count: 0} = +               AccountView.render("show.json", %{user: user, for: user}) +    end + +    test "shows non-zero when follow requests are pending" do +      user = insert(:user, %{info: %{locked: true}}) + +      assert %{locked: true} = AccountView.render("show.json", %{user: user, for: user}) + +      other_user = insert(:user) +      {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user) + +      assert %{locked: true, follow_requests_count: 1} = +               AccountView.render("show.json", %{user: user, for: user}) +    end + +    test "decreases when accepting a follow request" do +      user = insert(:user, %{info: %{locked: true}}) + +      assert %{locked: true} = AccountView.render("show.json", %{user: user, for: user}) + +      other_user = insert(:user) +      {:ok, other_user, user, _activity} = CommonAPI.follow(other_user, user) + +      assert %{locked: true, follow_requests_count: 1} = +               AccountView.render("show.json", %{user: user, for: user}) + +      {:ok, _other_user} = CommonAPI.accept_follow_request(other_user, user) + +      assert %{locked: true, follow_requests_count: 0} = +               AccountView.render("show.json", %{user: user, for: user}) +    end + +    test "decreases when rejecting a follow request" do +      user = insert(:user, %{info: %{locked: true}}) + +      assert %{locked: true} = AccountView.render("show.json", %{user: user, for: user}) + +      other_user = insert(:user) +      {:ok, other_user, user, _activity} = CommonAPI.follow(other_user, user) + +      assert %{locked: true, follow_requests_count: 1} = +               AccountView.render("show.json", %{user: user, for: user}) + +      {:ok, _other_user} = CommonAPI.reject_follow_request(other_user, user) + +      assert %{locked: true, follow_requests_count: 0} = +               AccountView.render("show.json", %{user: user, for: user}) +    end + +    test "shows non-zero when historical unapproved requests are present" do +      user = insert(:user, %{info: %{locked: true}}) + +      assert %{locked: true} = AccountView.render("show.json", %{user: user, for: user}) + +      other_user = insert(:user) +      {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user) + +      {:ok, user} = User.update_info(user, &User.Info.user_upgrade(&1, %{locked: false})) + +      assert %{locked: false, follow_requests_count: 1} = +               AccountView.render("show.json", %{user: user, for: user})      end    end  end diff --git a/test/web/mastodon_api/views/notification_view_test.exs b/test/web/mastodon_api/views/notification_view_test.exs index 9231aaec8..81ab82e2b 100644 --- a/test/web/mastodon_api/views/notification_view_test.exs +++ b/test/web/mastodon_api/views/notification_view_test.exs @@ -27,8 +27,8 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do        id: to_string(notification.id),        pleroma: %{is_seen: false},        type: "mention", -      account: AccountView.render("account.json", %{user: user, for: mentioned_user}), -      status: StatusView.render("status.json", %{activity: activity, for: mentioned_user}), +      account: AccountView.render("show.json", %{user: user, for: mentioned_user}), +      status: StatusView.render("show.json", %{activity: activity, for: mentioned_user}),        created_at: Utils.to_masto_date(notification.inserted_at)      } @@ -50,8 +50,8 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do        id: to_string(notification.id),        pleroma: %{is_seen: false},        type: "favourite", -      account: AccountView.render("account.json", %{user: another_user, for: user}), -      status: StatusView.render("status.json", %{activity: create_activity, for: user}), +      account: AccountView.render("show.json", %{user: another_user, for: user}), +      status: StatusView.render("show.json", %{activity: create_activity, for: user}),        created_at: Utils.to_masto_date(notification.inserted_at)      } @@ -72,8 +72,8 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do        id: to_string(notification.id),        pleroma: %{is_seen: false},        type: "reblog", -      account: AccountView.render("account.json", %{user: another_user, for: user}), -      status: StatusView.render("status.json", %{activity: reblog_activity, for: user}), +      account: AccountView.render("show.json", %{user: another_user, for: user}), +      status: StatusView.render("show.json", %{activity: reblog_activity, for: user}),        created_at: Utils.to_masto_date(notification.inserted_at)      } @@ -92,7 +92,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do        id: to_string(notification.id),        pleroma: %{is_seen: false},        type: "follow", -      account: AccountView.render("account.json", %{user: follower, for: followed}), +      account: AccountView.render("show.json", %{user: follower, for: followed}),        created_at: Utils.to_masto_date(notification.inserted_at)      } diff --git a/test/web/mastodon_api/views/status_view_test.exs b/test/web/mastodon_api/views/status_view_test.exs index 51f8434fa..8df23d0a8 100644 --- a/test/web/mastodon_api/views/status_view_test.exs +++ b/test/web/mastodon_api/views/status_view_test.exs @@ -29,7 +29,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do      {:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!", "visibility" => "direct"})      status = -      StatusView.render("status.json", +      StatusView.render("show.json",          activity: activity,          with_direct_conversation_id: true,          for: user @@ -46,7 +46,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do      Repo.delete(user)      Cachex.clear(:user_cache) -    %{account: ms_user} = StatusView.render("status.json", activity: activity) +    %{account: ms_user} = StatusView.render("show.json", activity: activity)      assert ms_user.acct == "erroruser@example.com"    end @@ -63,7 +63,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do      Cachex.clear(:user_cache) -    result = StatusView.render("status.json", activity: activity) +    result = StatusView.render("show.json", activity: activity)      assert result[:account][:id] == to_string(user.id)    end @@ -81,7 +81,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do      User.get_cached_by_ap_id(note.data["actor"]) -    status = StatusView.render("status.json", %{activity: note}) +    status = StatusView.render("show.json", %{activity: note})      assert status.content == ""    end @@ -93,7 +93,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do      convo_id = Utils.context_to_conversation_id(object_data["context"]) -    status = StatusView.render("status.json", %{activity: note}) +    status = StatusView.render("show.json", %{activity: note})      created_at =        (object_data["published"] || "") @@ -103,7 +103,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do        id: to_string(note.id),        uri: object_data["id"],        url: Pleroma.Web.Router.Helpers.o_status_url(Pleroma.Web.Endpoint, :notice, note), -      account: AccountView.render("account.json", %{user: user}), +      account: AccountView.render("show.json", %{user: user}),        in_reply_to_id: nil,        in_reply_to_account_id: nil,        card: nil, @@ -165,11 +165,11 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do      {:ok, user} = User.mute(user, other_user)      {:ok, activity} = CommonAPI.post(other_user, %{"status" => "test"}) -    status = StatusView.render("status.json", %{activity: activity}) +    status = StatusView.render("show.json", %{activity: activity})      assert status.muted == false -    status = StatusView.render("status.json", %{activity: activity, for: user}) +    status = StatusView.render("show.json", %{activity: activity, for: user})      assert status.muted == true    end @@ -181,13 +181,13 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do      {:ok, user} = User.mute(user, other_user)      {:ok, activity} = CommonAPI.post(other_user, %{"status" => "test"}) -    status = StatusView.render("status.json", %{activity: activity, for: user}) +    status = StatusView.render("show.json", %{activity: activity, for: user})      assert status.pleroma.thread_muted == false      {:ok, activity} = CommonAPI.add_mute(user, activity) -    status = StatusView.render("status.json", %{activity: activity, for: user}) +    status = StatusView.render("show.json", %{activity: activity, for: user})      assert status.pleroma.thread_muted == true    end @@ -196,11 +196,11 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do      user = insert(:user)      {:ok, activity} = CommonAPI.post(user, %{"status" => "Cute girls doing cute things"}) -    status = StatusView.render("status.json", %{activity: activity}) +    status = StatusView.render("show.json", %{activity: activity})      assert status.bookmarked == false -    status = StatusView.render("status.json", %{activity: activity, for: user}) +    status = StatusView.render("show.json", %{activity: activity, for: user})      assert status.bookmarked == false @@ -208,7 +208,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do      activity = Activity.get_by_id_with_object(activity.id) -    status = StatusView.render("status.json", %{activity: activity, for: user}) +    status = StatusView.render("show.json", %{activity: activity, for: user})      assert status.bookmarked == true    end @@ -220,7 +220,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do      {:ok, activity} =        CommonAPI.post(user, %{"status" => "he", "in_reply_to_status_id" => note.id}) -    status = StatusView.render("status.json", %{activity: activity}) +    status = StatusView.render("show.json", %{activity: activity})      assert status.in_reply_to_id == to_string(note.id) @@ -237,7 +237,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do      {:ok, [activity]} = OStatus.handle_incoming(incoming) -    status = StatusView.render("status.json", %{activity: activity}) +    status = StatusView.render("show.json", %{activity: activity})      assert status.mentions ==               Enum.map([user], fn u -> AccountView.render("mention.json", %{user: u}) end) @@ -263,7 +263,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do      assert length(activity.recipients) == 3 -    %{mentions: [mention] = mentions} = StatusView.render("status.json", %{activity: activity}) +    %{mentions: [mention] = mentions} = StatusView.render("show.json", %{activity: activity})      assert length(mentions) == 1      assert mention.url == recipient_ap_id @@ -300,7 +300,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do      assert length(activity.recipients) == 3 -    %{mentions: [mention] = mentions} = StatusView.render("status.json", %{activity: activity}) +    %{mentions: [mention] = mentions} = StatusView.render("show.json", %{activity: activity})      assert length(mentions) == 1      assert mention.url == recipient.ap_id @@ -340,7 +340,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do      id = "https://wedistribute.org/wp-json/pterotype/v1/object/85810"      [activity] = Activity.search(nil, id) -    status = StatusView.render("status.json", %{activity: activity}) +    status = StatusView.render("show.json", %{activity: activity})      assert status.uri == id      assert status.url == "https://wedistribute.org/2019/07/mastodon-drops-ostatus/" @@ -352,7 +352,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do      {:ok, reblog, _} = CommonAPI.repeat(activity.id, user) -    represented = StatusView.render("status.json", %{for: user, activity: reblog}) +    represented = StatusView.render("show.json", %{for: user, activity: reblog})      assert represented[:id] == to_string(reblog.id)      assert represented[:reblog][:id] == to_string(activity.id) @@ -369,7 +369,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do      %Activity{} = activity = Activity.get_create_by_object_ap_id(object.data["id"]) -    represented = StatusView.render("status.json", %{for: user, activity: activity}) +    represented = StatusView.render("show.json", %{for: user, activity: activity})      assert represented[:id] == to_string(activity.id)      assert length(represented[:media_attachments]) == 1 @@ -570,7 +570,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do          "status" => "drink more water"        }) -    result = StatusView.render("status.json", %{activity: activity, for: other_user}) +    result = StatusView.render("show.json", %{activity: activity, for: other_user})      assert result[:account][:pleroma][:relationship] ==               AccountView.render("relationship.json", %{user: other_user, target: user}) @@ -587,7 +587,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do      {:ok, activity, _object} = CommonAPI.repeat(activity.id, other_user) -    result = StatusView.render("status.json", %{activity: activity, for: user}) +    result = StatusView.render("show.json", %{activity: activity, for: user})      assert result[:account][:pleroma][:relationship] ==               AccountView.render("relationship.json", %{user: user, target: other_user}) @@ -604,8 +604,17 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do      {:ok, activity} =        CommonAPI.post(user, %{"status" => "foobar", "visibility" => "list:#{list.id}"}) -    status = StatusView.render("status.json", activity: activity) +    status = StatusView.render("show.json", activity: activity)      assert status.visibility == "list"    end + +  test "successfully renders a Listen activity (pleroma extension)" do +    listen_activity = insert(:listen) + +    status = StatusView.render("listen.json", activity: listen_activity) + +    assert status.length == listen_activity.data["object"]["length"] +    assert status.title == listen_activity.data["object"]["title"] +  end  end diff --git a/test/web/oauth/app_test.exs b/test/web/oauth/app_test.exs new file mode 100644 index 000000000..195b8c17f --- /dev/null +++ b/test/web/oauth/app_test.exs @@ -0,0 +1,33 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.OAuth.AppTest do +  use Pleroma.DataCase + +  alias Pleroma.Web.OAuth.App +  import Pleroma.Factory + +  describe "get_or_make/2" do +    test "gets exist app" do +      attrs = %{client_name: "Mastodon-Local", redirect_uris: "."} +      app = insert(:oauth_app, Map.merge(attrs, %{scopes: ["read", "write"]})) +      {:ok, %App{} = exist_app} = App.get_or_make(attrs, []) +      assert exist_app == app +    end + +    test "make app" do +      attrs = %{client_name: "Mastodon-Local", redirect_uris: "."} +      {:ok, %App{} = app} = App.get_or_make(attrs, ["write"]) +      assert app.scopes == ["write"] +    end + +    test "gets exist app and updates scopes" do +      attrs = %{client_name: "Mastodon-Local", redirect_uris: "."} +      app = insert(:oauth_app, Map.merge(attrs, %{scopes: ["read", "write"]})) +      {:ok, %App{} = exist_app} = App.get_or_make(attrs, ["read", "write", "follow", "push"]) +      assert exist_app.id == app.id +      assert exist_app.scopes == ["read", "write", "follow", "push"] +    end +  end +end diff --git a/test/web/oauth/oauth_controller_test.exs b/test/web/oauth/oauth_controller_test.exs index c73c500d9..9a251b7ed 100644 --- a/test/web/oauth/oauth_controller_test.exs +++ b/test/web/oauth/oauth_controller_test.exs @@ -7,6 +7,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do    import Pleroma.Factory    alias Pleroma.Repo +  alias Pleroma.User    alias Pleroma.Web.OAuth.Authorization    alias Pleroma.Web.OAuth.OAuthController    alias Pleroma.Web.OAuth.Token @@ -775,15 +776,11 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do      test "rejects token exchange for valid credentials belonging to unconfirmed user and confirmation is required" do        Pleroma.Config.put([:instance, :account_activation_required], true) -        password = "testpassword" -      user = insert(:user, password_hash: Comeonin.Pbkdf2.hashpwsalt(password)) -      info_change = Pleroma.User.Info.confirmation_changeset(user.info, need_confirmation: true)        {:ok, user} = -        user -        |> Ecto.Changeset.change() -        |> Ecto.Changeset.put_embed(:info, info_change) +        insert(:user, password_hash: Comeonin.Pbkdf2.hashpwsalt(password)) +        |> User.change_info(&User.Info.confirmation_changeset(&1, need_confirmation: true))          |> Repo.update()        refute Pleroma.User.auth_active?(user) @@ -831,6 +828,33 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do        refute Map.has_key?(resp, "access_token")      end +    test "rejects token exchange for user with password_reset_pending set to true" do +      password = "testpassword" + +      user = +        insert(:user, +          password_hash: Comeonin.Pbkdf2.hashpwsalt(password), +          info: %{password_reset_pending: true} +        ) + +      app = insert(:oauth_app, scopes: ["read", "write"]) + +      conn = +        build_conn() +        |> post("/oauth/token", %{ +          "grant_type" => "password", +          "username" => user.nickname, +          "password" => password, +          "client_id" => app.client_id, +          "client_secret" => app.client_secret +        }) + +      assert resp = json_response(conn, 403) + +      assert resp["error"] == "Password reset is required" +      refute Map.has_key?(resp, "access_token") +    end +      test "rejects an invalid authorization code" do        app = insert(:oauth_app) diff --git a/test/web/ostatus/ostatus_controller_test.exs b/test/web/ostatus/ostatus_controller_test.exs index ec96f0012..f06023dff 100644 --- a/test/web/ostatus/ostatus_controller_test.exs +++ b/test/web/ostatus/ostatus_controller_test.exs @@ -50,20 +50,16 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do                 assert response(conn, 200)               end) =~ "[error]" -      # Set a wrong magic-key for a user so it has to refetch -      salmon_user = User.get_cached_by_ap_id("http://gs.example.org:4040/index.php/user/1") -        # Wrong key -      info_cng = -        User.Info.remote_user_creation(salmon_user.info, %{ -          magic_key: -            "RSA.pu0s-halox4tu7wmES1FVSx6u-4wc0YrUFXcqWXZG4-27UmbCOpMQftRCldNRfyA-qLbz-eqiwrong1EwUvjsD4cYbAHNGHwTvDOyx5AKthQUP44ykPv7kjKGh3DWKySJvcs9tlUG87hlo7AvnMo9pwRS_Zz2CacQ-MKaXyDepk=.AQAB" -        }) +      info = %{ +        magic_key: +          "RSA.pu0s-halox4tu7wmES1FVSx6u-4wc0YrUFXcqWXZG4-27UmbCOpMQftRCldNRfyA-qLbz-eqiwrong1EwUvjsD4cYbAHNGHwTvDOyx5AKthQUP44ykPv7kjKGh3DWKySJvcs9tlUG87hlo7AvnMo9pwRS_Zz2CacQ-MKaXyDepk=.AQAB" +      } -      salmon_user -      |> Ecto.Changeset.change() -      |> Ecto.Changeset.put_embed(:info, info_cng) -      |> User.update_and_set_cache() +      # Set a wrong magic-key for a user so it has to refetch +      "http://gs.example.org:4040/index.php/user/1" +      |> User.get_cached_by_ap_id() +      |> User.update_info(&User.Info.remote_user_creation(&1, info))        assert capture_log(fn ->                 conn = @@ -400,7 +396,8 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do                 "oauthAuthorizationEndpoint" => "#{Pleroma.Web.base_url()}/oauth/authorize",                 "oauthRegistrationEndpoint" => "#{Pleroma.Web.base_url()}/api/v1/apps",                 "oauthTokenEndpoint" => "#{Pleroma.Web.base_url()}/oauth/token", -               "sharedInbox" => "#{Pleroma.Web.base_url()}/inbox" +               "sharedInbox" => "#{Pleroma.Web.base_url()}/inbox", +               "uploadMedia" => "#{Pleroma.Web.base_url()}/api/ap/upload_media"               }        assert response["@context"] == [ @@ -462,7 +459,8 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do                 "oauthAuthorizationEndpoint" => "#{Pleroma.Web.base_url()}/oauth/authorize",                 "oauthRegistrationEndpoint" => "#{Pleroma.Web.base_url()}/api/v1/apps",                 "oauthTokenEndpoint" => "#{Pleroma.Web.base_url()}/oauth/token", -               "sharedInbox" => "#{Pleroma.Web.base_url()}/inbox" +               "sharedInbox" => "#{Pleroma.Web.base_url()}/inbox", +               "uploadMedia" => "#{Pleroma.Web.base_url()}/api/ap/upload_media"               }        assert response["@context"] == [ diff --git a/test/web/pleroma_api/controllers/account_controller_test.exs b/test/web/pleroma_api/controllers/account_controller_test.exs new file mode 100644 index 000000000..3b4665afd --- /dev/null +++ b/test/web/pleroma_api/controllers/account_controller_test.exs @@ -0,0 +1,395 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do +  use Pleroma.Web.ConnCase + +  alias Pleroma.Config +  alias Pleroma.Repo +  alias Pleroma.Tests.ObanHelpers +  alias Pleroma.User +  alias Pleroma.Web.CommonAPI + +  import Pleroma.Factory +  import Swoosh.TestAssertions + +  @image "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7" + +  describe "POST /api/v1/pleroma/accounts/confirmation_resend" do +    setup do +      {:ok, user} = +        insert(:user) +        |> User.change_info(&User.Info.confirmation_changeset(&1, need_confirmation: true)) +        |> Repo.update() + +      assert user.info.confirmation_pending + +      [user: user] +    end + +    clear_config([:instance, :account_activation_required]) do +      Config.put([:instance, :account_activation_required], true) +    end + +    test "resend account confirmation email", %{conn: conn, user: user} do +      conn +      |> assign(:user, user) +      |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}") +      |> json_response(:no_content) + +      ObanHelpers.perform_all() + +      email = Pleroma.Emails.UserEmail.account_confirmation_email(user) +      notify_email = Config.get([:instance, :notify_email]) +      instance_name = Config.get([:instance, :name]) + +      assert_email_sent( +        from: {instance_name, notify_email}, +        to: {user.name, user.email}, +        html_body: email.html_body +      ) +    end +  end + +  describe "PATCH /api/v1/pleroma/accounts/update_avatar" do +    test "user avatar can be set", %{conn: conn} do +      user = insert(:user) +      avatar_image = File.read!("test/fixtures/avatar_data_uri") + +      conn = +        conn +        |> assign(:user, user) +        |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: avatar_image}) + +      user = refresh_record(user) + +      assert %{ +               "name" => _, +               "type" => _, +               "url" => [ +                 %{ +                   "href" => _, +                   "mediaType" => _, +                   "type" => _ +                 } +               ] +             } = user.avatar + +      assert %{"url" => _} = json_response(conn, 200) +    end + +    test "user avatar can be reset", %{conn: conn} do +      user = insert(:user) + +      conn = +        conn +        |> assign(:user, user) +        |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: ""}) + +      user = User.get_cached_by_id(user.id) + +      assert user.avatar == nil + +      assert %{"url" => nil} = json_response(conn, 200) +    end +  end + +  describe "PATCH /api/v1/pleroma/accounts/update_banner" do +    test "can set profile banner", %{conn: conn} do +      user = insert(:user) + +      conn = +        conn +        |> assign(:user, user) +        |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => @image}) + +      user = refresh_record(user) +      assert user.info.banner["type"] == "Image" + +      assert %{"url" => _} = json_response(conn, 200) +    end + +    test "can reset profile banner", %{conn: conn} do +      user = insert(:user) + +      conn = +        conn +        |> assign(:user, user) +        |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => ""}) + +      user = refresh_record(user) +      assert user.info.banner == %{} + +      assert %{"url" => nil} = json_response(conn, 200) +    end +  end + +  describe "PATCH /api/v1/pleroma/accounts/update_background" do +    test "background image can be set", %{conn: conn} do +      user = insert(:user) + +      conn = +        conn +        |> assign(:user, user) +        |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => @image}) + +      user = refresh_record(user) +      assert user.info.background["type"] == "Image" +      assert %{"url" => _} = json_response(conn, 200) +    end + +    test "background image can be reset", %{conn: conn} do +      user = insert(:user) + +      conn = +        conn +        |> assign(:user, user) +        |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => ""}) + +      user = refresh_record(user) +      assert user.info.background == %{} +      assert %{"url" => nil} = json_response(conn, 200) +    end +  end + +  describe "getting favorites timeline of specified user" do +    setup do +      [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}}) +      [current_user: current_user, user: user] +    end + +    test "returns list of statuses favorited by specified user", %{ +      conn: conn, +      current_user: current_user, +      user: user +    } do +      [activity | _] = insert_pair(:note_activity) +      CommonAPI.favorite(activity.id, user) + +      response = +        conn +        |> assign(:user, current_user) +        |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") +        |> json_response(:ok) + +      [like] = response + +      assert length(response) == 1 +      assert like["id"] == activity.id +    end + +    test "returns favorites for specified user_id when user is not logged in", %{ +      conn: conn, +      user: user +    } do +      activity = insert(:note_activity) +      CommonAPI.favorite(activity.id, user) + +      response = +        conn +        |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") +        |> json_response(:ok) + +      assert length(response) == 1 +    end + +    test "returns favorited DM only when user is logged in and he is one of recipients", %{ +      conn: conn, +      current_user: current_user, +      user: user +    } do +      {:ok, direct} = +        CommonAPI.post(current_user, %{ +          "status" => "Hi @#{user.nickname}!", +          "visibility" => "direct" +        }) + +      CommonAPI.favorite(direct.id, user) + +      response = +        conn +        |> assign(:user, current_user) +        |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") +        |> json_response(:ok) + +      assert length(response) == 1 + +      anonymous_response = +        conn +        |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") +        |> json_response(:ok) + +      assert Enum.empty?(anonymous_response) +    end + +    test "does not return others' favorited DM when user is not one of recipients", %{ +      conn: conn, +      current_user: current_user, +      user: user +    } do +      user_two = insert(:user) + +      {:ok, direct} = +        CommonAPI.post(user_two, %{ +          "status" => "Hi @#{user.nickname}!", +          "visibility" => "direct" +        }) + +      CommonAPI.favorite(direct.id, user) + +      response = +        conn +        |> assign(:user, current_user) +        |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") +        |> json_response(:ok) + +      assert Enum.empty?(response) +    end + +    test "paginates favorites using since_id and max_id", %{ +      conn: conn, +      current_user: current_user, +      user: user +    } do +      activities = insert_list(10, :note_activity) + +      Enum.each(activities, fn activity -> +        CommonAPI.favorite(activity.id, user) +      end) + +      third_activity = Enum.at(activities, 2) +      seventh_activity = Enum.at(activities, 6) + +      response = +        conn +        |> assign(:user, current_user) +        |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{ +          since_id: third_activity.id, +          max_id: seventh_activity.id +        }) +        |> json_response(:ok) + +      assert length(response) == 3 +      refute third_activity in response +      refute seventh_activity in response +    end + +    test "limits favorites using limit parameter", %{ +      conn: conn, +      current_user: current_user, +      user: user +    } do +      7 +      |> insert_list(:note_activity) +      |> Enum.each(fn activity -> +        CommonAPI.favorite(activity.id, user) +      end) + +      response = +        conn +        |> assign(:user, current_user) +        |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"}) +        |> json_response(:ok) + +      assert length(response) == 3 +    end + +    test "returns empty response when user does not have any favorited statuses", %{ +      conn: conn, +      current_user: current_user, +      user: user +    } do +      response = +        conn +        |> assign(:user, current_user) +        |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") +        |> json_response(:ok) + +      assert Enum.empty?(response) +    end + +    test "returns 404 error when specified user is not exist", %{conn: conn} do +      conn = get(conn, "/api/v1/pleroma/accounts/test/favourites") + +      assert json_response(conn, 404) == %{"error" => "Record not found"} +    end + +    test "returns 403 error when user has hidden own favorites", %{ +      conn: conn, +      current_user: current_user +    } do +      user = insert(:user, %{info: %{hide_favorites: true}}) +      activity = insert(:note_activity) +      CommonAPI.favorite(activity.id, user) + +      conn = +        conn +        |> assign(:user, current_user) +        |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") + +      assert json_response(conn, 403) == %{"error" => "Can't get favorites"} +    end + +    test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do +      user = insert(:user) +      activity = insert(:note_activity) +      CommonAPI.favorite(activity.id, user) + +      conn = +        conn +        |> assign(:user, current_user) +        |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") + +      assert user.info.hide_favorites +      assert json_response(conn, 403) == %{"error" => "Can't get favorites"} +    end +  end + +  describe "subscribing / unsubscribing" do +    test "subscribing / unsubscribing to a user", %{conn: conn} do +      user = insert(:user) +      subscription_target = insert(:user) + +      conn = +        conn +        |> assign(:user, user) +        |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe") + +      assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200) + +      conn = +        build_conn() +        |> assign(:user, user) +        |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe") + +      assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200) +    end +  end + +  describe "subscribing" do +    test "returns 404 when subscription_target not found", %{conn: conn} do +      user = insert(:user) + +      conn = +        conn +        |> assign(:user, user) +        |> post("/api/v1/pleroma/accounts/target_id/subscribe") + +      assert %{"error" => "Record not found"} = json_response(conn, 404) +    end +  end + +  describe "unsubscribing" do +    test "returns 404 when subscription_target not found", %{conn: conn} do +      user = insert(:user) + +      conn = +        conn +        |> assign(:user, user) +        |> post("/api/v1/pleroma/accounts/target_id/unsubscribe") + +      assert %{"error" => "Record not found"} = json_response(conn, 404) +    end +  end +end diff --git a/test/web/pleroma_api/controllers/emoji_api_controller_test.exs b/test/web/pleroma_api/controllers/emoji_api_controller_test.exs new file mode 100644 index 000000000..5f74460e8 --- /dev/null +++ b/test/web/pleroma_api/controllers/emoji_api_controller_test.exs @@ -0,0 +1,463 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do +  use Pleroma.Web.ConnCase + +  import Tesla.Mock + +  import Pleroma.Factory + +  @emoji_dir_path Path.join( +                    Pleroma.Config.get!([:instance, :static_dir]), +                    "emoji" +                  ) + +  test "shared & non-shared pack information in list_packs is ok" do +    conn = build_conn() +    resp = conn |> get(emoji_api_path(conn, :list_packs)) |> json_response(200) + +    assert Map.has_key?(resp, "test_pack") + +    pack = resp["test_pack"] + +    assert Map.has_key?(pack["pack"], "download-sha256") +    assert pack["pack"]["can-download"] + +    assert pack["files"] == %{"blank" => "blank.png"} + +    # Non-shared pack + +    assert Map.has_key?(resp, "test_pack_nonshared") + +    pack = resp["test_pack_nonshared"] + +    refute pack["pack"]["shared"] +    refute pack["pack"]["can-download"] +  end + +  test "listing remote packs" do +    admin = insert(:user, info: %{is_admin: true}) +    conn = build_conn() |> assign(:user, admin) + +    resp = conn |> get(emoji_api_path(conn, :list_packs)) |> json_response(200) + +    mock(fn +      %{method: :get, url: "https://example.com/.well-known/nodeinfo"} -> +        json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]}) + +      %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} -> +        json(%{metadata: %{features: ["shareable_emoji_packs"]}}) + +      %{method: :get, url: "https://example.com/api/pleroma/emoji/packs"} -> +        json(resp) +    end) + +    assert conn +           |> post(emoji_api_path(conn, :list_from), %{instance_address: "https://example.com"}) +           |> json_response(200) == resp +  end + +  test "downloading a shared pack from download_shared" do +    conn = build_conn() + +    resp = +      conn +      |> get(emoji_api_path(conn, :download_shared, "test_pack")) +      |> response(200) + +    {:ok, arch} = :zip.unzip(resp, [:memory]) + +    assert Enum.find(arch, fn {n, _} -> n == 'pack.json' end) +    assert Enum.find(arch, fn {n, _} -> n == 'blank.png' end) +  end + +  test "downloading shared & unshared packs from another instance via download_from, deleting them" do +    on_exit(fn -> +      File.rm_rf!("#{@emoji_dir_path}/test_pack2") +      File.rm_rf!("#{@emoji_dir_path}/test_pack_nonshared2") +    end) + +    mock(fn +      %{method: :get, url: "https://old-instance/.well-known/nodeinfo"} -> +        json(%{links: [%{href: "https://old-instance/nodeinfo/2.1.json"}]}) + +      %{method: :get, url: "https://old-instance/nodeinfo/2.1.json"} -> +        json(%{metadata: %{features: []}}) + +      %{method: :get, url: "https://example.com/.well-known/nodeinfo"} -> +        json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]}) + +      %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} -> +        json(%{metadata: %{features: ["shareable_emoji_packs"]}}) + +      %{ +        method: :get, +        url: "https://example.com/api/pleroma/emoji/packs/list" +      } -> +        conn = build_conn() + +        conn +        |> get(emoji_api_path(conn, :list_packs)) +        |> json_response(200) +        |> json() + +      %{ +        method: :get, +        url: "https://example.com/api/pleroma/emoji/packs/download_shared/test_pack" +      } -> +        conn = build_conn() + +        conn +        |> get(emoji_api_path(conn, :download_shared, "test_pack")) +        |> response(200) +        |> text() + +      %{ +        method: :get, +        url: "https://nonshared-pack" +      } -> +        text(File.read!("#{@emoji_dir_path}/test_pack_nonshared/nonshared.zip")) +    end) + +    admin = insert(:user, info: %{is_admin: true}) + +    conn = build_conn() |> assign(:user, admin) + +    assert (conn +            |> put_req_header("content-type", "application/json") +            |> post( +              emoji_api_path( +                conn, +                :download_from +              ), +              %{ +                instance_address: "https://old-instance", +                pack_name: "test_pack", +                as: "test_pack2" +              } +              |> Jason.encode!() +            ) +            |> json_response(500))["error"] =~ "does not support" + +    assert conn +           |> put_req_header("content-type", "application/json") +           |> post( +             emoji_api_path( +               conn, +               :download_from +             ), +             %{ +               instance_address: "https://example.com", +               pack_name: "test_pack", +               as: "test_pack2" +             } +             |> Jason.encode!() +           ) +           |> json_response(200) == "ok" + +    assert File.exists?("#{@emoji_dir_path}/test_pack2/pack.json") +    assert File.exists?("#{@emoji_dir_path}/test_pack2/blank.png") + +    assert conn +           |> delete(emoji_api_path(conn, :delete, "test_pack2")) +           |> json_response(200) == "ok" + +    refute File.exists?("#{@emoji_dir_path}/test_pack2") + +    # non-shared, downloaded from the fallback URL + +    conn = build_conn() |> assign(:user, admin) + +    assert conn +           |> put_req_header("content-type", "application/json") +           |> post( +             emoji_api_path( +               conn, +               :download_from +             ), +             %{ +               instance_address: "https://example.com", +               pack_name: "test_pack_nonshared", +               as: "test_pack_nonshared2" +             } +             |> Jason.encode!() +           ) +           |> json_response(200) == "ok" + +    assert File.exists?("#{@emoji_dir_path}/test_pack_nonshared2/pack.json") +    assert File.exists?("#{@emoji_dir_path}/test_pack_nonshared2/blank.png") + +    assert conn +           |> delete(emoji_api_path(conn, :delete, "test_pack_nonshared2")) +           |> json_response(200) == "ok" + +    refute File.exists?("#{@emoji_dir_path}/test_pack_nonshared2") +  end + +  describe "updating pack metadata" do +    setup do +      pack_file = "#{@emoji_dir_path}/test_pack/pack.json" +      original_content = File.read!(pack_file) + +      on_exit(fn -> +        File.write!(pack_file, original_content) +      end) + +      {:ok, +       admin: insert(:user, info: %{is_admin: true}), +       pack_file: pack_file, +       new_data: %{ +         "license" => "Test license changed", +         "homepage" => "https://pleroma.social", +         "description" => "Test description", +         "share-files" => false +       }} +    end + +    test "for a pack without a fallback source", ctx do +      conn = build_conn() + +      assert conn +             |> assign(:user, ctx[:admin]) +             |> post( +               emoji_api_path(conn, :update_metadata, "test_pack"), +               %{ +                 "new_data" => ctx[:new_data] +               } +             ) +             |> json_response(200) == ctx[:new_data] + +      assert Jason.decode!(File.read!(ctx[:pack_file]))["pack"] == ctx[:new_data] +    end + +    test "for a pack with a fallback source", ctx do +      mock(fn +        %{ +          method: :get, +          url: "https://nonshared-pack" +        } -> +          text(File.read!("#{@emoji_dir_path}/test_pack_nonshared/nonshared.zip")) +      end) + +      new_data = Map.put(ctx[:new_data], "fallback-src", "https://nonshared-pack") + +      new_data_with_sha = +        Map.put( +          new_data, +          "fallback-src-sha256", +          "74409E2674DAA06C072729C6C8426C4CB3B7E0B85ED77792DB7A436E11D76DAF" +        ) + +      conn = build_conn() + +      assert conn +             |> assign(:user, ctx[:admin]) +             |> post( +               emoji_api_path(conn, :update_metadata, "test_pack"), +               %{ +                 "new_data" => new_data +               } +             ) +             |> json_response(200) == new_data_with_sha + +      assert Jason.decode!(File.read!(ctx[:pack_file]))["pack"] == new_data_with_sha +    end + +    test "when the fallback source doesn't have all the files", ctx do +      mock(fn +        %{ +          method: :get, +          url: "https://nonshared-pack" +        } -> +          {:ok, {'empty.zip', empty_arch}} = :zip.zip('empty.zip', [], [:memory]) +          text(empty_arch) +      end) + +      new_data = Map.put(ctx[:new_data], "fallback-src", "https://nonshared-pack") + +      conn = build_conn() + +      assert (conn +              |> assign(:user, ctx[:admin]) +              |> post( +                emoji_api_path(conn, :update_metadata, "test_pack"), +                %{ +                  "new_data" => new_data +                } +              ) +              |> json_response(:bad_request))["error"] =~ "does not have all" +    end +  end + +  test "updating pack files" do +    pack_file = "#{@emoji_dir_path}/test_pack/pack.json" +    original_content = File.read!(pack_file) + +    on_exit(fn -> +      File.write!(pack_file, original_content) + +      File.rm_rf!("#{@emoji_dir_path}/test_pack/blank_url.png") +      File.rm_rf!("#{@emoji_dir_path}/test_pack/dir") +      File.rm_rf!("#{@emoji_dir_path}/test_pack/dir_2") +    end) + +    admin = insert(:user, info: %{is_admin: true}) + +    conn = build_conn() + +    same_name = %{ +      "action" => "add", +      "shortcode" => "blank", +      "filename" => "dir/blank.png", +      "file" => %Plug.Upload{ +        filename: "blank.png", +        path: "#{@emoji_dir_path}/test_pack/blank.png" +      } +    } + +    different_name = %{same_name | "shortcode" => "blank_2"} + +    conn = conn |> assign(:user, admin) + +    assert (conn +            |> post(emoji_api_path(conn, :update_file, "test_pack"), same_name) +            |> json_response(:conflict))["error"] =~ "already exists" + +    assert conn +           |> post(emoji_api_path(conn, :update_file, "test_pack"), different_name) +           |> json_response(200) == %{"blank" => "blank.png", "blank_2" => "dir/blank.png"} + +    assert File.exists?("#{@emoji_dir_path}/test_pack/dir/blank.png") + +    assert conn +           |> post(emoji_api_path(conn, :update_file, "test_pack"), %{ +             "action" => "update", +             "shortcode" => "blank_2", +             "new_shortcode" => "blank_3", +             "new_filename" => "dir_2/blank_3.png" +           }) +           |> json_response(200) == %{"blank" => "blank.png", "blank_3" => "dir_2/blank_3.png"} + +    refute File.exists?("#{@emoji_dir_path}/test_pack/dir/") +    assert File.exists?("#{@emoji_dir_path}/test_pack/dir_2/blank_3.png") + +    assert conn +           |> post(emoji_api_path(conn, :update_file, "test_pack"), %{ +             "action" => "remove", +             "shortcode" => "blank_3" +           }) +           |> json_response(200) == %{"blank" => "blank.png"} + +    refute File.exists?("#{@emoji_dir_path}/test_pack/dir_2/") + +    mock(fn +      %{ +        method: :get, +        url: "https://test-blank/blank_url.png" +      } -> +        text(File.read!("#{@emoji_dir_path}/test_pack/blank.png")) +    end) + +    # The name should be inferred from the URL ending +    from_url = %{ +      "action" => "add", +      "shortcode" => "blank_url", +      "file" => "https://test-blank/blank_url.png" +    } + +    assert conn +           |> post(emoji_api_path(conn, :update_file, "test_pack"), from_url) +           |> json_response(200) == %{ +             "blank" => "blank.png", +             "blank_url" => "blank_url.png" +           } + +    assert File.exists?("#{@emoji_dir_path}/test_pack/blank_url.png") + +    assert conn +           |> post(emoji_api_path(conn, :update_file, "test_pack"), %{ +             "action" => "remove", +             "shortcode" => "blank_url" +           }) +           |> json_response(200) == %{"blank" => "blank.png"} + +    refute File.exists?("#{@emoji_dir_path}/test_pack/blank_url.png") +  end + +  test "creating and deleting a pack" do +    on_exit(fn -> +      File.rm_rf!("#{@emoji_dir_path}/test_created") +    end) + +    admin = insert(:user, info: %{is_admin: true}) + +    conn = build_conn() |> assign(:user, admin) + +    assert conn +           |> put_req_header("content-type", "application/json") +           |> put( +             emoji_api_path( +               conn, +               :create, +               "test_created" +             ) +           ) +           |> json_response(200) == "ok" + +    assert File.exists?("#{@emoji_dir_path}/test_created/pack.json") + +    assert Jason.decode!(File.read!("#{@emoji_dir_path}/test_created/pack.json")) == %{ +             "pack" => %{}, +             "files" => %{} +           } + +    assert conn +           |> delete(emoji_api_path(conn, :delete, "test_created")) +           |> json_response(200) == "ok" + +    refute File.exists?("#{@emoji_dir_path}/test_created/pack.json") +  end + +  test "filesystem import" do +    on_exit(fn -> +      File.rm!("#{@emoji_dir_path}/test_pack_for_import/emoji.txt") +      File.rm!("#{@emoji_dir_path}/test_pack_for_import/pack.json") +    end) + +    conn = build_conn() +    resp = conn |> get(emoji_api_path(conn, :list_packs)) |> json_response(200) + +    refute Map.has_key?(resp, "test_pack_for_import") + +    admin = insert(:user, info: %{is_admin: true}) + +    assert conn +           |> assign(:user, admin) +           |> post(emoji_api_path(conn, :import_from_fs)) +           |> json_response(200) == ["test_pack_for_import"] + +    resp = conn |> get(emoji_api_path(conn, :list_packs)) |> json_response(200) +    assert resp["test_pack_for_import"]["files"] == %{"blank" => "blank.png"} + +    File.rm!("#{@emoji_dir_path}/test_pack_for_import/pack.json") +    refute File.exists?("#{@emoji_dir_path}/test_pack_for_import/pack.json") + +    emoji_txt_content = "blank, blank.png, Fun\n\nblank2, blank.png" + +    File.write!("#{@emoji_dir_path}/test_pack_for_import/emoji.txt", emoji_txt_content) + +    assert conn +           |> assign(:user, admin) +           |> post(emoji_api_path(conn, :import_from_fs)) +           |> json_response(200) == ["test_pack_for_import"] + +    resp = conn |> get(emoji_api_path(conn, :list_packs)) |> json_response(200) + +    assert resp["test_pack_for_import"]["files"] == %{ +             "blank" => "blank.png", +             "blank2" => "blank.png" +           } +  end +end diff --git a/test/web/pleroma_api/controllers/mascot_controller_test.exs b/test/web/pleroma_api/controllers/mascot_controller_test.exs new file mode 100644 index 000000000..ae9539b04 --- /dev/null +++ b/test/web/pleroma_api/controllers/mascot_controller_test.exs @@ -0,0 +1,77 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaAPI.MascotControllerTest do +  use Pleroma.Web.ConnCase + +  alias Pleroma.User + +  import Pleroma.Factory + +  test "mascot upload", %{conn: conn} do +    user = insert(:user) + +    non_image_file = %Plug.Upload{ +      content_type: "audio/mpeg", +      path: Path.absname("test/fixtures/sound.mp3"), +      filename: "sound.mp3" +    } + +    conn = +      conn +      |> assign(:user, user) +      |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file}) + +    assert json_response(conn, 415) + +    file = %Plug.Upload{ +      content_type: "image/jpg", +      path: Path.absname("test/fixtures/image.jpg"), +      filename: "an_image.jpg" +    } + +    conn = +      build_conn() +      |> assign(:user, user) +      |> put("/api/v1/pleroma/mascot", %{"file" => file}) + +    assert %{"id" => _, "type" => image} = json_response(conn, 200) +  end + +  test "mascot retrieving", %{conn: conn} do +    user = insert(:user) +    # When user hasn't set a mascot, we should just get pleroma tan back +    conn = +      conn +      |> assign(:user, user) +      |> get("/api/v1/pleroma/mascot") + +    assert %{"url" => url} = json_response(conn, 200) +    assert url =~ "pleroma-fox-tan-smol" + +    # When a user sets their mascot, we should get that back +    file = %Plug.Upload{ +      content_type: "image/jpg", +      path: Path.absname("test/fixtures/image.jpg"), +      filename: "an_image.jpg" +    } + +    conn = +      build_conn() +      |> assign(:user, user) +      |> put("/api/v1/pleroma/mascot", %{"file" => file}) + +    assert json_response(conn, 200) + +    user = User.get_cached_by_id(user.id) + +    conn = +      build_conn() +      |> assign(:user, user) +      |> get("/api/v1/pleroma/mascot") + +    assert %{"url" => url, "type" => "image"} = json_response(conn, 200) +    assert url =~ "an_image" +  end +end diff --git a/test/web/pleroma_api/pleroma_api_controller_test.exs b/test/web/pleroma_api/controllers/pleroma_api_controller_test.exs index 7eaeda4a0..7eaeda4a0 100644 --- a/test/web/pleroma_api/pleroma_api_controller_test.exs +++ b/test/web/pleroma_api/controllers/pleroma_api_controller_test.exs diff --git a/test/web/pleroma_api/controllers/scrobble_controller_test.exs b/test/web/pleroma_api/controllers/scrobble_controller_test.exs new file mode 100644 index 000000000..881f8012c --- /dev/null +++ b/test/web/pleroma_api/controllers/scrobble_controller_test.exs @@ -0,0 +1,63 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaAPI.ScrobbleControllerTest do +  use Pleroma.Web.ConnCase + +  alias Pleroma.Web.CommonAPI +  import Pleroma.Factory + +  describe "POST /api/v1/pleroma/scrobble" do +    test "works correctly", %{conn: conn} do +      user = insert(:user) + +      conn = +        conn +        |> assign(:user, user) +        |> post("/api/v1/pleroma/scrobble", %{ +          "title" => "lain radio episode 1", +          "artist" => "lain", +          "album" => "lain radio", +          "length" => "180000" +        }) + +      assert %{"title" => "lain radio episode 1"} = json_response(conn, 200) +    end +  end + +  describe "GET /api/v1/pleroma/accounts/:id/scrobbles" do +    test "works correctly", %{conn: conn} do +      user = insert(:user) + +      {:ok, _activity} = +        CommonAPI.listen(user, %{ +          "title" => "lain radio episode 1", +          "artist" => "lain", +          "album" => "lain radio" +        }) + +      {:ok, _activity} = +        CommonAPI.listen(user, %{ +          "title" => "lain radio episode 2", +          "artist" => "lain", +          "album" => "lain radio" +        }) + +      {:ok, _activity} = +        CommonAPI.listen(user, %{ +          "title" => "lain radio episode 3", +          "artist" => "lain", +          "album" => "lain radio" +        }) + +      conn = +        conn +        |> get("/api/v1/pleroma/accounts/#{user.id}/scrobbles") + +      result = json_response(conn, 200) + +      assert length(result) == 3 +    end +  end +end diff --git a/test/web/twitter_api/password_controller_test.exs b/test/web/twitter_api/password_controller_test.exs index 3a7246ea8..dc6d4e3e3 100644 --- a/test/web/twitter_api/password_controller_test.exs +++ b/test/web/twitter_api/password_controller_test.exs @@ -6,6 +6,7 @@ defmodule Pleroma.Web.TwitterAPI.PasswordControllerTest do    use Pleroma.Web.ConnCase    alias Pleroma.PasswordResetToken +  alias Pleroma.User    alias Pleroma.Web.OAuth.Token    import Pleroma.Factory @@ -56,5 +57,25 @@ defmodule Pleroma.Web.TwitterAPI.PasswordControllerTest do        assert Comeonin.Pbkdf2.checkpw("test", user.password_hash)        assert length(Token.get_user_tokens(user)) == 0      end + +    test "it sets password_reset_pending to false", %{conn: conn} do +      user = insert(:user, info: %{password_reset_pending: true}) + +      {:ok, token} = PasswordResetToken.create_token(user) +      {:ok, _access_token} = Token.create_token(insert(:oauth_app), user, %{}) + +      params = %{ +        "password" => "test", +        password_confirmation: "test", +        token: token.token +      } + +      conn +      |> assign(:user, user) +      |> post("/api/pleroma/password_reset", %{data: params}) +      |> html_response(:ok) + +      assert User.get_by_id(user.id).info.password_reset_pending == false +    end    end  end diff --git a/test/web/twitter_api/twitter_api_test.exs b/test/web/twitter_api/twitter_api_test.exs index 08f264431..d1d61d11a 100644 --- a/test/web/twitter_api/twitter_api_test.exs +++ b/test/web/twitter_api/twitter_api_test.exs @@ -29,8 +29,8 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do      fetched_user = User.get_cached_by_nickname("lain") -    assert AccountView.render("account.json", %{user: user}) == -             AccountView.render("account.json", %{user: fetched_user}) +    assert AccountView.render("show.json", %{user: user}) == +             AccountView.render("show.json", %{user: fetched_user})    end    test "it registers a new user with empty string in bio and returns the user." do @@ -47,8 +47,8 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do      fetched_user = User.get_cached_by_nickname("lain") -    assert AccountView.render("account.json", %{user: user}) == -             AccountView.render("account.json", %{user: fetched_user}) +    assert AccountView.render("show.json", %{user: user}) == +             AccountView.render("show.json", %{user: fetched_user})    end    test "it sends confirmation email if :account_activation_required is specified in instance config" do @@ -109,7 +109,9 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do      {:ok, user2} = TwitterAPI.register_user(data2)      expected_text = -      "<span class='h-card'><a data-user='#{user1.id}' class='u-url mention' href='#{user1.ap_id}'>@<span>john</span></a></span> test" +      ~s(<span class="h-card"><a data-user="#{user1.id}" class="u-url mention" href="#{ +        user1.ap_id +      }" rel="ugc">@<span>john</span></a></span> test)      assert user2.bio == expected_text    end @@ -146,8 +148,8 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do        assert invite.used == true -      assert AccountView.render("account.json", %{user: user}) == -               AccountView.render("account.json", %{user: fetched_user}) +      assert AccountView.render("show.json", %{user: user}) == +               AccountView.render("show.json", %{user: fetched_user})      end      test "returns error on invalid token" do @@ -211,8 +213,8 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do          {:ok, user} = TwitterAPI.register_user(data)          fetched_user = User.get_cached_by_nickname("vinny") -        assert AccountView.render("account.json", %{user: user}) == -                 AccountView.render("account.json", %{user: fetched_user}) +        assert AccountView.render("show.json", %{user: user}) == +                 AccountView.render("show.json", %{user: fetched_user})        end        {:ok, data: data, check_fn: check_fn} @@ -286,8 +288,8 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do        assert invite.used == true -      assert AccountView.render("account.json", %{user: user}) == -               AccountView.render("account.json", %{user: fetched_user}) +      assert AccountView.render("show.json", %{user: user}) == +               AccountView.render("show.json", %{user: fetched_user})        data = %{          "nickname" => "GrimReaper", @@ -337,8 +339,8 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do        refute invite.used -      assert AccountView.render("account.json", %{user: user}) == -               AccountView.render("account.json", %{user: fetched_user}) +      assert AccountView.render("show.json", %{user: user}) == +               AccountView.render("show.json", %{user: fetched_user})      end      test "error after max uses" do @@ -361,8 +363,8 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do        invite = Repo.get_by(UserInviteToken, token: invite.token)        assert invite.used == true -      assert AccountView.render("account.json", %{user: user}) == -               AccountView.render("account.json", %{user: fetched_user}) +      assert AccountView.render("show.json", %{user: user}) == +               AccountView.render("show.json", %{user: fetched_user})        data = %{          "nickname" => "GrimReaper",  | 
