defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
use Pleroma.Web.ConnCase
+ use Oban.Testing, repo: Pleroma.Repo
alias Pleroma.Activity
- alias Pleroma.ActivityExpiration
alias Pleroma.Config
alias Pleroma.Conversation.Participation
alias Pleroma.Object
setup do: clear_config([:instance, :federating])
setup do: clear_config([:instance, :allow_relay])
setup do: clear_config([:rich_media, :enabled])
+ setup do: clear_config([:mrf, :policies])
+ setup do: clear_config([:mrf_keyword, :reject])
describe "posting statuses" do
setup do: oauth_access(["write:statuses"])
test "posting a status does not increment reblog_count when relaying", %{conn: conn} do
- Pleroma.Config.put([:instance, :federating], true)
- Pleroma.Config.get([:instance, :allow_relay], true)
+ Config.put([:instance, :federating], true)
+ Config.get([:instance, :allow_relay], true)
response =
conn
# An activity that will expire:
# 2 hours
- expires_in = 120 * 60
+ expires_in = 2 * 60 * 60
+
+ expires_at = DateTime.add(DateTime.utc_now(), expires_in)
conn_four =
conn
"expires_in" => expires_in
})
- assert fourth_response =
- %{"id" => fourth_id} = json_response_and_validate_schema(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)
+ assert %{"id" => fourth_id} = json_response_and_validate_schema(conn_four, 200)
- # 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 Activity.get_by_id(fourth_id)
- assert fourth_response["pleroma"]["expires_at"] ==
- NaiveDateTime.to_iso8601(expiration.scheduled_at)
+ assert_enqueued(
+ worker: Pleroma.Workers.PurgeExpiredActivity,
+ args: %{activity_id: fourth_id},
+ scheduled_at: expires_at
+ )
end
test "it fails to create a status if `expires_in` is less or equal than an hour", %{
conn: conn
} do
- # 1 hour
- expires_in = 60 * 60
+ # 1 minute
+ expires_in = 1 * 60
assert %{"error" => "Expiry date is too soon"} =
conn
})
|> json_response_and_validate_schema(422)
- # 30 minutes
- expires_in = 30 * 60
+ # 5 minutes
+ expires_in = 5 * 60
assert %{"error" => "Expiry date is too soon"} =
conn
|> json_response_and_validate_schema(422)
end
+ test "Get MRF reason when posting a status is rejected by one", %{conn: conn} do
+ Config.put([:mrf_keyword, :reject], ["GNO"])
+ Config.put([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.KeywordPolicy])
+
+ assert %{"error" => "[KeywordPolicy] Matches with rejected keyword"} =
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("api/v1/statuses", %{"status" => "GNO/Linux"})
+ |> json_response_and_validate_schema(422)
+ end
+
test "posting an undefined status with an attachment", %{user: user, conn: conn} do
file = %Plug.Upload{
content_type: "image/jpg",
assert real_status == fake_status
end
+ test "fake statuses' preview card is not cached", %{conn: conn} do
+ clear_config([:rich_media, :enabled], true)
+
+ Tesla.Mock.mock(fn
+ %{
+ method: :get,
+ url: "https://example.com/twitter-card"
+ } ->
+ %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/twitter_card.html")}
+
+ env ->
+ apply(HttpRequestMock, :request, [env])
+ end)
+
+ conn1 =
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/v1/statuses", %{
+ "status" => "https://example.com/ogp",
+ "preview" => true
+ })
+
+ conn2 =
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/v1/statuses", %{
+ "status" => "https://example.com/twitter-card",
+ "preview" => true
+ })
+
+ assert %{"card" => %{"title" => "The Rock"}} = json_response_and_validate_schema(conn1, 200)
+
+ assert %{"card" => %{"title" => "Small Island Developing States Photo Submission"}} =
+ json_response_and_validate_schema(conn2, 200)
+ 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)
+ clear_config([:rich_media, :enabled], true)
conn =
conn
test "when you created it" do
%{user: author, conn: conn} = oauth_access(["write:statuses"])
activity = insert(:note_activity, user: author)
+ object = Object.normalize(activity)
- conn =
+ content = object.data["content"]
+ source = object.data["source"]
+
+ result =
conn
|> assign(:user, author)
|> delete("/api/v1/statuses/#{activity.id}")
+ |> json_response_and_validate_schema(200)
- assert %{} = json_response_and_validate_schema(conn, 200)
+ assert match?(%{"content" => ^content, "text" => ^source}, result)
refute Activity.get_by_id(activity.id)
end
conn = delete(conn, "/api/v1/statuses/#{activity.id}")
- assert %{"error" => _} = json_response_and_validate_schema(conn, 403)
+ assert %{"error" => "Record not found"} == json_response_and_validate_schema(conn, 404)
assert Activity.get_by_id(activity.id) == activity
end
user3 = insert(:user)
{:ok, _} = CommonAPI.favorite(user2, activity.id)
{: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)
+ {:ok, reblog_activity1} = CommonAPI.repeat(activity.id, user1)
+ {:ok, _} = CommonAPI.repeat(activity.id, user2)
conn_res =
build_conn()
test "unreblogs and returns the unreblogged status", %{user: user, conn: conn} do
activity = insert(:note_activity)
- {:ok, _, _} = CommonAPI.repeat(activity.id, user)
+ {:ok, _} = CommonAPI.repeat(activity.id, user)
conn =
conn
|> post("/api/v1/statuses/#{activity_two.id}/pin")
|> json_response_and_validate_schema(400)
end
+
+ test "on pin removes deletion job, on unpin reschedule deletion" do
+ %{conn: conn} = oauth_access(["write:accounts", "write:statuses"])
+ expires_in = 2 * 60 * 60
+
+ expires_at = DateTime.add(DateTime.utc_now(), expires_in)
+
+ assert %{"id" => id} =
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("api/v1/statuses", %{
+ "status" => "oolong",
+ "expires_in" => expires_in
+ })
+ |> json_response_and_validate_schema(200)
+
+ assert_enqueued(
+ worker: Pleroma.Workers.PurgeExpiredActivity,
+ args: %{activity_id: id},
+ scheduled_at: expires_at
+ )
+
+ assert %{"id" => ^id, "pinned" => true} =
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/v1/statuses/#{id}/pin")
+ |> json_response_and_validate_schema(200)
+
+ refute_enqueued(
+ worker: Pleroma.Workers.PurgeExpiredActivity,
+ args: %{activity_id: id},
+ scheduled_at: expires_at
+ )
+
+ assert %{"id" => ^id, "pinned" => false} =
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/v1/statuses/#{id}/unpin")
+ |> json_response_and_validate_schema(200)
+
+ assert_enqueued(
+ worker: Pleroma.Workers.PurgeExpiredActivity,
+ args: %{activity_id: id},
+ scheduled_at: expires_at
+ )
+ end
end
describe "cards" do
[%{"id" => id}] = response
assert id == other_user.id
end
+
+ test "returns empty array when :show_reactions is disabled", %{conn: conn, activity: activity} do
+ clear_config([:instance, :show_reactions], false)
+
+ other_user = insert(:user)
+ {:ok, _} = CommonAPI.favorite(other_user, activity.id)
+
+ response =
+ conn
+ |> get("/api/v1/statuses/#{activity.id}/favourited_by")
+ |> json_response_and_validate_schema(:ok)
+
+ assert Enum.empty?(response)
+ end
end
describe "GET /api/v1/statuses/:id/reblogged_by" do
test "returns users who have reblogged the status", %{conn: conn, activity: activity} do
other_user = insert(:user)
- {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
+ {:ok, _} = CommonAPI.repeat(activity.id, other_user)
response =
conn
other_user = insert(:user)
{:ok, _user_relationship} = User.block(user, other_user)
- {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
+ {:ok, _} = CommonAPI.repeat(activity.id, other_user)
response =
conn
end
test "does not return users who have reblogged the status privately", %{
- conn: conn,
- activity: activity
+ conn: conn
} do
other_user = insert(:user)
+ {:ok, activity} = CommonAPI.post(other_user, %{status: "my secret post"})
- {:ok, _, _} = CommonAPI.repeat(activity.id, other_user, %{visibility: "private"})
+ {:ok, _} = CommonAPI.repeat(activity.id, other_user, %{visibility: "private"})
response =
conn
test "does not fail on an unauthenticated request", %{activity: activity} do
other_user = insert(:user)
- {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
+ {:ok, _} = CommonAPI.repeat(activity.id, other_user)
response =
build_conn()
} = response
end
+ test "favorites paginate correctly" do
+ %{user: user, conn: conn} = oauth_access(["read:favourites"])
+ other_user = insert(:user)
+ {:ok, first_post} = CommonAPI.post(other_user, %{status: "bla"})
+ {:ok, second_post} = CommonAPI.post(other_user, %{status: "bla"})
+ {:ok, third_post} = CommonAPI.post(other_user, %{status: "bla"})
+
+ {:ok, _first_favorite} = CommonAPI.favorite(user, third_post.id)
+ {:ok, _second_favorite} = CommonAPI.favorite(user, first_post.id)
+ {:ok, third_favorite} = CommonAPI.favorite(user, second_post.id)
+
+ result =
+ conn
+ |> get("/api/v1/favourites?limit=1")
+
+ assert [%{"id" => post_id}] = json_response_and_validate_schema(result, 200)
+ assert post_id == second_post.id
+
+ # Using the header for pagination works correctly
+ [next, _] = get_resp_header(result, "link") |> hd() |> String.split(", ")
+ [_, max_id] = Regex.run(~r/max_id=([^&]+)/, next)
+
+ assert max_id == third_favorite.id
+
+ result =
+ conn
+ |> get("/api/v1/favourites?max_id=#{max_id}")
+
+ assert [%{"id" => first_post_id}, %{"id" => third_post_id}] =
+ json_response_and_validate_schema(result, 200)
+
+ assert first_post_id == first_post.id
+ assert third_post_id == third_post.id
+ end
+
test "returns the favorites of a user" do
%{user: user, conn: conn} = oauth_access(["read:favourites"])
other_user = insert(:user)
{:ok, _} = CommonAPI.post(other_user, %{status: "bla"})
- {:ok, activity} = CommonAPI.post(other_user, %{status: "traps are happy"})
+ {:ok, activity} = CommonAPI.post(other_user, %{status: "trees are happy"})
- {:ok, _} = CommonAPI.favorite(user, activity.id)
+ {:ok, last_like} = CommonAPI.favorite(user, activity.id)
first_conn = get(conn, "/api/v1/favourites")
{:ok, _} = CommonAPI.favorite(user, second_activity.id)
- last_like = status["id"]
-
- second_conn = get(conn, "/api/v1/favourites?since_id=#{last_like}")
+ second_conn = get(conn, "/api/v1/favourites?since_id=#{last_like.id}")
assert [second_status] = json_response_and_validate_schema(second_conn, 200)
assert second_status["id"] == to_string(second_activity.id)
test "expires_at is nil for another user" do
%{conn: conn, user: user} = oauth_access(["read:statuses"])
+ expires_at = DateTime.add(DateTime.utc_now(), 1_000_000)
{:ok, activity} = CommonAPI.post(user, %{status: "foobar", expires_in: 1_000_000})
- expires_at =
- activity.id
- |> ActivityExpiration.get_by_activity_id()
- |> Map.get(:scheduled_at)
- |> NaiveDateTime.to_iso8601()
-
- assert %{"pleroma" => %{"expires_at" => ^expires_at}} =
+ assert %{"pleroma" => %{"expires_at" => a_expires_at}} =
conn
|> get("/api/v1/statuses/#{activity.id}")
|> json_response_and_validate_schema(:ok)
+ {:ok, a_expires_at, 0} = DateTime.from_iso8601(a_expires_at)
+ assert DateTime.diff(expires_at, a_expires_at) == 0
+
%{conn: conn} = oauth_access(["read:statuses"])
assert %{"pleroma" => %{"expires_at" => nil}} =