Add some controller tests, fixes

Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
marcin mikołajczak 2022-08-23 00:05:53 +02:00
parent 2aae16d810
commit d967e1f55e
11 changed files with 443 additions and 63 deletions

View file

@ -23,8 +23,8 @@ defmodule Pleroma.Constants do
"assigned_account",
"rules",
"content_type",
"participants",
"participant_count"
"participations",
"participation_count"
]
)

View file

@ -525,7 +525,7 @@ def create_poll_notifications(%Activity{} = activity) do
def create_event_notifications(%Activity{} = activity) do
with %Object{data: %{"type" => "Event", "actor" => actor} = data} <-
Object.normalize(activity) do
participations =
participations =
case data do
%{"participations" => participations} when is_list(participations) -> participations
_ -> []

View file

@ -77,6 +77,9 @@ defmacro event_object_fields do
field(:joinMode, :string, default: "free")
embeds_one(:location, PlaceValidator)
field(:participation_count, :integer, default: 0)
field(:participations, {:array, ObjectValidators.ObjectID}, default: [])
end
end
end

View file

@ -6,6 +6,7 @@ defmodule Pleroma.Web.ApiSpec.PleromaEventOperation do
alias OpenApiSpex.Operation
alias OpenApiSpex.Schema
alias Pleroma.Web.ApiSpec.AccountOperation
alias Pleroma.Web.ApiSpec.Schemas.Account
alias Pleroma.Web.ApiSpec.Schemas.ApiError
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
alias Pleroma.Web.ApiSpec.Schemas.ParticipationRequest
@ -48,6 +49,7 @@ def participations_operation do
"application/json",
AccountOperation.array_of_accounts()
),
403 => Operation.response("Error", "application/json", ApiError),
404 => Operation.response("Not Found", "application/json", ApiError)
}
}
@ -68,6 +70,7 @@ def participation_requests_operation do
"application/json",
array_of_participation_requests()
),
403 => Operation.response("Forbidden", "application/json", ApiError),
404 => Operation.response("Not Found", "application/json", ApiError)
}
}
@ -87,6 +90,7 @@ def participate_operation do
%Schema{
type: :object,
properties: %{
account: Account,
participation_message: %Schema{
type: :string,
description: "Why the user wants to participate"
@ -97,6 +101,7 @@ def participate_operation do
),
responses: %{
200 => event_response(),
400 => Operation.response("Error", "application/json", ApiError),
404 => Operation.response("Not Found", "application/json", ApiError)
}
}
@ -112,6 +117,7 @@ def unparticipate_operation do
parameters: [id_param()],
responses: %{
200 => event_response(),
400 => Operation.response("Error", "application/json", ApiError),
404 => Operation.response("Not Found", "application/json", ApiError)
}
}
@ -158,6 +164,7 @@ defp create_request do
},
status: %Schema{
type: :string,
nullable: true,
description: "Text description of the event."
},
banner_id: %Schema{

View file

@ -346,11 +346,15 @@ def join_helper(user, id, participation_message) do
end
def leave(%User{ap_id: participant_ap_id} = user, event_id) do
with %Activity{} = join_activity <- Utils.get_existing_join(participant_ap_id, event_id),
with %Activity{data: %{"object" => event_ap_id}} <- Activity.get_by_id(event_id),
%Activity{} = join_activity <- Utils.get_existing_join(participant_ap_id, event_ap_id),
{:ok, undo, _} <- Builder.undo(user, join_activity),
{:ok, activity, _} <- Pipeline.common_pipeline(undo, local: true) do
{:ok, activity}
else
nil ->
{:error, dgettext("errors", "Not participating in the event")}
_ ->
{:error, dgettext("errors", "Could not remove join activity")}
end

View file

@ -111,16 +111,14 @@ defp event_object(draft) do
emoji = Map.merge(Pleroma.Emoji.Formatter.get_emoji_map(draft.full_payload), draft.emoji)
object =
draft.params
|> Map.take([:name])
%{}
|> Map.put("name", draft.params[:name])
|> Map.put("type", "Event")
|> Map.put("to", draft.to)
|> Map.put("cc", draft.cc)
|> Map.put("content", draft.content_html)
|> Map.put("actor", draft.user.ap_id)
# |> Map.put("startTime", draft.params[:start_time])
# |> Map.put("endTime", draft.params[:end_time])
|> Map.put("joinMode", draft.params[:join_mode])
|> Map.put("joinMode", draft.params[:join_mode] || "free")
|> Map.put("tag", Keyword.values(draft.tags) |> Enum.uniq())
|> Map.put("emoji", emoji)
@ -337,8 +335,8 @@ defp event_date(draft) do
else
object =
draft.object
|> Map.put("startTime", start_time)
|> Map.put("endTime", end_time)
|> Map.put("startTime", start_time |> DateTime.to_iso8601())
|> Map.put("endTime", end_time |> DateTime.to_iso8601())
%__MODULE__{draft | object: object}
end
@ -346,7 +344,7 @@ defp event_date(draft) do
_ ->
object =
draft.object
|> Map.put("startTime", start_time)
|> Map.put("startTime", start_time |> DateTime.to_iso8601())
%__MODULE__{draft | object: object}
end

View file

@ -6,7 +6,7 @@ defmodule Pleroma.Web.PleromaAPI.EventController do
use Pleroma.Web, :controller
import Pleroma.Web.ControllerHelper,
only: [add_link_headers: 2, try_render: 3]
only: [add_link_headers: 2, json_response: 3, try_render: 3]
require Ecto.Query
@ -30,6 +30,18 @@ defmodule Pleroma.Web.PleromaAPI.EventController do
when action in [:accept_participation_request, :reject_participation_request]
)
plug(
:assign_event_activity
when action in [
:participations,
:participation_requests,
:accept_participation_request,
:reject_participation_request,
:participate,
:unparticipate
]
)
plug(
OAuthScopesPlug,
%{scopes: ["write"]}
@ -51,6 +63,10 @@ defmodule Pleroma.Web.PleromaAPI.EventController do
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaEventOperation
def create(%{assigns: %{user: user}, body_params: params} = conn, _) do
params =
params
|> Map.put(:status, Map.get(params, :status, ""))
with {:ok, activity} <- CommonAPI.event(user, params) do
conn
|> put_view(StatusView)
@ -72,10 +88,8 @@ def create(%{assigns: %{user: user}, body_params: params} = conn, _) do
end
end
def participations(%{assigns: %{user: user}} = conn, %{id: activity_id}) do
with %Activity{} = activity <- Activity.get_by_id_with_object(activity_id),
{:visible, true} <- {:visible, Visibility.visible_for_user?(activity, user)},
%Object{data: %{"participations" => participations}} <-
def participations(%{assigns: %{user: user, event_activity: activity}} = conn, _) do
with %Object{data: %{"participations" => participations}} <-
Object.normalize(activity, fetch: false) do
users =
User
@ -93,58 +107,83 @@ def participations(%{assigns: %{user: user}} = conn, %{id: activity_id}) do
end
def participation_requests(
%{assigns: %{user: %{ap_id: user_ap_id} = for_user}} = conn,
%{id: activity_id} = params
) do
with %Activity{object: %Object{data: %{"id" => ap_id, "actor" => ^user_ap_id}}} <-
Activity.get_by_id_with_object(activity_id) do
params =
%{assigns: %{user: %{ap_id: user_ap_id} = for_user, event_activity: activity}} = conn,
params
|> Map.put(:type, "Join")
|> Map.put(:object, ap_id)
|> Map.put(:state, "pending")
) do
case activity do
%Activity{actor: ^user_ap_id, data: %{"object" => ap_id}} ->
params =
params
|> Map.put(:type, "Join")
|> Map.put(:object, ap_id)
|> Map.put(:state, "pending")
activities =
[]
|> ActivityPub.fetch_activities_query(params)
|> Pagination.fetch_paginated(params)
activities =
[]
|> ActivityPub.fetch_activities_query(params)
|> Pagination.fetch_paginated(params)
conn
|> add_link_headers(activities)
|> put_view(EventView)
|> render("participation_requests.json",
activities: activities,
for: for_user,
as: :activity
)
conn
|> add_link_headers(activities)
|> put_view(EventView)
|> render("participation_requests.json",
activities: activities,
for: for_user,
as: :activity
)
%Activity{} ->
render_error(conn, :forbidden, "Can't get participation requests")
{:error, error} ->
json_response(conn, :bad_request, %{error: error})
end
end
def participate(%{assigns: %{user: user}, body_params: params} = conn, %{id: event_id}) do
with {:ok, _} <- CommonAPI.join(user, event_id, params),
%Activity{} = activity <- Activity.get_by_id(event_id) do
def participate(%{assigns: %{user: %{ap_id: actor}, event_activity: %{actor: actor}}} = conn, _) do
render_error(conn, :bad_request, "Can't join your own event")
end
def participate(
%{assigns: %{user: user, event_activity: activity}, body_params: params} = conn,
_
) do
with {:ok, _} <- CommonAPI.join(user, activity.id, params) do
conn
|> put_view(StatusView)
|> try_render("show.json", activity: activity, for: user, as: :activity)
end
end
def unparticipate(%{assigns: %{user: user}} = conn, %{id: event_id}) do
with {:ok, _} <- CommonAPI.leave(user, event_id),
%Activity{} = activity <- Activity.get_by_id(event_id) do
def unparticipate(
%{assigns: %{user: %{ap_id: actor}, event_activity: %{actor: actor}}} = conn,
_
) do
render_error(conn, :bad_request, "Can't leave your own event")
end
def unparticipate(%{assigns: %{user: user, event_activity: activity}} = conn, _) do
with {:ok, _} <- CommonAPI.leave(user, activity.id) do
conn
|> put_view(StatusView)
|> try_render("show.json", activity: activity, for: user, as: :activity)
else
{:error, error} ->
json_response(conn, :bad_request, %{error: error})
end
end
def accept_participation_request(
%{assigns: %{user: for_user, participant: participant}} = conn,
%{id: event_id}
%{
assigns: %{
user: for_user,
participant: participant,
event_activity: %Activity{data: %{"object" => ap_id}} = activity
}
} = conn,
_
) do
with %Activity{data: %{"object" => ap_id}} <- Activity.get_by_id(event_id),
{:ok, _} <- CommonAPI.accept_join_request(for_user, participant, ap_id),
%Activity{} = activity <- Activity.get_by_id(event_id) do
with {:ok, _} <- CommonAPI.accept_join_request(for_user, participant, ap_id) do
conn
|> put_view(StatusView)
|> try_render("show.json", activity: activity, for: for_user, as: :activity)
@ -152,12 +191,16 @@ def accept_participation_request(
end
def reject_participation_request(
%{assigns: %{user: for_user, participant: participant}} = conn,
%{id: event_id}
%{
assigns: %{
user: for_user,
participant: participant,
event_activity: %Activity{data: %{"object" => ap_id}} = activity
}
} = conn,
_
) do
with %Activity{data: %{"object" => ap_id}} <- Activity.get_by_id(event_id),
{:ok, _} <- CommonAPI.reject_join_request(for_user, participant, ap_id),
%Activity{} = activity <- Activity.get_by_id(event_id) do
with {:ok, _} <- CommonAPI.reject_join_request(for_user, participant, ap_id) do
conn
|> put_view(StatusView)
|> try_render("show.json", activity: activity, for: for_user, as: :activity)
@ -170,4 +213,13 @@ defp assign_participant(%{params: %{participant_id: id}} = conn, _) do
nil -> Pleroma.Web.MastodonAPI.FallbackController.call(conn, {:error, :not_found}) |> halt()
end
end
defp assign_event_activity(%{assigns: %{user: user}, params: %{id: event_id}} = conn, _) do
with %Activity{} = activity <- Activity.get_by_id(event_id),
{:visible, true} <- {:visible, Visibility.visible_for_user?(activity, user)} do
assign(conn, :event_activity, activity)
else
nil -> Pleroma.Web.MastodonAPI.FallbackController.call(conn, {:error, :not_found}) |> halt()
end
end
end

View file

@ -28,17 +28,17 @@ defp find_event_activity(activity_id) do
def schedule_event_reminder(%Activity{data: %{"type" => "Create"}, id: activity_id} = activity) do
with %Object{data: %{"type" => "Event", "startTime" => start_time}} <-
Object.normalize(activity),
{:ok, start_time} <- NaiveDateTime.from_iso8601(start_time),
{:ok, start_time} <- DateTime.from_iso8601(start_time),
:lt <-
NaiveDateTime.compare(
start_time |> NaiveDateTime.add(60 * 60 * -2, :second),
NaiveDateTime.utc_now()
DateTime.compare(
start_time |> DateTime.add(60 * 60 * -2, :second),
DateTime.utc_now()
) do
%{
op: "event_reminder",
activity_id: activity_id
}
|> new(scheduled_at: start_time |> NaiveDateTime.add(60 * 60 * -2, :second))
|> new(scheduled_at: start_time |> DateTime.add(60 * 60 * -2, :second))
|> Oban.insert()
else
_ -> {:error, activity}

View file

@ -133,7 +133,13 @@ test "creates notification for event join request" do
user = insert(:user)
other_user = insert(:user)
{:ok, activity} = CommonAPI.event(user, %{name: "test event", status: "", join_mode: "restricted"})
{:ok, activity} =
CommonAPI.event(user, %{
name: "test event",
status: "",
join_mode: "restricted",
start_time: DateTime.from_iso8601("2023-01-01T01:00:00.000Z") |> elem(1)
})
CommonAPI.join(other_user, activity.id)
@ -145,7 +151,13 @@ test "creates notification for event join approval" do
user = insert(:user)
other_user = insert(:user)
{:ok, activity} = CommonAPI.event(user, %{name: "test event", status: "", join_mode: "restricted"})
{:ok, activity} =
CommonAPI.event(user, %{
name: "test event",
status: "",
join_mode: "restricted",
start_time: DateTime.from_iso8601("2023-01-01T01:00:00.000Z") |> elem(1)
})
CommonAPI.join(other_user, activity.id)
@ -159,7 +171,13 @@ test "doesn't create notification for events without participation approval" do
user = insert(:user)
other_user = insert(:user)
{:ok, activity} = CommonAPI.event(user, %{name: "test event", status: "", join_mode: "free"})
{:ok, activity} =
CommonAPI.event(user, %{
name: "test event",
status: "",
join_mode: "free",
start_time: DateTime.from_iso8601("2023-01-01T01:00:00.000Z") |> elem(1)
})
CommonAPI.join(other_user, activity.id)

View file

@ -796,7 +796,7 @@ test "increases replies count", %{user: user} do
assert object.data["repliesCount"] == 2
end
test "increates quotes count", %{user: user} do
test "increases quotes count", %{user: user} do
user2 = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{status: "1", visibility: "public"})

View file

@ -0,0 +1,298 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.PleromaAPI.EventControllerTest do
use Pleroma.Web.ConnCase
use Oban.Testing, repo: Pleroma.Repo
alias Pleroma.Object
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.CommonAPI
import Pleroma.Factory
describe "POST /api/v1/pleroma/events" do
setup do
user = insert(:user)
%{user: user, conn: conn} = oauth_access(["write"], user: user)
[current_user: user, conn: conn]
end
test "creates an event", %{conn: conn} do
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/pleroma/events", %{
"name" => "Event name",
"start_time" => "2023-01-01T01:00:00.000Z",
"end_time" => "2023-01-01T04:00:00.000Z",
"join_mode" => "free"
})
assert %{
"pleroma" => %{
"event" => %{
"name" => "Event name",
"join_mode" => "free"
}
}
} = json_response_and_validate_schema(conn, 200)
end
test "can't create event that ends before its start", %{conn: conn} do
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/pleroma/events", %{
"name" => "Event name",
"start_time" => "2023-01-01T04:00:00.000Z",
"end_time" => "2022-12-31T04:00:00.000Z",
"join_mode" => "free"
})
assert json_response_and_validate_schema(conn, 422) == %{
"error" => "Event can't end before its start"
}
end
end
test "GET /api/v1/pleroma/events/:id/participations" do
%{conn: conn} = oauth_access(["read"])
user_one = insert(:user)
user_two = insert(:user)
user_three = insert(:user)
{:ok, activity} =
CommonAPI.event(user_one, %{
name: "test event",
status: "",
join_mode: "free",
start_time: DateTime.from_iso8601("2023-01-01T01:00:00.000Z") |> elem(1)
})
CommonAPI.join(user_two, activity.id)
CommonAPI.join(user_three, activity.id)
conn =
conn
|> get("/api/v1/pleroma/events/#{activity.id}/participations")
assert response = json_response_and_validate_schema(conn, 200)
assert length(response) == 2
end
describe "GET /api/v1/pleroma/events/:id/participation_requests" do
setup do
user = insert(:user)
%{user: user, conn: conn} = oauth_access(["read"], user: user)
[current_user: user, conn: conn]
end
test "show participation requests", %{conn: conn, current_user: user} do
other_user = insert(:user)
{:ok, activity} =
CommonAPI.event(user, %{
name: "test event",
status: "",
join_mode: "restricted",
start_time: DateTime.from_iso8601("2023-01-01T01:00:00.000Z") |> elem(1)
})
CommonAPI.join(other_user, activity.id, %{
participation_message: "I'm interested in this event"
})
conn =
conn
|> get("/api/v1/pleroma/events/#{activity.id}/participation_requests")
assert [
%{
"participation_message" => "I'm interested in this event"
}
] = response = json_response_and_validate_schema(conn, 200)
assert length(response) == 1
end
test "don't display requests if not an author", %{conn: conn} do
other_user = insert(:user)
{:ok, activity} =
CommonAPI.event(other_user, %{
name: "test event",
status: "",
start_time: DateTime.from_iso8601("2023-01-01T01:00:00.000Z") |> elem(1)
})
conn =
conn
|> get("/api/v1/pleroma/events/#{activity.id}/participation_requests")
assert %{"error" => "Can't get participation requests"} =
json_response_and_validate_schema(conn, 403)
end
end
describe "POST /api/v1/pleroma/events/:id/participate" do
setup do
user = insert(:user)
%{user: user, conn: conn} = oauth_access(["write"], user: user)
[current_user: user, conn: conn]
end
test "joins an event", %{conn: conn} do
other_user = insert(:user)
{:ok, activity} =
CommonAPI.event(other_user, %{
name: "test event",
status: "",
start_time: DateTime.from_iso8601("2023-01-01T01:00:00.000Z") |> elem(1)
})
conn =
conn
|> post("/api/v1/pleroma/events/#{activity.id}/participate")
assert json_response_and_validate_schema(conn, 200)
assert %{
data: %{
"participation_count" => 1
}
} = Object.get_by_ap_id(activity.data["object"])
end
test "can't participate in your own event", %{conn: conn, current_user: user} do
{:ok, activity} =
CommonAPI.event(user, %{
name: "test event",
status: "",
start_time: DateTime.from_iso8601("2023-01-01T01:00:00.000Z") |> elem(1)
})
conn =
conn
|> post("/api/v1/pleroma/events/#{activity.id}/participate")
assert json_response_and_validate_schema(conn, :bad_request) == %{
"error" => "Can't join your own event"
}
end
end
describe "POST /api/v1/pleroma/events/:id/unparticipate" do
setup do
user = insert(:user)
%{user: user, conn: conn} = oauth_access(["write"], user: user)
[current_user: user, conn: conn]
end
test "leaves an event", %{conn: conn, current_user: user} do
other_user = insert(:user)
{:ok, activity} =
CommonAPI.event(other_user, %{
name: "test event",
status: "",
start_time: DateTime.from_iso8601("2023-01-01T01:00:00.000Z") |> elem(1)
})
CommonAPI.join(user, activity.id)
conn =
conn
|> post("/api/v1/pleroma/events/#{activity.id}/unparticipate")
assert json_response_and_validate_schema(conn, 200)
assert %{
data: %{
"participation_count" => 0
}
} = Object.get_by_ap_id(activity.data["object"])
end
test "can't leave event you are not participating in", %{conn: conn} do
other_user = insert(:user)
{:ok, activity} =
CommonAPI.event(other_user, %{
name: "test event",
status: "",
start_time: DateTime.from_iso8601("2023-01-01T01:00:00.000Z") |> elem(1)
})
conn =
conn
|> post("/api/v1/pleroma/events/#{activity.id}/unparticipate")
assert json_response_and_validate_schema(conn, :bad_request) == %{
"error" => "Not participating in the event"
}
end
end
test "POST /api/v1/pleroma/events/:id/participation_requests/:participant_id/accept" do
[user, %{ap_id: ap_id} = other_user] = insert_pair(:user)
%{user: user, conn: conn} = oauth_access(["write"], user: user)
{:ok, activity} =
CommonAPI.event(user, %{
name: "test event",
status: "",
join_mode: "restricted",
start_time: DateTime.from_iso8601("2023-01-01T01:00:00.000Z") |> elem(1)
})
CommonAPI.join(other_user, activity.id)
conn =
conn
|> post(
"/api/v1/pleroma/events/#{activity.id}/participation_requests/#{other_user.id}/accept"
)
assert json_response_and_validate_schema(conn, 200)
assert %{
data: %{
"participations" => [^ap_id],
"participation_count" => 1
}
} = Object.get_by_ap_id(activity.data["object"])
assert %{data: %{"state" => "accept"}} =
Utils.get_existing_join(other_user.ap_id, activity.data["object"])
end
test "POST /api/v1/pleroma/events/:id/participation_requests/:participant_id/reject" do
[user, other_user] = insert_pair(:user)
%{user: user, conn: conn} = oauth_access(["write"], user: user)
{:ok, activity} =
CommonAPI.event(user, %{
name: "test event",
status: "",
join_mode: "restricted",
start_time: DateTime.from_iso8601("2023-01-01T01:00:00.000Z") |> elem(1)
})
CommonAPI.join(other_user, activity.id)
conn =
conn
|> post(
"/api/v1/pleroma/events/#{activity.id}/participation_requests/#{other_user.id}/reject"
)
assert json_response_and_validate_schema(conn, 200)
assert %{data: %{"state" => "reject"}} =
Utils.get_existing_join(other_user.ap_id, activity.data["object"])
end
end