Merge branch 'develop' into chore/remove-deprecated-functions

This commit is contained in:
FloatingGhost 2022-07-20 12:45:41 +01:00
commit e8ad9b6dfe
20 changed files with 13 additions and 532 deletions

View file

@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased]
### Removed
- Scrobbling support
## 2022.07 ## 2022.07
### Added ### Added

View file

@ -4,21 +4,21 @@
## About ## About
This is a fork of Pleroma, which is a microblogging server software that can federate (= exchange messages with) other servers that support ActivityPub. What that means is that you can host a server for yourself or your friends and stay in control of your online identity, but still exchange messages with people on larger servers. Pleroma will federate with all servers that implement ActivityPub, like Friendica, GNU Social, Hubzilla, Mastodon, Misskey, Peertube, and Pixelfed. This is a fork of Pleroma, which is a microblogging server software that can federate (= exchange messages with) other servers that support ActivityPub. What that means is that you can host a server for yourself or your friends and stay in control of your online identity, but still exchange messages with people on larger servers. Akkoma will federate with all servers that implement ActivityPub, like Friendica, GNU Social, Hubzilla, Mastodon, Misskey, Peertube, and Pixelfed.
Akkoma is written in Elixir and uses PostgresSQL for data storage. Akkoma is written in Elixir and uses PostgresSQL for data storage.
For clients it supports the [Mastodon client API](https://docs.joinmastodon.org/api/guidelines/) with Pleroma extensions (see the API section on <https://docs.akkoma.dev/stable/>). For clients it supports the [Mastodon client API](https://docs.joinmastodon.org/api/guidelines/) with Pleroma extensions (see the API section on <https://docs.akkoma.dev/stable/>).
- [Client Applications for Pleroma](https://docs.akkoma.dev/stable/clients/) - [Client Applications for Akkoma](https://docs.akkoma.dev/stable/clients/)
## Installation ## Installation
### OTP releases (Recommended) ### OTP releases (Recommended)
If you are running Linux (glibc or musl) on x86, the recommended way to install Pleroma is by using OTP releases. OTP releases are as close as you can get to binary releases with Erlang/Elixir. The release is self-contained, and provides everything needed to boot it. The installation instructions are available [here](https://docs.akkoma.dev/stable/installation/otp_en/). If you are running Linux (glibc or musl) on x86, the recommended way to install Akkoma is by using OTP releases. OTP releases are as close as you can get to binary releases with Erlang/Elixir. The release is self-contained, and provides everything needed to boot it. The installation instructions are available [here](https://docs.akkoma.dev/stable/installation/otp_en/).
### From Source ### From Source
If your platform is not supported, or you just want to be able to edit the source code easily, you may install Pleroma from source. If your platform is not supported, or you just want to be able to edit the source code easily, you may install Akkoma from source.
- [Alpine Linux](https://docs.akkoma.dev/stable/installation/alpine_linux_en/) - [Alpine Linux](https://docs.akkoma.dev/stable/installation/alpine_linux_en/)
- [Arch Linux](https://docs.akkoma.dev/stable/installation/arch_linux_en/) - [Arch Linux](https://docs.akkoma.dev/stable/installation/arch_linux_en/)
@ -34,7 +34,7 @@ If your platform is not supported, or you just want to be able to edit the sourc
While we dont provide docker files, other people have written very good ones. Take a look at <https://github.com/angristan/docker-pleroma> or <https://glitch.sh/sn0w/pleroma-docker>. While we dont provide docker files, other people have written very good ones. Take a look at <https://github.com/angristan/docker-pleroma> or <https://glitch.sh/sn0w/pleroma-docker>.
### Compilation Troubleshooting ### Compilation Troubleshooting
If you ever encounter compilation issues during the updating of Pleroma, you can try these commands and see if they fix things: If you ever encounter compilation issues during the updating of Akkoma, you can try these commands and see if they fix things:
- `mix deps.clean --all` - `mix deps.clean --all`
- `mix local.rebar` - `mix local.rebar`

View file

@ -576,38 +576,6 @@ The status posting endpoint takes an additional parameter, `in_reply_to_conversa
* Response: the archive of the pack with a 200 status code, 403 if the pack is not set as shared, * Response: the archive of the pack with a 200 status code, 403 if the pack is not set as shared,
404 if the pack does not exist 404 if the pack does not exist
## `GET /api/v1/pleroma/accounts/:id/scrobbles`
### Requests a list of current and recent Listen activities for an account
* Method `GET`
* Authentication: not required
* Params: None
* Response: An array of media metadata entities.
* Example response:
```json
[
{
"account": {...},
"id": "1234",
"title": "Some Title",
"artist": "Some Artist",
"album": "Some Album",
"length": 180000,
"created_at": "2019-09-28T12:40:45.000Z"
}
]
```
## `POST /api/v1/pleroma/scrobble`
### Creates a new Listen activity for an account
* Method `POST`
* Authentication: required
* Params:
* `title`: the title of the media playing
* `album`: the album of the media playing [optional]
* `artist`: the artist of the media playing [optional]
* `length`: the length of the media playing [optional]
* Response: the newly created media metadata entity representing the Listen activity
# Emoji Reactions # Emoji Reactions
Emoji reactions work a lot like favourites do. They make it possible to react to a post with a single emoji character. To detect the presence of this feature, you can check `pleroma_emoji_reactions` entry in the features list of nodeinfo. Emoji reactions work a lot like favourites do. They make it possible to react to a post with a single emoji character. To detect the presence of this feature, you can check `pleroma_emoji_reactions` entry in the features list of nodeinfo.

View file

@ -318,26 +318,6 @@ defp maybe_schedule_poll_notifications(activity) do
:ok :ok
end end
@spec listen(map()) :: {:ok, Activity.t()} | {:error, any()}
def listen(%{to: to, actor: actor, context: context, object: object} = params) do
additional = params[:additional] || %{}
# only accept false as false value
local = !(params[:local] == false)
published = params[:published]
listen_data =
make_listen_data(
%{to: to, actor: actor, published: published, context: context, object: object},
additional
)
with {:ok, activity} <- insert(listen_data, local),
_ <- notify_and_stream(activity),
:ok <- maybe_federate(activity) do
{:ok, activity}
end
end
@spec unfollow(User.t(), User.t(), String.t() | nil, boolean()) :: @spec unfollow(User.t(), User.t(), String.t() | nil, boolean()) ::
{:ok, Activity.t()} | nil | {:error, any()} {:ok, Activity.t()} | nil | {:error, any()}
def unfollow(follower, followed, activity_id \\ nil, local \\ true) do def unfollow(follower, followed, activity_id \\ nil, local \\ true) do

View file

@ -384,37 +384,6 @@ def handle_incoming(%{"id" => ""}, _options), do: :error
def handle_incoming(%{"id" => id}, _options) when is_binary(id) and byte_size(id) < 8, def handle_incoming(%{"id" => id}, _options) when is_binary(id) and byte_size(id) < 8,
do: :error do: :error
def handle_incoming(
%{"type" => "Listen", "object" => %{"type" => "Audio"} = object} = data,
options
) do
actor = Containment.get_actor(data)
data =
Map.put(data, "actor", actor)
|> fix_addressing
with {:ok, %User{} = user} <- User.get_or_fetch_by_ap_id(data["actor"]) do
reply_depth = (options[:depth] || 0) + 1
options = Keyword.put(options, :depth, reply_depth)
object = fix_object(object, options)
params = %{
to: data["to"],
object: object,
actor: user,
context: nil,
local: false,
published: data["published"],
additional: Map.take(data, ["cc", "id"])
}
ActivityPub.listen(params)
else
_e -> :error
end
end
@doc "Rewrite misskey likes into EmojiReacts" @doc "Rewrite misskey likes into EmojiReacts"
def handle_incoming( def handle_incoming(
%{ %{
@ -695,7 +664,7 @@ def prepare_object(object) do
# """ # """
def prepare_outgoing(%{"type" => activity_type, "object" => object_id} = data) def prepare_outgoing(%{"type" => activity_type, "object" => object_id} = data)
when activity_type in ["Create", "Listen"] do when activity_type in ["Create"] do
object = object =
object_id object_id
|> Object.normalize(fetch: false) |> Object.normalize(fetch: false)

View file

@ -713,21 +713,6 @@ def make_create_data(params, additional) do
|> Map.merge(additional) |> Map.merge(additional)
end end
#### Listen-related helpers
def make_listen_data(params, additional) do
published = params.published || make_date()
%{
"type" => "Listen",
"to" => params.to |> Enum.uniq(),
"actor" => params.actor.ap_id,
"object" => params.object,
"published" => published,
"context" => params.context
}
|> Map.merge(additional)
end
#### Flag-related helpers #### Flag-related helpers
@spec make_flag_data(map(), map()) :: map() @spec make_flag_data(map(), map()) :: map()
def make_flag_data(%{actor: actor, context: context, content: content} = params, additional) do def make_flag_data(%{actor: actor, context: context, content: content} = params, additional) do

View file

@ -16,7 +16,7 @@ def render("object.json", %{object: %Object{} = object}) do
end end
def render("object.json", %{object: %Activity{data: %{"type" => activity_type}} = activity}) def render("object.json", %{object: %Activity{data: %{"type" => activity_type}} = activity})
when activity_type in ["Create", "Listen"] do when activity_type in ["Create"] do
base = Pleroma.Web.ActivityPub.Utils.make_json_ld_header() base = Pleroma.Web.ActivityPub.Utils.make_json_ld_header()
object = Object.normalize(activity, fetch: false) object = Object.normalize(activity, fetch: false)

View file

@ -79,7 +79,7 @@ def spec(opts \\ []) do
"x-tagGroups": [ "x-tagGroups": [
%{ %{
"name" => "Accounts", "name" => "Accounts",
"tags" => ["Account actions", "Retrieve account information", "Scrobbles"] "tags" => ["Account actions", "Retrieve account information"]
}, },
%{ %{
"name" => "Administration", "name" => "Administration",

View file

@ -1,102 +0,0 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.PleromaScrobbleOperation do
alias OpenApiSpex.Operation
alias OpenApiSpex.Reference
alias OpenApiSpex.Schema
alias Pleroma.Web.ApiSpec.Schemas.Account
alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope
import Pleroma.Web.ApiSpec.Helpers
def open_api_operation(action) do
operation = String.to_existing_atom("#{action}_operation")
apply(__MODULE__, operation, [])
end
def create_operation do
%Operation{
tags: ["Scrobbles"],
summary: "Creates a new Listen activity for an account",
security: [%{"oAuth" => ["write"]}],
operationId: "PleromaAPI.ScrobbleController.create",
requestBody: request_body("Parameters", create_request(), requried: true),
responses: %{
200 => Operation.response("Scrobble", "application/json", scrobble())
}
}
end
def index_operation do
%Operation{
tags: ["Scrobbles"],
summary: "Requests a list of current and recent Listen activities for an account",
operationId: "PleromaAPI.ScrobbleController.index",
parameters: [
%Reference{"$ref": "#/components/parameters/accountIdOrNickname"} | pagination_params()
],
security: [%{"oAuth" => ["read"]}],
responses: %{
200 =>
Operation.response("Array of Scrobble", "application/json", %Schema{
type: :array,
items: scrobble()
})
}
}
end
defp create_request do
%Schema{
type: :object,
required: [:title],
properties: %{
title: %Schema{type: :string, description: "The title of the media playing"},
album: %Schema{type: :string, description: "The album of the media playing"},
artist: %Schema{type: :string, description: "The artist of the media playing"},
length: %Schema{type: :integer, description: "The length of the media playing"},
visibility: %Schema{
allOf: [VisibilityScope],
default: "public",
description: "Scrobble visibility"
}
},
example: %{
"title" => "Some Title",
"artist" => "Some Artist",
"album" => "Some Album",
"length" => 180_000
}
}
end
defp scrobble do
%Schema{
type: :object,
properties: %{
id: %Schema{type: :string},
account: Account,
title: %Schema{type: :string, description: "The title of the media playing"},
album: %Schema{type: :string, description: "The album of the media playing"},
artist: %Schema{type: :string, description: "The artist of the media playing"},
length: %Schema{
type: :integer,
description: "The length of the media playing",
nullable: true
},
created_at: %Schema{type: :string, format: :"date-time"}
},
example: %{
"id" => "1234",
"account" => Account.schema().example,
"title" => "Some Title",
"artist" => "Some Artist",
"album" => "Some Album",
"length" => 180_000,
"created_at" => "2019-09-28T12:40:45.000Z"
}
}
end
end

View file

@ -388,12 +388,6 @@ def check_expiry_date(expiry_str) do
|> check_expiry_date() |> check_expiry_date()
end end
def listen(user, data) do
with {:ok, draft} <- ActivityDraft.listen(user, data) do
ActivityPub.listen(draft.changes)
end
end
def post(user, %{status: _} = data) do def post(user, %{status: _} = data) do
with {:ok, draft} <- ActivityDraft.create(user, data) do with {:ok, draft} <- ActivityDraft.create(user, data) do
ActivityPub.create(draft.changes, draft.preview?) ActivityPub.create(draft.changes, draft.preview?)

View file

@ -64,30 +64,6 @@ def create(user, params) do
|> validate() |> validate()
end end
def listen(user, params) do
user
|> new(params)
|> visibility()
|> to_and_cc()
|> context()
|> listen_object()
|> with_valid(&changes/1)
|> validate()
end
defp listen_object(draft) do
object =
draft.params
|> Map.take([:album, :artist, :title, :length])
|> Map.new(fn {key, value} -> {to_string(key), value} end)
|> Map.put("type", "Audio")
|> Map.put("to", draft.to)
|> Map.put("cc", draft.cc)
|> Map.put("actor", draft.user.ap_id)
%__MODULE__{draft | object: object}
end
defp put_params(draft, params) do defp put_params(draft, params) do
params = Map.put_new(params, :in_reply_to_status_id, params[:in_reply_to_id]) params = Map.put_new(params, :in_reply_to_status_id, params[:in_reply_to_id])
%__MODULE__{draft | params: params} %__MODULE__{draft | params: params}

View file

@ -1,52 +0,0 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.PleromaAPI.ScrobbleController do
use Pleroma.Web, :controller
import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2]
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.Plugs.OAuthScopesPlug
plug(Pleroma.Web.ApiSpec.CastAndValidate)
plug(
OAuthScopesPlug,
%{scopes: ["read"], fallback: :proceed_unauthenticated} when action == :index
)
plug(OAuthScopesPlug, %{scopes: ["write"]} when action == :create)
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaScrobbleOperation
def create(%{assigns: %{user: user}, body_params: params} = conn, _) do
with {:ok, activity} <- CommonAPI.listen(user, params) do
render(conn, "show.json", activity: activity, for: user)
else
{:error, message} ->
conn
|> put_status(:bad_request)
|> json(%{"error" => message})
end
end
def index(%{assigns: %{user: reading_user}} = conn, %{id: id} = params) do
with %User{} = user <- User.get_cached_by_nickname_or_id(id, for: reading_user) do
params = Map.put(params, :type, ["Listen"])
activities = ActivityPub.fetch_user_abstract_activities(user, reading_user, params)
conn
|> add_link_headers(activities)
|> render("index.json", %{
activities: activities,
for: reading_user,
as: :activity
})
end
end
end

View file

@ -1,37 +0,0 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.PleromaAPI.ScrobbleView do
use Pleroma.Web, :view
require Pleroma.Constants
alias Pleroma.Activity
alias Pleroma.HTML
alias Pleroma.Object
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.CommonAPI.Utils
alias Pleroma.Web.MastodonAPI.AccountView
def render("show.json", %{activity: %Activity{data: %{"type" => "Listen"}} = activity} = opts) do
object = Object.normalize(activity, fetch: false)
user = CommonAPI.get_user(activity.data["actor"])
created_at = Utils.to_masto_date(activity.data["published"])
%{
id: activity.id,
account: AccountView.render("show.json", %{user: user, for: opts[:for]}),
created_at: created_at,
title: object.data["title"] |> HTML.strip_tags(),
artist: object.data["artist"] |> HTML.strip_tags(),
album: object.data["album"] |> HTML.strip_tags(),
length: object.data["length"]
}
end
def render("index.json", opts) do
safe_render_many(opts.activities, __MODULE__, "show.json", opts)
end
end

View file

@ -447,8 +447,6 @@ defmodule Pleroma.Web.Router do
get("/mascot", MascotController, :show) get("/mascot", MascotController, :show)
put("/mascot", MascotController, :update) put("/mascot", MascotController, :update)
post("/scrobble", ScrobbleController, :create)
get("/backups", BackupController, :index) get("/backups", BackupController, :index)
post("/backups", BackupController, :create) post("/backups", BackupController, :create)
end end
@ -470,7 +468,6 @@ defmodule Pleroma.Web.Router do
scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do
pipe_through(:api) pipe_through(:api)
get("/accounts/:id/scrobbles", ScrobbleController, :index)
get("/federation_status", InstancesController, :show) get("/federation_status", InstancesController, :show)
end end

View file

@ -543,42 +543,6 @@ test "adds an id to a given object if it lacks one and is a note and inserts it
end end
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.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 describe "create activities" do
setup do setup do
[user: insert(:user)] [user: insert(:user)]

View file

@ -12,39 +12,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.AudioHandlingTest do
import Pleroma.Factory import Pleroma.Factory
test "it works for incoming listens" do
_user = insert(:user, ap_id: "http://mastodon.example.org/users/admin")
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",
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
"cc" => [],
"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, fetch: false)
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 "Funkwhale Audio object" do test "Funkwhale Audio object" do
Tesla.Mock.mock(fn Tesla.Mock.mock(fn
%{url: "https://channels.tests.funkwhale.audio/federation/actors/compositions"} -> %{url: "https://channels.tests.funkwhale.audio/federation/actors/compositions"} ->

View file

@ -273,20 +273,6 @@ test "it strips BCC field" do
assert is_nil(modified["bcc"]) assert is_nil(modified["bcc"])
end 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
test "custom emoji urls are URI encoded" do test "custom emoji urls are URI encoded" do
# :dinosaur: filename has a space -> dino walking.gif # :dinosaur: filename has a space -> dino walking.gif
user = insert(:user) user = insert(:user)

View file

@ -1331,45 +1331,6 @@ test "does not allow to vote twice" do
end end
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, fetch: false)
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, fetch: false)
assert object.data["title"] == "lain radio episode 1"
assert Visibility.get_visibility(activity) == "private"
end
end
describe "get_user/1" do describe "get_user/1" do
test "gets user by ap_id" do test "gets user by ap_id" do
user = insert(:user) user = insert(:user)

View file

@ -1,60 +0,0 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.PleromaAPI.ScrobbleControllerTest do
use Pleroma.Web.ConnCase, async: true
alias Pleroma.Web.CommonAPI
describe "POST /api/v1/pleroma/scrobble" do
test "works correctly" do
%{conn: conn} = oauth_access(["write"])
conn =
conn
|> put_req_header("content-type", "application/json")
|> 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_and_validate_schema(conn, 200)
end
end
describe "GET /api/v1/pleroma/accounts/:id/scrobbles" do
test "works correctly" do
%{user: user, conn: conn} = oauth_access(["read"])
{: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 = get(conn, "/api/v1/pleroma/accounts/#{user.id}/scrobbles")
result = json_response_and_validate_schema(conn, 200)
assert length(result) == 3
end
end
end

View file

@ -1,20 +0,0 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.PleromaAPI.ScrobbleViewTest do
use Pleroma.DataCase, async: true
alias Pleroma.Web.PleromaAPI.ScrobbleView
import Pleroma.Factory
test "successfully renders a Listen activity (pleroma extension)" do
listen_activity = insert(:listen)
status = ScrobbleView.render("show.json", activity: listen_activity)
assert status.length == listen_activity.data["object"]["length"]
assert status.title == listen_activity.data["object"]["title"]
end
end