Scrape instance nodeinfo (#251)
Some checks are pending
ci/woodpecker/push/woodpecker Pipeline is pending
Some checks are pending
ci/woodpecker/push/woodpecker Pipeline is pending
Co-authored-by: FloatingGhost <hannah@coffee-and-dreams.uk> Reviewed-on: #251
This commit is contained in:
parent
ccdf55acff
commit
b7e8ce2350
14 changed files with 575 additions and 115 deletions
|
@ -567,7 +567,8 @@
|
||||||
attachments_cleanup: 1,
|
attachments_cleanup: 1,
|
||||||
new_users_digest: 1,
|
new_users_digest: 1,
|
||||||
mute_expire: 5,
|
mute_expire: 5,
|
||||||
search_indexing: 10
|
search_indexing: 10,
|
||||||
|
nodeinfo_fetcher: 1
|
||||||
],
|
],
|
||||||
plugins: [
|
plugins: [
|
||||||
Oban.Plugins.Pruner,
|
Oban.Plugins.Pruner,
|
||||||
|
@ -806,7 +807,8 @@
|
||||||
|
|
||||||
config :web_push_encryption, http_client: Pleroma.HTTP.WebPush
|
config :web_push_encryption, http_client: Pleroma.HTTP.WebPush
|
||||||
|
|
||||||
config :pleroma, :instances_favicons, enabled: false
|
config :pleroma, :instances_favicons, enabled: true
|
||||||
|
config :pleroma, :instances_nodeinfo, enabled: true
|
||||||
|
|
||||||
config :floki, :html_parser, Floki.HTMLParser.FastHtml
|
config :floki, :html_parser, Floki.HTMLParser.FastHtml
|
||||||
|
|
||||||
|
|
|
@ -3047,6 +3047,19 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
%{
|
||||||
|
group: :pleroma,
|
||||||
|
key: :instances_nodeinfo,
|
||||||
|
type: :group,
|
||||||
|
description: "Control favicons for instances",
|
||||||
|
children: [
|
||||||
|
%{
|
||||||
|
key: :enabled,
|
||||||
|
type: :boolean,
|
||||||
|
description: "Allow/disallow getting instance nodeinfo"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
%{
|
%{
|
||||||
group: :ex_aws,
|
group: :ex_aws,
|
||||||
key: :s3,
|
key: :s3,
|
||||||
|
|
|
@ -139,6 +139,8 @@
|
||||||
# Reduce recompilation time
|
# Reduce recompilation time
|
||||||
# https://dashbit.co/blog/speeding-up-re-compilation-of-elixir-projects
|
# https://dashbit.co/blog/speeding-up-re-compilation-of-elixir-projects
|
||||||
config :phoenix, :plug_init_mode, :runtime
|
config :phoenix, :plug_init_mode, :runtime
|
||||||
|
config :pleroma, :instances_favicons, enabled: false
|
||||||
|
config :pleroma, :instances_nodeinfo, enabled: false
|
||||||
|
|
||||||
if File.exists?("./config/test.secret.exs") do
|
if File.exists?("./config/test.secret.exs") do
|
||||||
import_config "test.secret.exs"
|
import_config "test.secret.exs"
|
||||||
|
|
|
@ -156,7 +156,8 @@ defp cachex_children do
|
||||||
build_cachex("emoji_packs", expiration: emoji_packs_expiration(), limit: 10),
|
build_cachex("emoji_packs", expiration: emoji_packs_expiration(), limit: 10),
|
||||||
build_cachex("failed_proxy_url", limit: 2500),
|
build_cachex("failed_proxy_url", limit: 2500),
|
||||||
build_cachex("banned_urls", default_ttl: :timer.hours(24 * 30), limit: 5_000),
|
build_cachex("banned_urls", default_ttl: :timer.hours(24 * 30), limit: 5_000),
|
||||||
build_cachex("translations", default_ttl: :timer.hours(24 * 30), limit: 2500)
|
build_cachex("translations", default_ttl: :timer.hours(24 * 30), limit: 2500),
|
||||||
|
build_cachex("instances", default_ttl: :timer.hours(24), limit: 2500)
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
defmodule Pleroma.Instances.Instance do
|
defmodule Pleroma.Instances.Instance do
|
||||||
@moduledoc "Instance."
|
@moduledoc "Instance."
|
||||||
|
|
||||||
|
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
|
||||||
|
|
||||||
alias Pleroma.Instances
|
alias Pleroma.Instances
|
||||||
alias Pleroma.Instances.Instance
|
alias Pleroma.Instances.Instance
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
|
@ -22,7 +24,8 @@ defmodule Pleroma.Instances.Instance do
|
||||||
field(:host, :string)
|
field(:host, :string)
|
||||||
field(:unreachable_since, :naive_datetime_usec)
|
field(:unreachable_since, :naive_datetime_usec)
|
||||||
field(:favicon, :string)
|
field(:favicon, :string)
|
||||||
field(:favicon_updated_at, :naive_datetime)
|
field(:metadata_updated_at, :naive_datetime)
|
||||||
|
field(:nodeinfo, :map, default: %{})
|
||||||
|
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
|
@ -31,7 +34,7 @@ defmodule Pleroma.Instances.Instance do
|
||||||
|
|
||||||
def changeset(struct, params \\ %{}) do
|
def changeset(struct, params \\ %{}) do
|
||||||
struct
|
struct
|
||||||
|> cast(params, [:host, :unreachable_since, :favicon, :favicon_updated_at])
|
|> cast(params, [:host, :unreachable_since, :favicon, :nodeinfo, :metadata_updated_at])
|
||||||
|> validate_required([:host])
|
|> validate_required([:host])
|
||||||
|> unique_constraint(:host)
|
|> unique_constraint(:host)
|
||||||
end
|
end
|
||||||
|
@ -138,63 +141,144 @@ defp parse_datetime(datetime) when is_binary(datetime) do
|
||||||
|
|
||||||
defp parse_datetime(datetime), do: datetime
|
defp parse_datetime(datetime), do: datetime
|
||||||
|
|
||||||
def get_or_update_favicon(%URI{host: host} = instance_uri) do
|
def needs_update(nil), do: true
|
||||||
existing_record = Repo.get_by(Instance, %{host: host})
|
|
||||||
now = NaiveDateTime.utc_now()
|
|
||||||
|
|
||||||
if existing_record && existing_record.favicon_updated_at &&
|
def needs_update(%Instance{metadata_updated_at: nil}), do: true
|
||||||
NaiveDateTime.diff(now, existing_record.favicon_updated_at) < 86_400 do
|
|
||||||
|
def needs_update(%Instance{metadata_updated_at: metadata_updated_at}) do
|
||||||
|
now = NaiveDateTime.utc_now()
|
||||||
|
NaiveDateTime.diff(now, metadata_updated_at) > 86_400
|
||||||
|
end
|
||||||
|
|
||||||
|
def local do
|
||||||
|
%Instance{
|
||||||
|
host: Pleroma.Web.Endpoint.host(),
|
||||||
|
favicon: Pleroma.Web.Endpoint.url() <> "/favicon.png",
|
||||||
|
nodeinfo: Pleroma.Web.Nodeinfo.NodeinfoController.raw_nodeinfo()
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_metadata(%URI{host: host} = uri) do
|
||||||
|
Logger.info("Checking metadata for #{host}")
|
||||||
|
existing_record = Repo.get_by(Instance, %{host: host})
|
||||||
|
|
||||||
|
if reachable?(host) do
|
||||||
|
do_update_metadata(uri, existing_record)
|
||||||
|
else
|
||||||
|
{:discard, :unreachable}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp do_update_metadata(%URI{host: host} = uri, existing_record) do
|
||||||
|
if existing_record do
|
||||||
|
if needs_update(existing_record) do
|
||||||
|
Logger.info("Updating metadata for #{host}")
|
||||||
|
favicon = scrape_favicon(uri)
|
||||||
|
nodeinfo = scrape_nodeinfo(uri)
|
||||||
|
|
||||||
|
{:ok, instance} =
|
||||||
|
existing_record
|
||||||
|
|> changeset(%{
|
||||||
|
host: host,
|
||||||
|
favicon: favicon,
|
||||||
|
nodeinfo: nodeinfo,
|
||||||
|
metadata_updated_at: NaiveDateTime.utc_now()
|
||||||
|
})
|
||||||
|
|> Repo.update()
|
||||||
|
|
||||||
|
@cachex.put(:instances_cache, "instances:#{host}", instance)
|
||||||
|
else
|
||||||
|
{:discard, "Does not require update"}
|
||||||
|
end
|
||||||
|
else
|
||||||
|
favicon = scrape_favicon(uri)
|
||||||
|
nodeinfo = scrape_nodeinfo(uri)
|
||||||
|
|
||||||
|
Logger.info("Creating metadata for #{host}")
|
||||||
|
|
||||||
|
{:ok, instance} =
|
||||||
|
%Instance{}
|
||||||
|
|> changeset(%{
|
||||||
|
host: host,
|
||||||
|
favicon: favicon,
|
||||||
|
nodeinfo: nodeinfo,
|
||||||
|
metadata_updated_at: NaiveDateTime.utc_now()
|
||||||
|
})
|
||||||
|
|> Repo.insert()
|
||||||
|
|
||||||
|
@cachex.put(:instances_cache, "instances:#{host}", instance)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_favicon(%URI{host: host}) do
|
||||||
|
existing_record = Repo.get_by(Instance, %{host: host})
|
||||||
|
|
||||||
|
if existing_record do
|
||||||
existing_record.favicon
|
existing_record.favicon
|
||||||
else
|
else
|
||||||
favicon = scrape_favicon(instance_uri)
|
|
||||||
|
|
||||||
if existing_record do
|
|
||||||
existing_record
|
|
||||||
|> changeset(%{favicon: favicon, favicon_updated_at: now})
|
|
||||||
|> Repo.update()
|
|
||||||
else
|
|
||||||
%Instance{}
|
|
||||||
|> changeset(%{host: host, favicon: favicon, favicon_updated_at: now})
|
|
||||||
|> Repo.insert()
|
|
||||||
end
|
|
||||||
|
|
||||||
favicon
|
|
||||||
end
|
|
||||||
rescue
|
|
||||||
e ->
|
|
||||||
Logger.warn("Instance.get_or_update_favicon(\"#{host}\") error: #{inspect(e)}")
|
|
||||||
nil
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp scrape_nodeinfo(%URI{} = instance_uri) do
|
||||||
|
with true <- Pleroma.Config.get([:instances_nodeinfo, :enabled]),
|
||||||
|
{_, true} <- {:reachable, reachable?(instance_uri.host)},
|
||||||
|
{:ok, %Tesla.Env{status: 200, body: body}} <-
|
||||||
|
Tesla.get(
|
||||||
|
"https://#{instance_uri.host}/.well-known/nodeinfo",
|
||||||
|
headers: [{"Accept", "application/json"}]
|
||||||
|
),
|
||||||
|
{:ok, json} <- Jason.decode(body),
|
||||||
|
{:ok, %{"links" => links}} <- {:ok, json},
|
||||||
|
{:ok, %{"href" => href}} <-
|
||||||
|
{:ok,
|
||||||
|
Enum.find(links, &(&1["rel"] == "http://nodeinfo.diaspora.software/ns/schema/2.0"))},
|
||||||
|
{:ok, %Tesla.Env{body: data}} <-
|
||||||
|
Pleroma.HTTP.get(href, [{"accept", "application/json"}], []),
|
||||||
|
{:length, true} <- {:length, String.length(data) < 50_000},
|
||||||
|
{:ok, nodeinfo} <- Jason.decode(data) do
|
||||||
|
nodeinfo
|
||||||
|
else
|
||||||
|
{:reachable, false} ->
|
||||||
|
Logger.debug(
|
||||||
|
"Instance.scrape_nodeinfo(\"#{to_string(instance_uri)}\") ignored unreachable host"
|
||||||
|
)
|
||||||
|
|
||||||
|
nil
|
||||||
|
|
||||||
|
{:length, false} ->
|
||||||
|
Logger.debug(
|
||||||
|
"Instance.scrape_nodeinfo(\"#{to_string(instance_uri)}\") ignored too long body"
|
||||||
|
)
|
||||||
|
|
||||||
|
nil
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp scrape_favicon(%URI{} = instance_uri) do
|
defp scrape_favicon(%URI{} = instance_uri) do
|
||||||
try do
|
with true <- Pleroma.Config.get([:instances_favicons, :enabled]),
|
||||||
with {_, true} <- {:reachable, reachable?(instance_uri.host)},
|
{_, true} <- {:reachable, reachable?(instance_uri.host)},
|
||||||
{:ok, %Tesla.Env{body: html}} <-
|
{:ok, %Tesla.Env{body: html}} <-
|
||||||
Pleroma.HTTP.get(to_string(instance_uri), [{"accept", "text/html"}], []),
|
Pleroma.HTTP.get(to_string(instance_uri), [{"accept", "text/html"}], []),
|
||||||
{_, [favicon_rel | _]} when is_binary(favicon_rel) <-
|
{_, [favicon_rel | _]} when is_binary(favicon_rel) <-
|
||||||
{:parse,
|
{:parse, html |> Floki.parse_document!() |> Floki.attribute("link[rel=icon]", "href")},
|
||||||
html |> Floki.parse_document!() |> Floki.attribute("link[rel=icon]", "href")},
|
{_, favicon} when is_binary(favicon) <-
|
||||||
{_, favicon} when is_binary(favicon) <-
|
{:merge, URI.merge(instance_uri, favicon_rel) |> to_string()},
|
||||||
{:merge, URI.merge(instance_uri, favicon_rel) |> to_string()} do
|
{:length, true} <- {:length, String.length(favicon) < 255} do
|
||||||
favicon
|
favicon
|
||||||
else
|
else
|
||||||
{:reachable, false} ->
|
{:reachable, false} ->
|
||||||
Logger.debug(
|
Logger.debug(
|
||||||
"Instance.scrape_favicon(\"#{to_string(instance_uri)}\") ignored unreachable host"
|
"Instance.scrape_favicon(\"#{to_string(instance_uri)}\") ignored unreachable host"
|
||||||
)
|
|
||||||
|
|
||||||
nil
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
rescue
|
|
||||||
e ->
|
|
||||||
Logger.warn(
|
|
||||||
"Instance.scrape_favicon(\"#{to_string(instance_uri)}\") error: #{inspect(e)}"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
nil
|
nil
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -217,4 +301,25 @@ def perform(:delete_instance, host) when is_binary(host) do
|
||||||
end)
|
end)
|
||||||
|> Stream.run()
|
|> Stream.run()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get_by_url(url_or_host) do
|
||||||
|
url = host(url_or_host)
|
||||||
|
Repo.get_by(Instance, host: url)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_cached_by_url(url_or_host) do
|
||||||
|
url = host(url_or_host)
|
||||||
|
|
||||||
|
if url == Pleroma.Web.Endpoint.host() do
|
||||||
|
{:ok, local()}
|
||||||
|
else
|
||||||
|
@cachex.fetch!(:instances_cache, "instances:#{url}", fn _ ->
|
||||||
|
with %Instance{} = instance <- get_by_url(url) do
|
||||||
|
{:commit, {:ok, instance}}
|
||||||
|
else
|
||||||
|
_ -> {:ignore, nil}
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -192,6 +192,7 @@ def handle(%{data: %{"type" => "Like"}} = object, meta) do
|
||||||
# - Increase the user note count
|
# - Increase the user note count
|
||||||
# - Increase the reply count
|
# - Increase the reply count
|
||||||
# - Increase replies count
|
# - Increase replies count
|
||||||
|
# - Ask for scraping of nodeinfo
|
||||||
# - Set up ActivityExpiration
|
# - Set up ActivityExpiration
|
||||||
# - Set up notifications
|
# - Set up notifications
|
||||||
# - Index incoming posts for search (if needed)
|
# - Index incoming posts for search (if needed)
|
||||||
|
@ -209,6 +210,10 @@ def handle(%{data: %{"type" => "Create"}} = activity, meta) do
|
||||||
|
|
||||||
reply_depth = (meta[:depth] || 0) + 1
|
reply_depth = (meta[:depth] || 0) + 1
|
||||||
|
|
||||||
|
Pleroma.Workers.NodeInfoFetcherWorker.enqueue("process", %{
|
||||||
|
"source_url" => activity.data["actor"]
|
||||||
|
})
|
||||||
|
|
||||||
# FIXME: Force inReplyTo to replies
|
# FIXME: Force inReplyTo to replies
|
||||||
if Pleroma.Web.Federator.allowed_thread_distance?(reply_depth) and
|
if Pleroma.Web.Federator.allowed_thread_distance?(reply_depth) and
|
||||||
object.data["replies"] != nil do
|
object.data["replies"] != nil do
|
||||||
|
@ -234,7 +239,9 @@ def handle(%{data: %{"type" => "Create"}} = activity, meta) do
|
||||||
|
|
||||||
{:ok, activity, meta}
|
{:ok, activity, meta}
|
||||||
else
|
else
|
||||||
e -> Repo.rollback(e)
|
e ->
|
||||||
|
Logger.error(inspect(e))
|
||||||
|
Repo.rollback(e)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -186,6 +186,16 @@ def render("relationships.json", %{user: user, targets: targets} = opts) do
|
||||||
render_many(targets, AccountView, "relationship.json", render_opts)
|
render_many(targets, AccountView, "relationship.json", render_opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def render("instance.json", %{instance: %Pleroma.Instances.Instance{} = instance}) do
|
||||||
|
%{
|
||||||
|
name: instance.host,
|
||||||
|
favicon: instance.favicon |> MediaProxy.url(),
|
||||||
|
nodeinfo: instance.nodeinfo
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def render("instance.json", _), do: nil
|
||||||
|
|
||||||
defp do_render("show.json", %{user: user} = opts) do
|
defp do_render("show.json", %{user: user} = opts) do
|
||||||
user = User.sanitize_html(user, User.html_filter_policy(opts[:for]))
|
user = User.sanitize_html(user, User.html_filter_policy(opts[:for]))
|
||||||
display_name = user.name || user.nickname
|
display_name = user.name || user.nickname
|
||||||
|
@ -230,16 +240,20 @@ defp do_render("show.json", %{user: user} = opts) do
|
||||||
%{}
|
%{}
|
||||||
end
|
end
|
||||||
|
|
||||||
favicon =
|
instance =
|
||||||
if Pleroma.Config.get([:instances_favicons, :enabled]) do
|
with {:ok, instance} <- Pleroma.Instances.Instance.get_cached_by_url(user.ap_id) do
|
||||||
user
|
instance
|
||||||
|> Map.get(:ap_id, "")
|
|
||||||
|> URI.parse()
|
|
||||||
|> URI.merge("/")
|
|
||||||
|> Pleroma.Instances.Instance.get_or_update_favicon()
|
|
||||||
|> MediaProxy.url()
|
|
||||||
else
|
else
|
||||||
|
_ ->
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
favicon =
|
||||||
|
if is_nil(instance) do
|
||||||
nil
|
nil
|
||||||
|
else
|
||||||
|
instance.favicon
|
||||||
|
|> MediaProxy.url()
|
||||||
end
|
end
|
||||||
|
|
||||||
%{
|
%{
|
||||||
|
@ -271,7 +285,9 @@ defp do_render("show.json", %{user: user} = opts) do
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
last_status_at: user.last_status_at,
|
last_status_at: user.last_status_at,
|
||||||
|
akkoma: %{
|
||||||
|
instance: render("instance.json", %{instance: instance})
|
||||||
|
},
|
||||||
# Pleroma extensions
|
# Pleroma extensions
|
||||||
# Note: it's insecure to output :email but fully-qualified nickname may serve as safe stub
|
# Note: it's insecure to output :email but fully-qualified nickname may serve as safe stub
|
||||||
fqn: User.full_nickname(user),
|
fqn: User.full_nickname(user),
|
||||||
|
|
18
lib/pleroma/workers/nodeinfo_fetcher_worker.ex
Normal file
18
lib/pleroma/workers/nodeinfo_fetcher_worker.ex
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
defmodule Pleroma.Workers.NodeInfoFetcherWorker do
|
||||||
|
use Pleroma.Workers.WorkerHelper, queue: "nodeinfo_fetcher"
|
||||||
|
|
||||||
|
alias Oban.Job
|
||||||
|
alias Pleroma.Instances.Instance
|
||||||
|
|
||||||
|
@impl Oban.Worker
|
||||||
|
def perform(%Job{
|
||||||
|
args: %{"op" => "process", "source_url" => domain}
|
||||||
|
}) do
|
||||||
|
uri =
|
||||||
|
domain
|
||||||
|
|> URI.parse()
|
||||||
|
|> URI.merge("/")
|
||||||
|
|
||||||
|
Instance.update_metadata(uri)
|
||||||
|
end
|
||||||
|
end
|
17
priv/repo/migrations/20221020135943_add_nodeinfo.exs
Normal file
17
priv/repo/migrations/20221020135943_add_nodeinfo.exs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.AddNodeinfo do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def up do
|
||||||
|
alter table(:instances) do
|
||||||
|
add_if_not_exists(:nodeinfo, :map, default: %{})
|
||||||
|
add_if_not_exists(:metadata_updated_at, :naive_datetime)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def down do
|
||||||
|
alter table(:instances) do
|
||||||
|
remove_if_exists(:nodeinfo, :map)
|
||||||
|
remove_if_exists(:metadata_updated_at, :naive_datetime)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -9,12 +9,16 @@ defmodule Pleroma.Instances.InstanceTest do
|
||||||
alias Pleroma.Tests.ObanHelpers
|
alias Pleroma.Tests.ObanHelpers
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
|
|
||||||
use Pleroma.DataCase
|
use Pleroma.DataCase, async: true
|
||||||
|
|
||||||
import ExUnit.CaptureLog
|
import ExUnit.CaptureLog
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
setup_all do: clear_config([:instance, :federation_reachability_timeout_days], 1)
|
setup_all do
|
||||||
|
clear_config([:instance, :federation_reachability_timeout_days], 1)
|
||||||
|
clear_config([:instances_nodeinfo, :enabled], true)
|
||||||
|
clear_config([:instances_favicons, :enabled], true)
|
||||||
|
end
|
||||||
|
|
||||||
describe "set_reachable/1" do
|
describe "set_reachable/1" do
|
||||||
test "clears `unreachable_since` of existing matching Instance record having non-nil `unreachable_since`" do
|
test "clears `unreachable_since` of existing matching Instance record having non-nil `unreachable_since`" do
|
||||||
|
@ -102,62 +106,220 @@ test "does NOT modify `unreachable_since` value of existing record in case it's
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "get_or_update_favicon/1" do
|
describe "update_metadata/1" do
|
||||||
test "Scrapes favicon URLs" do
|
test "Scrapes favicon URLs and nodeinfo" do
|
||||||
Tesla.Mock.mock(fn %{url: "https://favicon.example.org/"} ->
|
Tesla.Mock.mock(fn
|
||||||
%Tesla.Env{
|
%{url: "https://favicon.example.org/"} ->
|
||||||
status: 200,
|
%Tesla.Env{
|
||||||
body: ~s[<html><head><link rel="icon" href="/favicon.png"></head></html>]
|
status: 200,
|
||||||
}
|
body: ~s[<html><head><link rel="icon" href="/favicon.png"></head></html>]
|
||||||
|
}
|
||||||
|
|
||||||
|
%{url: "https://favicon.example.org/.well-known/nodeinfo"} ->
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body:
|
||||||
|
Jason.encode!(%{
|
||||||
|
links: [
|
||||||
|
%{
|
||||||
|
rel: "http://nodeinfo.diaspora.software/ns/schema/2.0",
|
||||||
|
href: "https://favicon.example.org/nodeinfo/2.0"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
%{url: "https://favicon.example.org/nodeinfo/2.0"} ->
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body: Jason.encode!(%{version: "2.0", software: %{name: "Akkoma"}})
|
||||||
|
}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
assert "https://favicon.example.org/favicon.png" ==
|
assert {:ok, true} ==
|
||||||
Instance.get_or_update_favicon(URI.parse("https://favicon.example.org/"))
|
Instance.update_metadata(URI.parse("https://favicon.example.org/"))
|
||||||
|
|
||||||
|
{:ok, instance} = Instance.get_cached_by_url("https://favicon.example.org/")
|
||||||
|
assert instance.favicon == "https://favicon.example.org/favicon.png"
|
||||||
|
assert instance.nodeinfo == %{"version" => "2.0", "software" => %{"name" => "Akkoma"}}
|
||||||
end
|
end
|
||||||
|
|
||||||
test "Returns nil on too long favicon URLs" do
|
test "Does not retain favicons that are too long" do
|
||||||
long_favicon_url =
|
long_favicon_url =
|
||||||
"https://Lorem.ipsum.dolor.sit.amet/consecteturadipiscingelit/Praesentpharetrapurusutaliquamtempus/Mauriseulaoreetarcu/atfacilisisorci/Nullamporttitor/nequesedfeugiatmollis/dolormagnaefficiturlorem/nonpretiumsapienorcieurisus/Nullamveleratsem/Maecenassedaccumsanexnam/favicon.png"
|
"https://Lorem.ipsum.dolor.sit.amet/consecteturadipiscingelit/Praesentpharetrapurusutaliquamtempus/Mauriseulaoreetarcu/atfacilisisorci/Nullamporttitor/nequesedfeugiatmollis/dolormagnaefficiturlorem/nonpretiumsapienorcieurisus/Nullamveleratsem/Maecenassedaccumsanexnam/favicon.png"
|
||||||
|
|
||||||
Tesla.Mock.mock(fn %{url: "https://long-favicon.example.org/"} ->
|
Tesla.Mock.mock(fn
|
||||||
%Tesla.Env{
|
%{url: "https://long-favicon.example.org/"} ->
|
||||||
status: 200,
|
%Tesla.Env{
|
||||||
body:
|
status: 200,
|
||||||
~s[<html><head><link rel="icon" href="] <> long_favicon_url <> ~s["></head></html>]
|
body:
|
||||||
}
|
~s[<html><head><link rel="icon" href="] <> long_favicon_url <> ~s["></head></html>]
|
||||||
|
}
|
||||||
|
|
||||||
|
%{url: "https://long-favicon.example.org/.well-known/nodeinfo"} ->
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body:
|
||||||
|
Jason.encode!(%{
|
||||||
|
links: [
|
||||||
|
%{
|
||||||
|
rel: "http://nodeinfo.diaspora.software/ns/schema/2.0",
|
||||||
|
href: "https://long-favicon.example.org/nodeinfo/2.0"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
%{url: "https://long-favicon.example.org/nodeinfo/2.0"} ->
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body: Jason.encode!(%{version: "2.0", software: %{name: "Akkoma"}})
|
||||||
|
}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
assert capture_log(fn ->
|
assert {:ok, true} ==
|
||||||
assert nil ==
|
Instance.update_metadata(URI.parse("https://long-favicon.example.org/"))
|
||||||
Instance.get_or_update_favicon(
|
|
||||||
URI.parse("https://long-favicon.example.org/")
|
{:ok, instance} = Instance.get_cached_by_url("https://long-favicon.example.org/")
|
||||||
)
|
assert instance.favicon == nil
|
||||||
end) =~
|
|
||||||
"Instance.get_or_update_favicon(\"long-favicon.example.org\") error: %Postgrex.Error{"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "Handles not getting a favicon URL properly" do
|
test "Handles not getting a favicon URL properly" do
|
||||||
Tesla.Mock.mock(fn %{url: "https://no-favicon.example.org/"} ->
|
Tesla.Mock.mock(fn
|
||||||
%Tesla.Env{
|
%{url: "https://no-favicon.example.org/"} ->
|
||||||
status: 200,
|
%Tesla.Env{
|
||||||
body: ~s[<html><head><h1>I wil look down and whisper "GNO.."</h1></head></html>]
|
status: 200,
|
||||||
}
|
body: ~s[<html><head><h1>I wil look down and whisper "GNO.."</h1></head></html>]
|
||||||
|
}
|
||||||
|
|
||||||
|
%{url: "https://no-favicon.example.org/.well-known/nodeinfo"} ->
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body:
|
||||||
|
Jason.encode!(%{
|
||||||
|
links: [
|
||||||
|
%{
|
||||||
|
rel: "http://nodeinfo.diaspora.software/ns/schema/2.0",
|
||||||
|
href: "https://no-favicon.example.org/nodeinfo/2.0"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
%{url: "https://no-favicon.example.org/nodeinfo/2.0"} ->
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body: Jason.encode!(%{version: "2.0", software: %{name: "Akkoma"}})
|
||||||
|
}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
refute capture_log(fn ->
|
refute capture_log(fn ->
|
||||||
assert nil ==
|
assert {:ok, true} =
|
||||||
Instance.get_or_update_favicon(
|
Instance.update_metadata(URI.parse("https://no-favicon.example.org/"))
|
||||||
URI.parse("https://no-favicon.example.org/")
|
end) =~ "Instance.update_metadata(\"https://no-favicon.example.org/\") error: "
|
||||||
)
|
|
||||||
end) =~ "Instance.scrape_favicon(\"https://no-favicon.example.org/\") error: "
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "Doesn't scrapes unreachable instances" do
|
test "Doesn't scrape unreachable instances" do
|
||||||
instance = insert(:instance, unreachable_since: Instances.reachability_datetime_threshold())
|
instance = insert(:instance, unreachable_since: Instances.reachability_datetime_threshold())
|
||||||
url = "https://" <> instance.host
|
url = "https://" <> instance.host
|
||||||
|
|
||||||
assert capture_log(fn -> assert nil == Instance.get_or_update_favicon(URI.parse(url)) end) =~
|
assert {:discard, :unreachable} == Instance.update_metadata(URI.parse(url))
|
||||||
"Instance.scrape_favicon(\"#{url}\") ignored unreachable host"
|
end
|
||||||
|
|
||||||
|
test "doesn't continue scraping nodeinfo if we can't find a link" do
|
||||||
|
Tesla.Mock.mock(fn
|
||||||
|
%{url: "https://bad-nodeinfo.example.org/"} ->
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body: ~s[<html><head><h1>I wil look down and whisper "GNO.."</h1></head></html>]
|
||||||
|
}
|
||||||
|
|
||||||
|
%{url: "https://bad-nodeinfo.example.org/.well-known/nodeinfo"} ->
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body: "oepsie woepsie de nodeinfo is kapotie uwu"
|
||||||
|
}
|
||||||
|
end)
|
||||||
|
|
||||||
|
assert {:ok, true} ==
|
||||||
|
Instance.update_metadata(URI.parse("https://bad-nodeinfo.example.org/"))
|
||||||
|
|
||||||
|
{:ok, instance} = Instance.get_cached_by_url("https://bad-nodeinfo.example.org/")
|
||||||
|
assert instance.nodeinfo == nil
|
||||||
|
end
|
||||||
|
|
||||||
|
test "doesn't store bad json in the nodeinfo" do
|
||||||
|
Tesla.Mock.mock(fn
|
||||||
|
%{url: "https://bad-nodeinfo.example.org/"} ->
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body: ~s[<html><head><h1>I wil look down and whisper "GNO.."</h1></head></html>]
|
||||||
|
}
|
||||||
|
|
||||||
|
%{url: "https://bad-nodeinfo.example.org/.well-known/nodeinfo"} ->
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body:
|
||||||
|
Jason.encode!(%{
|
||||||
|
links: [
|
||||||
|
%{
|
||||||
|
rel: "http://nodeinfo.diaspora.software/ns/schema/2.0",
|
||||||
|
href: "https://bad-nodeinfo.example.org/nodeinfo/2.0"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
%{url: "https://bad-nodeinfo.example.org/nodeinfo/2.0"} ->
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body: "oepsie woepsie de json might be bad uwu"
|
||||||
|
}
|
||||||
|
end)
|
||||||
|
|
||||||
|
assert {:ok, true} ==
|
||||||
|
Instance.update_metadata(URI.parse("https://bad-nodeinfo.example.org/"))
|
||||||
|
|
||||||
|
{:ok, instance} = Instance.get_cached_by_url("https://bad-nodeinfo.example.org/")
|
||||||
|
assert instance.nodeinfo == nil
|
||||||
|
end
|
||||||
|
|
||||||
|
test "doesn't store incredibly long json nodeinfo" do
|
||||||
|
too_long = String.duplicate("a", 50_000)
|
||||||
|
|
||||||
|
Tesla.Mock.mock(fn
|
||||||
|
%{url: "https://bad-nodeinfo.example.org/"} ->
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body: ~s[<html><head><h1>I wil look down and whisper "GNO.."</h1></head></html>]
|
||||||
|
}
|
||||||
|
|
||||||
|
%{url: "https://bad-nodeinfo.example.org/.well-known/nodeinfo"} ->
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body:
|
||||||
|
Jason.encode!(%{
|
||||||
|
links: [
|
||||||
|
%{
|
||||||
|
rel: "http://nodeinfo.diaspora.software/ns/schema/2.0",
|
||||||
|
href: "https://bad-nodeinfo.example.org/nodeinfo/2.0"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
%{url: "https://bad-nodeinfo.example.org/nodeinfo/2.0"} ->
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body: Jason.encode!(%{version: "2.0", software: %{name: too_long}})
|
||||||
|
}
|
||||||
|
end)
|
||||||
|
|
||||||
|
assert {:ok, true} ==
|
||||||
|
Instance.update_metadata(URI.parse("https://bad-nodeinfo.example.org/"))
|
||||||
|
|
||||||
|
{:ok, instance} = Instance.get_cached_by_url("https://bad-nodeinfo.example.org/")
|
||||||
|
assert instance.nodeinfo == nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,35 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do
|
||||||
import Mock
|
import Mock
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
describe "handle" do
|
||||||
|
test "it queues a fetch of instance information" do
|
||||||
|
author = insert(:user, local: false, ap_id: "https://wowee.example.com/users/1")
|
||||||
|
recipient = insert(:user, local: true)
|
||||||
|
|
||||||
|
{:ok, note_data, _meta} =
|
||||||
|
Builder.note(%Pleroma.Web.CommonAPI.ActivityDraft{
|
||||||
|
user: author,
|
||||||
|
to: [recipient.ap_id],
|
||||||
|
mentions: [recipient],
|
||||||
|
content_html: "hey",
|
||||||
|
extra: %{"id" => "https://wowee.example.com/notes/1"}
|
||||||
|
})
|
||||||
|
|
||||||
|
{:ok, create_activity_data, _meta} =
|
||||||
|
Builder.create(author, note_data["id"], [recipient.ap_id])
|
||||||
|
|
||||||
|
{:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false)
|
||||||
|
|
||||||
|
{:ok, _create_activity, _meta} =
|
||||||
|
SideEffects.handle(create_activity, local: false, object_data: note_data)
|
||||||
|
|
||||||
|
assert_enqueued(
|
||||||
|
worker: Pleroma.Workers.NodeInfoFetcherWorker,
|
||||||
|
args: %{"op" => "process", "source_url" => "https://wowee.example.com/users/1"}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "handle_after_transaction" do
|
describe "handle_after_transaction" do
|
||||||
test "it streams out notifications and streams" do
|
test "it streams out notifications and streams" do
|
||||||
author = insert(:user, local: true)
|
author = insert(:user, local: true)
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
|
defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
|
||||||
use Pleroma.DataCase
|
use Pleroma.DataCase, async: false
|
||||||
|
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.UserRelationship
|
alias Pleroma.UserRelationship
|
||||||
|
@ -12,6 +12,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
|
||||||
|
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
import Tesla.Mock
|
import Tesla.Mock
|
||||||
|
import Mock
|
||||||
|
|
||||||
setup do
|
setup do
|
||||||
mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
||||||
|
@ -25,6 +26,7 @@ test "Represent a user account" do
|
||||||
|
|
||||||
user =
|
user =
|
||||||
insert(:user, %{
|
insert(:user, %{
|
||||||
|
ap_id: "https://example.com/users/chikichikibanban",
|
||||||
follower_count: 3,
|
follower_count: 3,
|
||||||
note_count: 5,
|
note_count: 5,
|
||||||
background: background_image,
|
background: background_image,
|
||||||
|
@ -38,6 +40,8 @@ test "Represent a user account" do
|
||||||
also_known_as: ["https://shitposter.zone/users/shp"]
|
also_known_as: ["https://shitposter.zone/users/shp"]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
insert(:instance, %{host: "example.com", nodeinfo: %{version: "2.1"}})
|
||||||
|
|
||||||
expected = %{
|
expected = %{
|
||||||
id: to_string(user.id),
|
id: to_string(user.id),
|
||||||
username: "shp",
|
username: "shp",
|
||||||
|
@ -50,6 +54,15 @@ test "Represent a user account" do
|
||||||
statuses_count: 5,
|
statuses_count: 5,
|
||||||
note: "<span>valid html</span>. a<br/>b<br/>c<br/>d<br/>f '&<>"",
|
note: "<span>valid html</span>. a<br/>b<br/>c<br/>d<br/>f '&<>"",
|
||||||
url: user.ap_id,
|
url: user.ap_id,
|
||||||
|
akkoma: %{
|
||||||
|
instance: %{
|
||||||
|
name: "example.com",
|
||||||
|
nodeinfo: %{
|
||||||
|
"version" => "2.1"
|
||||||
|
},
|
||||||
|
favicon: nil
|
||||||
|
}
|
||||||
|
},
|
||||||
avatar: "http://localhost:4001/images/avi.png",
|
avatar: "http://localhost:4001/images/avi.png",
|
||||||
avatar_static: "http://localhost:4001/images/avi.png",
|
avatar_static: "http://localhost:4001/images/avi.png",
|
||||||
header: "http://localhost:4001/images/banner.png",
|
header: "http://localhost:4001/images/banner.png",
|
||||||
|
@ -98,9 +111,57 @@ test "Represent a user account" do
|
||||||
assert expected == AccountView.render("show.json", %{user: user, skip_visibility_check: true})
|
assert expected == AccountView.render("show.json", %{user: user, skip_visibility_check: true})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "nodeinfo" do
|
||||||
|
setup do
|
||||||
|
[
|
||||||
|
user: insert(:user, ap_id: "https://somewhere.example.com/users/chikichikibanban"),
|
||||||
|
instance:
|
||||||
|
insert(:instance, %{
|
||||||
|
host: "somewhere.example.com",
|
||||||
|
favicon: "https://example.com/favicon.ico"
|
||||||
|
})
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "is embedded in the account view", %{user: user} do
|
||||||
|
assert %{
|
||||||
|
akkoma: %{
|
||||||
|
instance: %{
|
||||||
|
name: "somewhere.example.com",
|
||||||
|
nodeinfo: %{
|
||||||
|
"version" => "2.0"
|
||||||
|
},
|
||||||
|
favicon: "https://example.com/favicon.ico"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} = AccountView.render("show.json", %{user: user, skip_visibility_check: true})
|
||||||
|
end
|
||||||
|
|
||||||
|
test "uses local nodeinfo for local users" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
akkoma: %{
|
||||||
|
instance: %{
|
||||||
|
name: "localhost",
|
||||||
|
nodeinfo: %{
|
||||||
|
software: %{
|
||||||
|
name: "akkoma"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} = AccountView.render("show.json", %{user: user, skip_visibility_check: true})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "favicon" do
|
describe "favicon" do
|
||||||
setup do
|
setup do
|
||||||
[user: insert(:user)]
|
[
|
||||||
|
user: insert(:user, ap_id: "https://example.com/users/chikichikibanban"),
|
||||||
|
instance:
|
||||||
|
insert(:instance, %{host: "example.com", favicon: "https://example.com/favicon.ico"})
|
||||||
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
test "is parsed when :instance_favicons is enabled", %{user: user} do
|
test "is parsed when :instance_favicons is enabled", %{user: user} do
|
||||||
|
@ -108,13 +169,14 @@ test "is parsed when :instance_favicons is enabled", %{user: user} do
|
||||||
|
|
||||||
assert %{
|
assert %{
|
||||||
pleroma: %{
|
pleroma: %{
|
||||||
favicon:
|
favicon: "https://example.com/favicon.ico"
|
||||||
"https://shitposter.club/plugins/Qvitter/img/gnusocial-favicons/favicon-16x16.png"
|
|
||||||
}
|
}
|
||||||
} = AccountView.render("show.json", %{user: user, skip_visibility_check: true})
|
} = AccountView.render("show.json", %{user: user, skip_visibility_check: true})
|
||||||
end
|
end
|
||||||
|
|
||||||
test "is nil when :instances_favicons is disabled", %{user: user} do
|
test "is nil when we have no instance", %{user: user} do
|
||||||
|
user = %{user | ap_id: "https://wowee.example.com/users/2"}
|
||||||
|
|
||||||
assert %{pleroma: %{favicon: nil}} =
|
assert %{pleroma: %{favicon: nil}} =
|
||||||
AccountView.render("show.json", %{user: user, skip_visibility_check: true})
|
AccountView.render("show.json", %{user: user, skip_visibility_check: true})
|
||||||
end
|
end
|
||||||
|
@ -176,11 +238,18 @@ test "Represent a Service(bot) account" do
|
||||||
},
|
},
|
||||||
fqn: "shp@shitposter.club",
|
fqn: "shp@shitposter.club",
|
||||||
last_status_at: nil,
|
last_status_at: nil,
|
||||||
|
akkoma: %{
|
||||||
|
instance: %{
|
||||||
|
name: "localhost",
|
||||||
|
favicon: "http://localhost:4001/favicon.png",
|
||||||
|
nodeinfo: %{version: "2.0"}
|
||||||
|
}
|
||||||
|
},
|
||||||
pleroma: %{
|
pleroma: %{
|
||||||
ap_id: user.ap_id,
|
ap_id: user.ap_id,
|
||||||
also_known_as: [],
|
also_known_as: [],
|
||||||
background_image: nil,
|
background_image: nil,
|
||||||
favicon: nil,
|
favicon: "http://localhost:4001/favicon.png",
|
||||||
is_confirmed: true,
|
is_confirmed: true,
|
||||||
tags: [],
|
tags: [],
|
||||||
is_admin: false,
|
is_admin: false,
|
||||||
|
@ -196,7 +265,13 @@ test "Represent a Service(bot) account" do
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assert expected == AccountView.render("show.json", %{user: user, skip_visibility_check: true})
|
with_mock(
|
||||||
|
Pleroma.Web.Nodeinfo.NodeinfoController,
|
||||||
|
raw_nodeinfo: fn -> %{version: "2.0"} end
|
||||||
|
) do
|
||||||
|
assert expected ==
|
||||||
|
AccountView.render("show.json", %{user: user, skip_visibility_check: true})
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "Represent a Funkwhale channel" do
|
test "Represent a Funkwhale channel" do
|
||||||
|
@ -578,6 +653,8 @@ test "uses mediaproxy urls when it's enabled (regardless of media preview proxy
|
||||||
emoji: %{"joker_smile" => "https://evil.website/society.png"}
|
emoji: %{"joker_smile" => "https://evil.website/society.png"}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
insert(:instance, %{host: "localhost", favicon: "https://evil.website/favicon.png"})
|
||||||
|
|
||||||
with media_preview_enabled <- [false, true] do
|
with media_preview_enabled <- [false, true] do
|
||||||
clear_config([:media_preview_proxy, :enabled], media_preview_enabled)
|
clear_config([:media_preview_proxy, :enabled], media_preview_enabled)
|
||||||
|
|
||||||
|
@ -586,6 +663,9 @@ test "uses mediaproxy urls when it's enabled (regardless of media preview proxy
|
||||||
{key, url} when key in [:avatar, :avatar_static, :header, :header_static] ->
|
{key, url} when key in [:avatar, :avatar_static, :header, :header_static] ->
|
||||||
String.starts_with?(url, Pleroma.Web.Endpoint.url())
|
String.starts_with?(url, Pleroma.Web.Endpoint.url())
|
||||||
|
|
||||||
|
{:akkoma, %{instance: %{favicon: favicon_url}}} ->
|
||||||
|
String.starts_with?(favicon_url, Pleroma.Web.Endpoint.url())
|
||||||
|
|
||||||
{:emojis, emojis} ->
|
{:emojis, emojis} ->
|
||||||
Enum.all?(emojis, fn %{url: url, static_url: static_url} ->
|
Enum.all?(emojis, fn %{url: url, static_url: static_url} ->
|
||||||
String.starts_with?(url, Pleroma.Web.Endpoint.url()) &&
|
String.starts_with?(url, Pleroma.Web.Endpoint.url()) &&
|
||||||
|
@ -598,4 +678,10 @@ test "uses mediaproxy urls when it's enabled (regardless of media preview proxy
|
||||||
|> assert()
|
|> assert()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "returns nil in the instance field when no instance is held locally" do
|
||||||
|
user = insert(:user, ap_id: "https://example.com/users/1")
|
||||||
|
view = AccountView.render("show.json", %{user: user, skip_visibility_check: true})
|
||||||
|
assert view[:akkoma][:instance] == nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.StreamerTest do
|
defmodule Pleroma.Web.StreamerTest do
|
||||||
use Pleroma.DataCase
|
use Pleroma.DataCase, async: false
|
||||||
|
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,15 @@ def conversation_factory do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def instance_factory(attrs \\ %{}) do
|
||||||
|
%Pleroma.Instances.Instance{
|
||||||
|
host: attrs[:domain] || "example.com",
|
||||||
|
nodeinfo: %{version: "2.0", openRegistrations: true},
|
||||||
|
unreachable_since: nil
|
||||||
|
}
|
||||||
|
|> Map.merge(attrs)
|
||||||
|
end
|
||||||
|
|
||||||
def user_factory(attrs \\ %{}) do
|
def user_factory(attrs \\ %{}) do
|
||||||
pem = Enum.random(@rsa_keys)
|
pem = Enum.random(@rsa_keys)
|
||||||
|
|
||||||
|
@ -522,13 +531,6 @@ def oauth_app_factory do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def instance_factory do
|
|
||||||
%Pleroma.Instances.Instance{
|
|
||||||
host: "domain.com",
|
|
||||||
unreachable_since: nil
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def oauth_token_factory(attrs \\ %{}) do
|
def oauth_token_factory(attrs \\ %{}) do
|
||||||
scopes = Map.get(attrs, :scopes, ["read"])
|
scopes = Map.get(attrs, :scopes, ["read"])
|
||||||
oauth_app = Map.get_lazy(attrs, :app, fn -> insert(:oauth_app, scopes: scopes) end)
|
oauth_app = Map.get_lazy(attrs, :app, fn -> insert(:oauth_app, scopes: scopes) end)
|
||||||
|
|
Loading…
Reference in a new issue