+
+ 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)
+
+ {:ok, activity} =
+ CommonAPI.post(user, %{
+ "status" => "Pleroma does",
+ "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
+ })
+
+ object = Object.normalize(activity)
+
+ conn =
+ conn
+ |> assign(:user, user)
+ |> get("/api/v1/polls/#{object.id}")
+
+ response = json_response(conn, 200)
+ id = object.id
+ assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
+ end
+
+ test "does not expose polls for private statuses", %{conn: conn} do
+ user = insert(:user)
+ other_user = insert(:user)
+
+ {:ok, activity} =
+ CommonAPI.post(user, %{
+ "status" => "Pleroma does",
+ "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
+ "visibility" => "private"
+ })
+
+ object = Object.normalize(activity)
+
+ conn =
+ conn
+ |> assign(:user, other_user)
+ |> get("/api/v1/polls/#{object.id}")
+
+ assert json_response(conn, 404)
+ end
+ end
+
+ describe "POST /api/v1/polls/:id/votes" do
+ test "votes are added to the poll", %{conn: conn} do
+ user = insert(:user)
+ other_user = insert(:user)
+
+ {:ok, activity} =
+ CommonAPI.post(user, %{
+ "status" => "A very delicious sandwich",
+ "poll" => %{
+ "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
+ "expires_in" => 20,
+ "multiple" => true
+ }
+ })
+
+ object = Object.normalize(activity)
+
+ conn =
+ conn
+ |> assign(:user, other_user)
+ |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
+
+ assert json_response(conn, 200)
+ object = Object.get_by_id(object.id)
+
+ assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
+ total_items == 1
+ end)
+ end
+
+ test "author can't vote", %{conn: conn} do
+ user = insert(:user)
+
+ {:ok, activity} =
+ CommonAPI.post(user, %{
+ "status" => "Am I cute?",
+ "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
+ })
+
+ object = Object.normalize(activity)
+
+ assert conn
+ |> assign(:user, user)
+ |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
+ |> json_response(422) == %{"error" => "Poll's author can't vote"}
+
+ object = Object.get_by_id(object.id)
+
+ refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
+ end
+
+ test "does not allow multiple choices on a single-choice question", %{conn: conn} do
+ user = insert(:user)
+ other_user = insert(:user)
+
+ {:ok, activity} =
+ CommonAPI.post(user, %{
+ "status" => "The glass is",
+ "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
+ })
+
+ object = Object.normalize(activity)
+
+ assert conn
+ |> assign(:user, other_user)
+ |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
+ |> json_response(422) == %{"error" => "Too many choices"}
+
+ object = Object.get_by_id(object.id)
+
+ refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
+ total_items == 1
+ end)
+ end
+ end