Merge remote-tracking branch 'origin/develop' into instance_rules

Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
marcin mikołajczak 2023-12-22 14:34:30 +01:00
commit 6051715a99
1114 changed files with 53017 additions and 6853 deletions

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Activity.Ir.TopicsTest do
use Pleroma.DataCase, async: true
use Pleroma.DataCase
alias Pleroma.Activity
alias Pleroma.Activity.Ir.Topics
@ -13,6 +13,29 @@ defmodule Pleroma.Activity.Ir.TopicsTest do
import Mock
describe "chat message" do
test "Create produces no topics" do
activity = %Activity{
object: %Object{data: %{"type" => "ChatMessage"}},
data: %{"type" => "Create"}
}
assert [] == Topics.get_activity_topics(activity)
end
test "Delete produces user and user:pleroma_chat" do
activity = %Activity{
object: %Object{data: %{"type" => "ChatMessage"}},
data: %{"type" => "Delete"}
}
topics = Topics.get_activity_topics(activity)
assert [_, _] = topics
assert "user" in topics
assert "user:pleroma_chat" in topics
end
end
describe "poll answer" do
test "produce no topics" do
activity = %Activity{object: %Object{data: %{"type" => "Answer"}}}
@ -35,7 +58,7 @@ defmodule Pleroma.Activity.Ir.TopicsTest do
setup do
activity = %Activity{
object: %Object{data: %{"type" => "Note"}},
data: %{"to" => [Pleroma.Constants.as_public()]}
data: %{"to" => [Pleroma.Constants.as_public()], "type" => "Create"}
}
{:ok, activity: activity}
@ -114,6 +137,55 @@ defmodule Pleroma.Activity.Ir.TopicsTest do
end
end
describe "public visibility Announces" do
setup do
activity = %Activity{
object: %Object{data: %{"attachment" => []}},
data: %{"type" => "Announce", "to" => [Pleroma.Constants.as_public()]}
}
{:ok, activity: activity}
end
test "does not generate public topics", %{activity: activity} do
topics = Topics.get_activity_topics(activity)
refute "public" in topics
refute "public:remote" in topics
refute "public:local" in topics
end
end
describe "local-public visibility create events" do
setup do
activity = %Activity{
object: %Object{data: %{"attachment" => []}},
data: %{"type" => "Create", "to" => [Pleroma.Web.ActivityPub.Utils.as_local_public()]}
}
{:ok, activity: activity}
end
test "doesn't produce public topics", %{activity: activity} do
topics = Topics.get_activity_topics(activity)
refute Enum.member?(topics, "public")
end
test "produces public:local topics", %{activity: activity} do
topics = Topics.get_activity_topics(activity)
assert Enum.member?(topics, "public:local")
end
test "with no attachments doesn't produce public:media topics", %{activity: activity} do
topics = Topics.get_activity_topics(activity)
refute Enum.member?(topics, "public:media")
refute Enum.member?(topics, "public:local:media")
end
end
describe "public visibility create events with attachments" do
setup do
activity = %Activity{
@ -152,9 +224,36 @@ defmodule Pleroma.Activity.Ir.TopicsTest do
end
end
describe "local-public visibility create events with attachments" do
setup do
activity = %Activity{
object: %Object{data: %{"attachment" => ["foo"]}},
data: %{"type" => "Create", "to" => [Pleroma.Web.ActivityPub.Utils.as_local_public()]}
}
{:ok, activity: activity}
end
test "do not produce public:media topics", %{activity: activity} do
topics = Topics.get_activity_topics(activity)
refute Enum.member?(topics, "public:media")
end
test "produces public:local:media topics", %{activity: activity} do
topics = Topics.get_activity_topics(activity)
assert Enum.member?(topics, "public:local:media")
end
end
describe "non-public visibility" do
test "produces direct topic" do
activity = %Activity{object: %Object{data: %{"type" => "Note"}}, data: %{"to" => []}}
activity = %Activity{
object: %Object{data: %{"type" => "Note"}},
data: %{"to" => [], "type" => "Create"}
}
topics = Topics.get_activity_topics(activity)
assert Enum.member?(topics, "direct")

View file

@ -278,4 +278,78 @@ defmodule Pleroma.ActivityTest do
assert Repo.aggregate(Activity, :count, :id) == 2
end
describe "associated_object_id() sql function" do
test "with json object" do
%{rows: [[object_id]]} =
Ecto.Adapters.SQL.query!(
Pleroma.Repo,
"""
select associated_object_id('{"object": {"id":"foobar"}}'::jsonb);
"""
)
assert object_id == "foobar"
end
test "with string object" do
%{rows: [[object_id]]} =
Ecto.Adapters.SQL.query!(
Pleroma.Repo,
"""
select associated_object_id('{"object": "foobar"}'::jsonb);
"""
)
assert object_id == "foobar"
end
test "with array object" do
%{rows: [[object_id]]} =
Ecto.Adapters.SQL.query!(
Pleroma.Repo,
"""
select associated_object_id('{"object": ["foobar", {}]}'::jsonb);
"""
)
assert object_id == "foobar"
end
test "invalid" do
%{rows: [[object_id]]} =
Ecto.Adapters.SQL.query!(
Pleroma.Repo,
"""
select associated_object_id('{"object": {}}'::jsonb);
"""
)
assert is_nil(object_id)
end
test "invalid object id" do
%{rows: [[object_id]]} =
Ecto.Adapters.SQL.query!(
Pleroma.Repo,
"""
select associated_object_id('{"object": {"id": 123}}'::jsonb);
"""
)
assert is_nil(object_id)
end
test "no object field" do
%{rows: [[object_id]]} =
Ecto.Adapters.SQL.query!(
Pleroma.Repo,
"""
select associated_object_id('{}'::jsonb);
"""
)
assert is_nil(object_id)
end
end
end

View file

@ -0,0 +1,40 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.AnnouncementReadRelationshipTest do
alias Pleroma.AnnouncementReadRelationship
use Pleroma.DataCase, async: true
import Pleroma.Factory
setup do
{:ok, user: insert(:user), announcement: insert(:announcement)}
end
describe "mark_read/2" do
test "should insert relationship", %{user: user, announcement: announcement} do
{:ok, _} = AnnouncementReadRelationship.mark_read(user, announcement)
assert AnnouncementReadRelationship.exists?(user, announcement)
end
end
describe "mark_unread/2" do
test "should delete relationship", %{user: user, announcement: announcement} do
{:ok, _} = AnnouncementReadRelationship.mark_read(user, announcement)
assert :ok = AnnouncementReadRelationship.mark_unread(user, announcement)
refute AnnouncementReadRelationship.exists?(user, announcement)
end
test "should not fail if relationship does not exist", %{
user: user,
announcement: announcement
} do
assert :ok = AnnouncementReadRelationship.mark_unread(user, announcement)
refute AnnouncementReadRelationship.exists?(user, announcement)
end
end
end

View file

@ -0,0 +1,98 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.AnnouncementTest do
alias Pleroma.Announcement
use Pleroma.DataCase, async: true
import Pleroma.Factory
describe "list_all_visible_when/1" do
setup do: {:ok, time: NaiveDateTime.utc_now()}
test "with no start or end time", %{time: time} do
_announcement = insert(:announcement)
assert [_] = Announcement.list_all_visible_when(time)
end
test "with start time before current", %{time: time} do
before_now = NaiveDateTime.add(time, -10, :second)
_announcement = insert(:announcement, %{starts_at: before_now})
assert [_] = Announcement.list_all_visible_when(time)
end
test "with start time after current", %{time: time} do
after_now = NaiveDateTime.add(time, 10, :second)
_announcement = insert(:announcement, %{starts_at: after_now})
assert [] = Announcement.list_all_visible_when(time)
end
test "with end time after current", %{time: time} do
after_now = NaiveDateTime.add(time, 10, :second)
_announcement = insert(:announcement, %{ends_at: after_now})
assert [_] = Announcement.list_all_visible_when(time)
end
test "with end time before current", %{time: time} do
before_now = NaiveDateTime.add(time, -10, :second)
_announcement = insert(:announcement, %{ends_at: before_now})
assert [] = Announcement.list_all_visible_when(time)
end
test "with both start and end time", %{time: time} do
before_now = NaiveDateTime.add(time, -10, :second)
after_now = NaiveDateTime.add(time, 10, :second)
_announcement = insert(:announcement, %{starts_at: before_now, ends_at: after_now})
assert [_] = Announcement.list_all_visible_when(time)
end
test "with both start and end time, current not in the range", %{time: time} do
before_now = NaiveDateTime.add(time, -10, :second)
after_now = NaiveDateTime.add(time, 10, :second)
_announcement = insert(:announcement, %{starts_at: after_now, ends_at: before_now})
assert [] = Announcement.list_all_visible_when(time)
end
end
describe "announcements formatting" do
test "it formats links" do
raw = "something on https://pleroma.social ."
announcement = insert(:announcement, %{data: %{"content" => raw}})
assert announcement.rendered["content"] =~ ~r(<a.+?https://pleroma.social)
assert announcement.data["content"] == raw
end
test "it formats mentions" do
user = insert(:user)
raw = "something on @#{user.nickname} ."
announcement = insert(:announcement, %{data: %{"content" => raw}})
assert announcement.rendered["content"] =~ ~r(<a.+?#{user.nickname})
assert announcement.data["content"] == raw
end
test "it formats tags" do
raw = "something on #mew ."
announcement = insert(:announcement, %{data: %{"content" => raw}})
assert announcement.rendered["content"] =~ ~r(<a.+?#mew)
assert announcement.data["content"] == raw
end
end
end

View file

@ -1,89 +0,0 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.BBS.HandlerTest do
use Pleroma.DataCase, async: true
alias Pleroma.Activity
alias Pleroma.BBS.Handler
alias Pleroma.Object
alias Pleroma.Repo
alias Pleroma.User
alias Pleroma.Web.CommonAPI
import ExUnit.CaptureIO
import Pleroma.Factory
import Ecto.Query
test "getting the home timeline" do
user = insert(:user)
followed = insert(:user)
{:ok, user, followed} = User.follow(user, followed)
{:ok, _first} = CommonAPI.post(user, %{status: "hey"})
{:ok, _second} = CommonAPI.post(followed, %{status: "hello"})
output =
capture_io(fn ->
Handler.handle_command(%{user: user}, "home")
end)
assert output =~ user.nickname
assert output =~ followed.nickname
assert output =~ "hey"
assert output =~ "hello"
end
test "posting" do
user = insert(:user)
output =
capture_io(fn ->
Handler.handle_command(%{user: user}, "p this is a test post")
end)
assert output =~ "Posted"
activity =
Repo.one(
from(a in Activity,
where: fragment("?->>'type' = ?", a.data, "Create")
)
)
assert activity.actor == user.ap_id
object = Object.normalize(activity, fetch: false)
assert object.data["content"] == "this is a test post"
end
test "replying" do
user = insert(:user)
another_user = insert(:user)
{:ok, activity} = CommonAPI.post(another_user, %{status: "this is a test post"})
activity_object = Object.normalize(activity, fetch: false)
output =
capture_io(fn ->
Handler.handle_command(%{user: user}, "r #{activity.id} this is a reply")
end)
assert output =~ "Replied"
reply =
Repo.one(
from(a in Activity,
where: fragment("?->>'type' = ?", a.data, "Create"),
where: a.actor == ^user.ap_id
)
)
assert reply.actor == user.ap_id
reply_object_data = Object.normalize(reply, fetch: false).data
assert reply_object_data["content"] == "this is a reply"
assert reply_object_data["inReplyTo"] == activity_object.data["id"]
end
end

View file

@ -11,6 +11,62 @@ defmodule Pleroma.Config.DeprecationWarningsTest do
alias Pleroma.Config
alias Pleroma.Config.DeprecationWarnings
describe "filter exiftool" do
test "gives warning when still used" do
clear_config(
[Pleroma.Upload, :filters],
[Pleroma.Upload.Filter.Exiftool]
)
assert capture_log(fn -> DeprecationWarnings.check_exiftool_filter() end) =~
"""
!!!DEPRECATION WARNING!!!
Your config is using Exiftool as a filter instead of Exiftool.StripLocation. This should work for now, but you are advised to change to the new configuration to prevent possible issues later:
```
config :pleroma, Pleroma.Upload,
filters: [Pleroma.Upload.Filter.Exiftool]
```
Is now
```
config :pleroma, Pleroma.Upload,
filters: [Pleroma.Upload.Filter.Exiftool.StripLocation]
```
"""
end
test "changes setting to exiftool strip location" do
clear_config(
[Pleroma.Upload, :filters],
[Pleroma.Upload.Filter.Exiftool, Pleroma.Upload.Filter.Exiftool.ReadDescription]
)
expected_config = [
Pleroma.Upload.Filter.Exiftool.StripLocation,
Pleroma.Upload.Filter.Exiftool.ReadDescription
]
capture_log(fn -> DeprecationWarnings.warn() end)
assert Config.get([Pleroma.Upload]) |> Keyword.get(:filters, []) == expected_config
end
test "doesn't give a warning with correct config" do
clear_config(
[Pleroma.Upload, :filters],
[
Pleroma.Upload.Filter.Exiftool.StripLocation,
Pleroma.Upload.Filter.Exiftool.ReadDescription
]
)
assert capture_log(fn -> DeprecationWarnings.check_exiftool_filter() end) == ""
end
end
describe "simple policy tuples" do
test "gives warning when there are still strings" do
clear_config([:mrf_simple],

View file

@ -11,7 +11,6 @@ defmodule Pleroma.Config.LoaderTest do
config = Loader.read("test/fixtures/config/temp.secret.exs")
assert config[:pleroma][:first_setting][:key] == "value"
assert config[:pleroma][:first_setting][:key2] == [Pleroma.Repo]
assert config[:quack][:level] == :info
end
test "filter_group/2" do

View file

@ -10,13 +10,15 @@ defmodule Pleroma.Config.ReleaseRuntimeProviderTest do
describe "load/2" do
test "loads release defaults config and warns about non-existent runtime config" do
ExUnit.CaptureIO.capture_io(fn ->
merged = ReleaseRuntimeProvider.load([], [])
merged = ReleaseRuntimeProvider.load([], config_path: "/var/empty/config.exs")
assert merged == Pleroma.Config.Holder.release_defaults()
end) =~
"!!! Config path is not declared! Please ensure it exists and that PLEROMA_CONFIG_PATH is unset or points to an existing file"
end
test "merged runtime config" do
assert :ok == File.chmod!("test/fixtures/config/temp.secret.exs", 0o640)
merged =
ReleaseRuntimeProvider.load([], config_path: "test/fixtures/config/temp.secret.exs")
@ -25,6 +27,8 @@ defmodule Pleroma.Config.ReleaseRuntimeProviderTest do
end
test "merged exported config" do
assert :ok == File.chmod!("test/fixtures/config/temp.exported_from_db.secret.exs", 0o640)
ExUnit.CaptureIO.capture_io(fn ->
merged =
ReleaseRuntimeProvider.load([],
@ -37,6 +41,9 @@ defmodule Pleroma.Config.ReleaseRuntimeProviderTest do
end
test "runtime config is merged with exported config" do
assert :ok == File.chmod!("test/fixtures/config/temp.secret.exs", 0o640)
assert :ok == File.chmod!("test/fixtures/config/temp.exported_from_db.secret.exs", 0o640)
merged =
ReleaseRuntimeProvider.load([],
config_path: "test/fixtures/config/temp.secret.exs",

View file

@ -15,13 +15,11 @@ defmodule Pleroma.Config.TransferTaskTest do
test "transfer config values from db to env" do
refute Application.get_env(:pleroma, :test_key)
refute Application.get_env(:idna, :test_key)
refute Application.get_env(:quack, :test_key)
refute Application.get_env(:postgrex, :test_key)
initial = Application.get_env(:logger, :level)
insert(:config, key: :test_key, value: [live: 2, com: 3])
insert(:config, group: :idna, key: :test_key, value: [live: 15, com: 35])
insert(:config, group: :quack, key: :test_key, value: [:test_value1, :test_value2])
insert(:config, group: :postgrex, key: :test_key, value: :value)
insert(:config, group: :logger, key: :level, value: :debug)
@ -29,36 +27,32 @@ defmodule Pleroma.Config.TransferTaskTest do
assert Application.get_env(:pleroma, :test_key) == [live: 2, com: 3]
assert Application.get_env(:idna, :test_key) == [live: 15, com: 35]
assert Application.get_env(:quack, :test_key) == [:test_value1, :test_value2]
assert Application.get_env(:logger, :level) == :debug
assert Application.get_env(:postgrex, :test_key) == :value
on_exit(fn ->
Application.delete_env(:pleroma, :test_key)
Application.delete_env(:idna, :test_key)
Application.delete_env(:quack, :test_key)
Application.delete_env(:postgrex, :test_key)
Application.put_env(:logger, :level, initial)
end)
end
test "transfer config values for 1 group and some keys" do
level = Application.get_env(:quack, :level)
meta = Application.get_env(:quack, :meta)
level = Application.get_env(:somegroup, :level)
meta = Application.get_env(:somegroup, :meta)
insert(:config, group: :quack, key: :level, value: :info)
insert(:config, group: :quack, key: :meta, value: [:none])
insert(:config, group: :somegroup, key: :level, value: :info)
insert(:config, group: :somegroup, key: :meta, value: [:none])
TransferTask.start_link([])
assert Application.get_env(:quack, :level) == :info
assert Application.get_env(:quack, :meta) == [:none]
default = Pleroma.Config.Holder.default_config(:quack, :webhook_url)
assert Application.get_env(:quack, :webhook_url) == default
assert Application.get_env(:somegroup, :level) == :info
assert Application.get_env(:somegroup, :meta) == [:none]
on_exit(fn ->
Application.put_env(:quack, :level, level)
Application.put_env(:quack, :meta, meta)
Application.put_env(:somegroup, :level, level)
Application.put_env(:somegroup, :meta, meta)
end)
end
@ -79,35 +73,70 @@ defmodule Pleroma.Config.TransferTaskTest do
describe "pleroma restart" do
setup do
on_exit(fn -> Restarter.Pleroma.refresh() end)
on_exit(fn ->
Restarter.Pleroma.refresh()
# Restarter.Pleroma.refresh/0 is an asynchronous call.
# A GenServer will first finish the previous call before starting a new one.
# Here we do a synchronous call.
# That way we are sure that the previous call has finished before we continue.
# See https://stackoverflow.com/questions/51361856/how-to-use-task-await-with-genserver
Restarter.Pleroma.rebooted?()
end)
end
@tag :erratic
test "don't restart if no reboot time settings were changed" do
clear_config(:emoji)
insert(:config, key: :emoji, value: [groups: [a: 1, b: 2]])
refute String.contains?(
capture_log(fn -> TransferTask.start_link([]) end),
capture_log(fn ->
TransferTask.start_link([])
# TransferTask.start_link/1 is an asynchronous call.
# A GenServer will first finish the previous call before starting a new one.
# Here we do a synchronous call.
# That way we are sure that the previous call has finished before we continue.
Restarter.Pleroma.rebooted?()
end),
"pleroma restarted"
)
end
@tag :erratic
test "on reboot time key" do
clear_config(:shout)
insert(:config, key: :shout, value: [enabled: false])
assert capture_log(fn -> TransferTask.start_link([]) end) =~ "pleroma restarted"
# Note that we don't actually restart Pleroma.
# See module Restarter.Pleroma
assert capture_log(fn ->
TransferTask.start_link([])
# TransferTask.start_link/1 is an asynchronous call.
# A GenServer will first finish the previous call before starting a new one.
# Here we do a synchronous call.
# That way we are sure that the previous call has finished before we continue.
Restarter.Pleroma.rebooted?()
end) =~ "pleroma restarted"
end
@tag :erratic
test "on reboot time subkey" do
clear_config(Pleroma.Captcha)
insert(:config, key: Pleroma.Captcha, value: [seconds_valid: 60])
assert capture_log(fn -> TransferTask.start_link([]) end) =~ "pleroma restarted"
# Note that we don't actually restart Pleroma.
# See module Restarter.Pleroma
assert capture_log(fn ->
TransferTask.start_link([])
# TransferTask.start_link/1 is an asynchronous call.
# A GenServer will first finish the previous call before starting a new one.
# Here we do a synchronous call.
# That way we are sure that the previous call has finished before we continue.
Restarter.Pleroma.rebooted?()
end) =~ "pleroma restarted"
end
@tag :erratic
test "don't restart pleroma on reboot time key and subkey if there is false flag" do
clear_config(:shout)
clear_config(Pleroma.Captcha)
@ -116,7 +145,15 @@ defmodule Pleroma.Config.TransferTaskTest do
insert(:config, key: Pleroma.Captcha, value: [seconds_valid: 60])
refute String.contains?(
capture_log(fn -> TransferTask.load_and_update_env([], false) end),
capture_log(fn ->
TransferTask.load_and_update_env([], false)
# TransferTask.start_link/1 is an asynchronous call.
# A GenServer will first finish the previous call before starting a new one.
# Here we do a synchronous call.
# That way we are sure that the previous call has finished before we continue.
Restarter.Pleroma.rebooted?()
end),
"pleroma restarted"
)
end

View file

@ -16,13 +16,13 @@ defmodule Pleroma.ConfigDBTest do
test "get_all_as_keyword/0" do
saved = insert(:config)
insert(:config, group: ":quack", key: ":level", value: :info)
insert(:config, group: ":quack", key: ":meta", value: [:none])
insert(:config, group: ":goose", key: ":level", value: :info)
insert(:config, group: ":goose", key: ":meta", value: [:none])
insert(:config,
group: ":quack",
group: ":goose",
key: ":webhook_url",
value: "https://hooks.slack.com/services/KEY/some_val"
value: "https://gander.com/"
)
config = ConfigDB.get_all_as_keyword()
@ -31,9 +31,9 @@ defmodule Pleroma.ConfigDBTest do
{saved.key, saved.value}
]
assert config[:quack][:level] == :info
assert config[:quack][:meta] == [:none]
assert config[:quack][:webhook_url] == "https://hooks.slack.com/services/KEY/some_val"
assert config[:goose][:level] == :info
assert config[:goose][:meta] == [:none]
assert config[:goose][:webhook_url] == "https://gander.com/"
end
describe "update_or_create/1" do
@ -238,10 +238,11 @@ defmodule Pleroma.ConfigDBTest do
end
test "ssl options" do
assert ConfigDB.to_elixir_types([":tlsv1", ":tlsv1.1", ":tlsv1.2"]) == [
assert ConfigDB.to_elixir_types([":tlsv1", ":tlsv1.1", ":tlsv1.2", ":tlsv1.3"]) == [
:tlsv1,
:"tlsv1.1",
:"tlsv1.2"
:"tlsv1.2",
:"tlsv1.3"
]
end
@ -266,10 +267,6 @@ defmodule Pleroma.ConfigDBTest do
assert ConfigDB.to_elixir_types("ExSyslogger") == ExSyslogger
end
test "Quack.Logger module" do
assert ConfigDB.to_elixir_types("Quack.Logger") == Quack.Logger
end
test "Swoosh.Adapters modules" do
assert ConfigDB.to_elixir_types("Swoosh.Adapters.SMTP") == Swoosh.Adapters.SMTP
assert ConfigDB.to_elixir_types("Swoosh.Adapters.AmazonSES") == Swoosh.Adapters.AmazonSES
@ -446,13 +443,13 @@ defmodule Pleroma.ConfigDBTest do
test "common keyword" do
assert ConfigDB.to_elixir_types([
%{"tuple" => [":level", ":warn"]},
%{"tuple" => [":level", ":warning"]},
%{"tuple" => [":meta", [":all"]]},
%{"tuple" => [":path", ""]},
%{"tuple" => [":val", nil]},
%{"tuple" => [":webhook_url", "https://hooks.slack.com/services/YOUR-KEY-HERE"]}
]) == [
level: :warn,
level: :warning,
meta: [:all],
path: "",
val: nil,

View file

@ -122,11 +122,11 @@ defmodule Pleroma.Conversation.ParticipationTest do
end
test "it marks a participation as read" do
participation = insert(:participation, %{read: false})
participation = insert(:participation, %{updated_at: ~N[2017-07-17 17:09:58], read: false})
{:ok, updated_participation} = Participation.mark_as_read(participation)
assert updated_participation.read
assert updated_participation.updated_at == participation.updated_at
assert :gt = NaiveDateTime.compare(updated_participation.updated_at, participation.updated_at)
end
test "it marks a participation as unread" do

View file

@ -13,6 +13,11 @@ defmodule Pleroma.ConversationTest do
setup_all do: clear_config([:instance, :federating], true)
setup do
Mox.stub_with(Pleroma.UnstubbedConfigMock, Pleroma.Config)
:ok
end
test "it goes through old direct conversations" do
user = insert(:user)
other_user = insert(:user)

View file

@ -86,7 +86,7 @@ defmodule Pleroma.Docs.GeneratorTest do
key: :versions,
type: {:list, :atom},
description: "List of TLS version to use",
suggestions: [:tlsv1, ":tlsv1.1", ":tlsv1.2"]
suggestions: [:tlsv1, ":tlsv1.1", ":tlsv1.2", ":tlsv1.3"]
}
]
},
@ -213,7 +213,7 @@ defmodule Pleroma.Docs.GeneratorTest do
test "suggestion for tls versions" do
[%{children: children} | _] = Generator.convert_to_strings(@descriptions)
child = Enum.at(children, 8)
assert child[:suggestions] == [":tlsv1", ":tlsv1.1", ":tlsv1.2"]
assert child[:suggestions] == [":tlsv1", ":tlsv1.1", ":tlsv1.2", ":tlsv1.3"]
end
test "subgroup with module name" do

View file

@ -0,0 +1,90 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Docs.Translator.CompilerTest do
use ExUnit.Case, async: true
alias Pleroma.Docs.Translator.Compiler
@descriptions [
%{
key: "1",
label: "1",
description: "2",
children: [
%{
key: "3",
label: "3",
description: "4"
},
%{
key: "5",
label: "5",
description: "6"
}
]
},
%{
key: "7",
label: "7",
description: "8",
children: [
%{
key: "9",
description: "9",
children: [
%{
key: "10",
description: "10",
children: [
%{key: "11", description: "11"},
%{description: "12"}
]
}
]
},
%{
label: "13"
}
]
},
%{
group: "14",
label: "14"
},
%{
group: "15",
key: "15",
label: "15"
},
%{
group: {":subgroup", "16"},
label: "16"
}
]
describe "extract_strings/1" do
test "it extracts all labels and descriptions" do
strings = Compiler.extract_strings(@descriptions)
assert length(strings) == 16
assert {["1"], "label", "1"} in strings
assert {["1"], "description", "2"} in strings
assert {["1", "3"], "label", "3"} in strings
assert {["1", "3"], "description", "4"} in strings
assert {["1", "5"], "label", "5"} in strings
assert {["1", "5"], "description", "6"} in strings
assert {["7"], "label", "7"} in strings
assert {["7"], "description", "8"} in strings
assert {["7", "9"], "description", "9"} in strings
assert {["7", "9", "10"], "description", "10"} in strings
assert {["7", "9", "10", "11"], "description", "11"} in strings
assert {["7", "9", "10", nil], "description", "12"} in strings
assert {["7", nil], "label", "13"} in strings
assert {["14"], "label", "14"} in strings
assert {["15-15"], "label", "15"} in strings
assert {["16"], "label", "16"} in strings
end
end
end

View file

@ -0,0 +1,25 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2023 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.BareUriTest do
use Pleroma.DataCase, async: true
alias Pleroma.EctoType.ActivityPub.ObjectValidators.BareUri
test "diaspora://" do
text = "diaspora://alice@fediverse.example/post/deadbeefdeadbeefdeadbeefdeadbeef"
assert {:ok, ^text} = BareUri.cast(text)
end
test "nostr:" do
text = "nostr:note1gwdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
assert {:ok, ^text} = BareUri.cast(text)
end
test "errors for non-URIs" do
assert :error == BareUri.cast(1)
assert :error == BareUri.cast("foo")
assert :error == BareUri.cast("foo bar")
end
end

View file

@ -90,4 +90,8 @@ defmodule Pleroma.Emoji.PackTest do
assert updated_pack.files_count == 1
end
test "load_pack/1 ignores path traversal in a forged pack name", %{pack: pack} do
assert {:ok, ^pack} = Pack.load_pack("../../../../../dump_pack")
end
end

View file

@ -17,6 +17,7 @@ defmodule Pleroma.HTMLTest do
this is a link with allowed "rel" attribute: <a href="http://example.com/" rel="tag">example.com</a>
this is a link with not allowed "rel" attribute: <a href="http://example.com/" rel="tag noallowed">example.com</a>
this is an image: <img src="http://example.com/image.jpg"><br />
this is an inline emoji: <img class="emoji" src="http://example.com/image.jpg"><br />
<script>alert('hacked')</script>
"""
@ -24,6 +25,10 @@ defmodule Pleroma.HTMLTest do
<img src="http://example.com/image.jpg" onerror="alert('hacked')">
"""
@html_stillimage_sample """
<img class="still-image" src="http://example.com/image.jpg">
"""
@html_span_class_sample """
<span class="animate-spin">hi</span>
"""
@ -45,6 +50,7 @@ defmodule Pleroma.HTMLTest do
this is a link with allowed &quot;rel&quot; attribute: example.com
this is a link with not allowed &quot;rel&quot; attribute: example.com
this is an image:
this is an inline emoji:
alert(&#39;hacked&#39;)
"""
@ -67,6 +73,7 @@ defmodule Pleroma.HTMLTest do
this is a link with allowed &quot;rel&quot; attribute: <a href="http://example.com/" rel="tag">example.com</a>
this is a link with not allowed &quot;rel&quot; attribute: <a href="http://example.com/">example.com</a>
this is an image: <img src="http://example.com/image.jpg"/><br/>
this is an inline emoji: <img class="emoji" src="http://example.com/image.jpg"/><br/>
alert(&#39;hacked&#39;)
"""
@ -90,6 +97,15 @@ defmodule Pleroma.HTMLTest do
HTML.filter_tags(@html_span_class_sample, Pleroma.HTML.Scrubber.TwitterText)
end
test "does not allow images with invalid classes" do
expected = """
<img src="http://example.com/image.jpg"/>
"""
assert expected ==
HTML.filter_tags(@html_stillimage_sample, Pleroma.HTML.Scrubber.TwitterText)
end
test "does allow microformats" do
expected = """
<span class="h-card"><a class="u-url mention">@<span>foo</span></a></span>
@ -121,6 +137,7 @@ defmodule Pleroma.HTMLTest do
this is a link with allowed &quot;rel&quot; attribute: <a href="http://example.com/" rel="tag">example.com</a>
this is a link with not allowed &quot;rel&quot; attribute: <a href="http://example.com/">example.com</a>
this is an image: <img src="http://example.com/image.jpg"/><br/>
this is an inline emoji: <img class="emoji" src="http://example.com/image.jpg"/><br/>
alert(&#39;hacked&#39;)
"""
@ -143,6 +160,15 @@ defmodule Pleroma.HTMLTest do
assert expected == HTML.filter_tags(@html_span_class_sample, Pleroma.HTML.Scrubber.Default)
end
test "does not allow images with invalid classes" do
expected = """
<img src="http://example.com/image.jpg"/>
"""
assert expected ==
HTML.filter_tags(@html_stillimage_sample, Pleroma.HTML.Scrubber.TwitterText)
end
test "does allow microformats" do
expected = """
<span class="h-card"><a class="u-url mention">@<span>foo</span></a></span>

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.HTTP.AdapterHelper.HackneyTest do
use ExUnit.Case, async: true
use ExUnit.Case
use Pleroma.Tests.Helpers
alias Pleroma.HTTP.AdapterHelper.Hackney

View file

@ -0,0 +1,45 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.HTTP.WebPushTest do
use ExUnit.Case
import Tesla.Mock
alias Pleroma.HTTP
@push_url "https://some-push-server/"
setup do
mock(fn
%{
method: :post,
url: @push_url,
headers: headers
} ->
if {"content-type", "octet-stream"} in headers do
%Tesla.Env{
status: 200
}
else
%Tesla.Env{
status: 403
}
end
end)
:ok
end
test "post" do
response =
HTTP.WebPush.post(
@push_url,
"encrypted payload",
%{"authorization" => "WebPush"},
[]
)
assert {:ok, %{status: 200}} = response
end
end

View file

@ -31,14 +31,6 @@ defmodule Pleroma.Instances.InstanceTest do
assert {:ok, instance} = Instance.set_reachable(instance.host)
refute instance.unreachable_since
end
test "does NOT create an Instance record in case of no existing matching record" do
host = "domain.org"
assert nil == Instance.set_reachable(host)
assert [] = Repo.all(Ecto.Query.from(i in Instance))
assert Instance.reachable?(host)
end
end
describe "set_unreachable/1" do
@ -161,6 +153,66 @@ defmodule Pleroma.Instances.InstanceTest do
end
end
describe "get_or_update_metadata/1" do
test "Scrapes Wildebeest NodeInfo" do
Tesla.Mock.mock(fn
%{url: "https://wildebeest.example.org/.well-known/nodeinfo"} ->
%Tesla.Env{
status: 200,
body: File.read!("test/fixtures/wildebeest-well-known-nodeinfo.json")
}
%{url: "https://wildebeest.example.org/nodeinfo/2.1"} ->
%Tesla.Env{
status: 200,
body: File.read!("test/fixtures/wildebeest-nodeinfo21.json")
}
end)
expected = %{
software_name: "wildebeest",
software_repository: "https://github.com/cloudflare/wildebeest",
software_version: "0.0.1"
}
assert expected ==
Instance.get_or_update_metadata(URI.parse("https://wildebeest.example.org/"))
expected = %Pleroma.Instances.Instance.Pleroma.Instances.Metadata{
software_name: "wildebeest",
software_repository: "https://github.com/cloudflare/wildebeest",
software_version: "0.0.1"
}
assert expected ==
Repo.get_by(Pleroma.Instances.Instance, %{host: "wildebeest.example.org"}).metadata
end
test "Scrapes Mastodon NodeInfo" do
Tesla.Mock.mock(fn
%{url: "https://mastodon.example.org/.well-known/nodeinfo"} ->
%Tesla.Env{
status: 200,
body: File.read!("test/fixtures/mastodon-well-known-nodeinfo.json")
}
%{url: "https://mastodon.example.org/nodeinfo/2.0"} ->
%Tesla.Env{
status: 200,
body: File.read!("test/fixtures/mastodon-nodeinfo20.json")
}
end)
expected = %{
software_name: "mastodon",
software_version: "4.1.0"
}
assert expected ==
Instance.get_or_update_metadata(URI.parse("https://mastodon.example.org/"))
end
end
test "delete_users_and_activities/1 deletes remote instance users and activities" do
[mario, luigi, _peach, wario] =
users = [

View file

@ -31,22 +31,41 @@ defmodule Pleroma.Integration.MastodonWebsocketTest do
WebsocketClient.start_link(self(), path, headers)
end
defp decode_json(json) do
with {:ok, %{"event" => event, "payload" => payload_text}} <- Jason.decode(json),
{:ok, payload} <- Jason.decode(payload_text) do
{:ok, %{"event" => event, "payload" => payload}}
end
end
# Turns atom keys to strings
defp atom_key_to_string(json) do
json
|> Jason.encode!()
|> Jason.decode!()
end
test "refuses invalid requests" do
capture_log(fn ->
assert {:error, {404, _}} = start_socket()
assert {:error, {404, _}} = start_socket("?stream=ncjdk")
assert {:error, %WebSockex.RequestError{code: 404}} = start_socket("?stream=ncjdk")
Process.sleep(30)
end)
end
test "requires authentication and a valid token for protected streams" do
capture_log(fn ->
assert {:error, {401, _}} = start_socket("?stream=user&access_token=aaaaaaaaaaaa")
assert {:error, {401, _}} = start_socket("?stream=user")
assert {:error, %WebSockex.RequestError{code: 401}} =
start_socket("?stream=user&access_token=aaaaaaaaaaaa")
assert {:error, %WebSockex.RequestError{code: 401}} = start_socket("?stream=user")
Process.sleep(30)
end)
end
test "allows unified stream" do
assert {:ok, _} = start_socket()
end
test "allows public streams without authentication" do
assert {:ok, _} = start_socket("?stream=public")
assert {:ok, _} = start_socket("?stream=public:local")
@ -68,12 +87,143 @@ defmodule Pleroma.Integration.MastodonWebsocketTest do
view_json =
Pleroma.Web.MastodonAPI.StatusView.render("show.json", activity: activity, for: nil)
|> Jason.encode!()
|> Jason.decode!()
|> atom_key_to_string()
assert json == view_json
end
describe "subscribing via WebSocket" do
test "can subscribe" do
user = insert(:user)
{:ok, pid} = start_socket()
WebsocketClient.send_text(pid, %{type: "subscribe", stream: "public"} |> Jason.encode!())
assert_receive {:text, raw_json}, 1_000
assert {:ok,
%{
"event" => "pleroma:respond",
"payload" => %{"type" => "subscribe", "result" => "success"}
}} = decode_json(raw_json)
{:ok, activity} = CommonAPI.post(user, %{status: "nice echo chamber"})
assert_receive {:text, raw_json}, 1_000
assert {:ok, json} = Jason.decode(raw_json)
assert "update" == json["event"]
assert json["payload"]
assert {:ok, json} = Jason.decode(json["payload"])
view_json =
Pleroma.Web.MastodonAPI.StatusView.render("show.json", activity: activity, for: nil)
|> Jason.encode!()
|> Jason.decode!()
assert json == view_json
end
test "can subscribe to multiple streams" do
user = insert(:user)
{:ok, pid} = start_socket()
WebsocketClient.send_text(pid, %{type: "subscribe", stream: "public"} |> Jason.encode!())
assert_receive {:text, raw_json}, 1_000
assert {:ok,
%{
"event" => "pleroma:respond",
"payload" => %{"type" => "subscribe", "result" => "success"}
}} = decode_json(raw_json)
WebsocketClient.send_text(
pid,
%{type: "subscribe", stream: "hashtag", tag: "mew"} |> Jason.encode!()
)
assert_receive {:text, raw_json}, 1_000
assert {:ok,
%{
"event" => "pleroma:respond",
"payload" => %{"type" => "subscribe", "result" => "success"}
}} = decode_json(raw_json)
{:ok, _activity} = CommonAPI.post(user, %{status: "nice echo chamber #mew"})
assert_receive {:text, raw_json}, 1_000
assert {:ok, %{"stream" => stream1}} = Jason.decode(raw_json)
assert_receive {:text, raw_json}, 1_000
assert {:ok, %{"stream" => stream2}} = Jason.decode(raw_json)
streams = [stream1, stream2]
assert ["hashtag", "mew"] in streams
assert ["public"] in streams
end
test "won't double subscribe" do
user = insert(:user)
{:ok, pid} = start_socket()
WebsocketClient.send_text(pid, %{type: "subscribe", stream: "public"} |> Jason.encode!())
assert_receive {:text, raw_json}, 1_000
assert {:ok,
%{
"event" => "pleroma:respond",
"payload" => %{"type" => "subscribe", "result" => "success"}
}} = decode_json(raw_json)
WebsocketClient.send_text(pid, %{type: "subscribe", stream: "public"} |> Jason.encode!())
assert_receive {:text, raw_json}, 1_000
assert {:ok,
%{
"event" => "pleroma:respond",
"payload" => %{"type" => "subscribe", "result" => "ignored"}
}} = decode_json(raw_json)
{:ok, _activity} = CommonAPI.post(user, %{status: "nice echo chamber"})
assert_receive {:text, _}, 1_000
refute_receive {:text, _}, 1_000
end
test "rejects invalid streams" do
{:ok, pid} = start_socket()
WebsocketClient.send_text(pid, %{type: "subscribe", stream: "nonsense"} |> Jason.encode!())
assert_receive {:text, raw_json}, 1_000
assert {:ok,
%{
"event" => "pleroma:respond",
"payload" => %{"type" => "subscribe", "result" => "error", "error" => "bad_topic"}
}} = decode_json(raw_json)
end
test "can unsubscribe" do
user = insert(:user)
{:ok, pid} = start_socket()
WebsocketClient.send_text(pid, %{type: "subscribe", stream: "public"} |> Jason.encode!())
assert_receive {:text, raw_json}, 1_000
assert {:ok,
%{
"event" => "pleroma:respond",
"payload" => %{"type" => "subscribe", "result" => "success"}
}} = decode_json(raw_json)
WebsocketClient.send_text(pid, %{type: "unsubscribe", stream: "public"} |> Jason.encode!())
assert_receive {:text, raw_json}, 1_000
assert {:ok,
%{
"event" => "pleroma:respond",
"payload" => %{"type" => "unsubscribe", "result" => "success"}
}} = decode_json(raw_json)
{:ok, _activity} = CommonAPI.post(user, %{status: "nice echo chamber"})
refute_receive {:text, _}, 1_000
end
end
describe "with a valid user token" do
setup do
{:ok, app} =
@ -91,7 +241,7 @@ defmodule Pleroma.Integration.MastodonWebsocketTest do
{:ok, token} = OAuth.Token.exchange_token(app, auth)
%{user: user, token: token}
%{app: app, user: user, token: token}
end
test "accepts valid tokens", state do
@ -102,7 +252,7 @@ defmodule Pleroma.Integration.MastodonWebsocketTest do
assert {:ok, _} = start_socket("?stream=user&access_token=#{token.token}")
capture_log(fn ->
assert {:error, {401, _}} = start_socket("?stream=user")
assert {:error, %WebSockex.RequestError{code: 401}} = start_socket("?stream=user")
Process.sleep(30)
end)
end
@ -111,7 +261,9 @@ defmodule Pleroma.Integration.MastodonWebsocketTest do
assert {:ok, _} = start_socket("?stream=user:notification&access_token=#{token.token}")
capture_log(fn ->
assert {:error, {401, _}} = start_socket("?stream=user:notification")
assert {:error, %WebSockex.RequestError{code: 401}} =
start_socket("?stream=user:notification")
Process.sleep(30)
end)
end
@ -120,11 +272,225 @@ defmodule Pleroma.Integration.MastodonWebsocketTest do
assert {:ok, _} = start_socket("?stream=user", [{"Sec-WebSocket-Protocol", token.token}])
capture_log(fn ->
assert {:error, {401, _}} =
assert {:error, %WebSockex.RequestError{code: 401}} =
start_socket("?stream=user", [{"Sec-WebSocket-Protocol", "I am a friend"}])
Process.sleep(30)
end)
end
test "accepts valid token on client-sent event", %{token: token} do
assert {:ok, pid} = start_socket()
WebsocketClient.send_text(
pid,
%{type: "pleroma:authenticate", token: token.token} |> Jason.encode!()
)
assert_receive {:text, raw_json}, 1_000
assert {:ok,
%{
"event" => "pleroma:respond",
"payload" => %{"type" => "pleroma:authenticate", "result" => "success"}
}} = decode_json(raw_json)
WebsocketClient.send_text(pid, %{type: "subscribe", stream: "user"} |> Jason.encode!())
assert_receive {:text, raw_json}, 1_000
assert {:ok,
%{
"event" => "pleroma:respond",
"payload" => %{"type" => "subscribe", "result" => "success"}
}} = decode_json(raw_json)
end
test "rejects invalid token on client-sent event" do
assert {:ok, pid} = start_socket()
WebsocketClient.send_text(
pid,
%{type: "pleroma:authenticate", token: "Something else"} |> Jason.encode!()
)
assert_receive {:text, raw_json}, 1_000
assert {:ok,
%{
"event" => "pleroma:respond",
"payload" => %{
"type" => "pleroma:authenticate",
"result" => "error",
"error" => "unauthorized"
}
}} = decode_json(raw_json)
end
test "rejects new authenticate request if already logged-in", %{token: token} do
assert {:ok, pid} = start_socket()
WebsocketClient.send_text(
pid,
%{type: "pleroma:authenticate", token: token.token} |> Jason.encode!()
)
assert_receive {:text, raw_json}, 1_000
assert {:ok,
%{
"event" => "pleroma:respond",
"payload" => %{"type" => "pleroma:authenticate", "result" => "success"}
}} = decode_json(raw_json)
WebsocketClient.send_text(
pid,
%{type: "pleroma:authenticate", token: "Something else"} |> Jason.encode!()
)
assert_receive {:text, raw_json}, 1_000
assert {:ok,
%{
"event" => "pleroma:respond",
"payload" => %{
"type" => "pleroma:authenticate",
"result" => "error",
"error" => "already_authenticated"
}
}} = decode_json(raw_json)
end
test "accepts the 'list' stream", %{token: token, user: user} do
posting_user = insert(:user)
{:ok, list} = Pleroma.List.create("test", user)
Pleroma.List.follow(list, posting_user)
assert {:ok, _} = start_socket("?stream=list&access_token=#{token.token}&list=#{list.id}")
assert {:ok, pid} = start_socket("?access_token=#{token.token}")
WebsocketClient.send_text(
pid,
%{type: "subscribe", stream: "list", list: list.id} |> Jason.encode!()
)
assert_receive {:text, raw_json}, 1_000
assert {:ok,
%{
"event" => "pleroma:respond",
"payload" => %{"type" => "subscribe", "result" => "success"}
}} = decode_json(raw_json)
WebsocketClient.send_text(
pid,
%{type: "subscribe", stream: "list", list: to_string(list.id)} |> Jason.encode!()
)
assert_receive {:text, raw_json}, 1_000
assert {:ok,
%{
"event" => "pleroma:respond",
"payload" => %{"type" => "subscribe", "result" => "ignored"}
}} = decode_json(raw_json)
end
test "disconnect when token is revoked", %{app: app, user: user, token: token} do
assert {:ok, _} = start_socket("?stream=user:notification&access_token=#{token.token}")
assert {:ok, _} = start_socket("?stream=user&access_token=#{token.token}")
{:ok, auth} = OAuth.Authorization.create_authorization(app, user)
{:ok, token2} = OAuth.Token.exchange_token(app, auth)
assert {:ok, _} = start_socket("?stream=user&access_token=#{token2.token}")
OAuth.Token.Strategy.Revoke.revoke(token)
assert_receive {:close, _}
assert_receive {:close, _}
refute_receive {:close, _}
end
test "receives private statuses", %{user: reading_user, token: token} do
user = insert(:user)
CommonAPI.follow(reading_user, user)
{:ok, _} = start_socket("?stream=user&access_token=#{token.token}")
{:ok, activity} =
CommonAPI.post(user, %{status: "nice echo chamber", visibility: "private"})
assert_receive {:text, raw_json}, 1_000
assert {:ok, json} = Jason.decode(raw_json)
assert "update" == json["event"]
assert json["payload"]
assert {:ok, json} = Jason.decode(json["payload"])
view_json =
Pleroma.Web.MastodonAPI.StatusView.render("show.json",
activity: activity,
for: reading_user
)
|> Jason.encode!()
|> Jason.decode!()
assert json == view_json
end
test "receives edits", %{user: reading_user, token: token} do
user = insert(:user)
CommonAPI.follow(reading_user, user)
{:ok, _} = start_socket("?stream=user&access_token=#{token.token}")
{:ok, activity} =
CommonAPI.post(user, %{status: "nice echo chamber", visibility: "private"})
assert_receive {:text, _raw_json}, 1_000
{:ok, _} = CommonAPI.update(user, activity, %{status: "mew mew", visibility: "private"})
assert_receive {:text, raw_json}, 1_000
activity = Pleroma.Activity.normalize(activity)
view_json =
Pleroma.Web.MastodonAPI.StatusView.render("show.json",
activity: activity,
for: reading_user
)
|> Jason.encode!()
|> Jason.decode!()
assert {:ok, %{"event" => "status.update", "payload" => ^view_json}} = decode_json(raw_json)
end
test "receives notifications", %{user: reading_user, token: token} do
user = insert(:user)
CommonAPI.follow(reading_user, user)
{:ok, _} = start_socket("?stream=user:notification&access_token=#{token.token}")
{:ok, %Pleroma.Activity{id: activity_id} = _activity} =
CommonAPI.post(user, %{
status: "nice echo chamber @#{reading_user.nickname}",
visibility: "private"
})
assert_receive {:text, raw_json}, 1_000
assert {:ok,
%{
"event" => "notification",
"payload" => %{
"status" => %{
"id" => ^activity_id
}
}
}} = decode_json(raw_json)
end
end
end

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.NotificationTest do
use Pleroma.DataCase
use Pleroma.DataCase, async: false
import Pleroma.Factory
import Mock
@ -21,6 +21,11 @@ defmodule Pleroma.NotificationTest do
alias Pleroma.Web.Push
alias Pleroma.Web.Streamer
setup do
Mox.stub_with(Pleroma.UnstubbedConfigMock, Pleroma.Config)
:ok
end
describe "create_notifications" do
test "never returns nil" do
user = insert(:user)
@ -32,20 +37,26 @@ defmodule Pleroma.NotificationTest do
refute {:ok, [nil]} == Notification.create_notifications(activity)
end
test "creates a notification for a report" do
test "creates a report notification only for privileged users" do
reporting_user = insert(:user)
reported_user = insert(:user)
{:ok, moderator_user} = insert(:user) |> User.admin_api_update(%{is_moderator: true})
moderator_user = insert(:user, is_moderator: true)
{:ok, activity} = CommonAPI.report(reporting_user, %{account_id: reported_user.id})
clear_config([:instance, :moderator_privileges], [])
{:ok, activity1} = CommonAPI.report(reporting_user, %{account_id: reported_user.id})
{:ok, []} = Notification.create_notifications(activity1)
{:ok, [notification]} = Notification.create_notifications(activity)
clear_config([:instance, :moderator_privileges], [:reports_manage_reports])
{:ok, activity2} = CommonAPI.report(reporting_user, %{account_id: reported_user.id})
{:ok, [notification]} = Notification.create_notifications(activity2)
assert notification.user_id == moderator_user.id
assert notification.type == "pleroma:report"
end
test "suppresses notification to reporter if reporter is an admin" do
test "suppresses notifications for own reports" do
clear_config([:instance, :admin_privileges], [:reports_manage_reports])
reporting_admin = insert(:user, is_admin: true)
reported_user = insert(:user)
other_admin = insert(:user, is_admin: true)
@ -127,6 +138,28 @@ defmodule Pleroma.NotificationTest do
subscriber_notifications = Notification.for_user(subscriber)
assert Enum.empty?(subscriber_notifications)
end
test "it sends edited notifications to those who repeated a status" do
user = insert(:user)
repeated_user = insert(:user)
other_user = insert(:user)
{:ok, activity_one} =
CommonAPI.post(user, %{
status: "hey @#{other_user.nickname}!"
})
{:ok, _activity_two} = CommonAPI.repeat(activity_one.id, repeated_user)
{:ok, _edit_activity} =
CommonAPI.update(user, activity_one, %{
status: "hey @#{other_user.nickname}! mew mew"
})
assert [%{type: "reblog"}] = Notification.for_user(user)
assert [%{type: "update"}] = Notification.for_user(repeated_user)
assert [%{type: "mention"}] = Notification.for_user(other_user)
end
end
test "create_poll_notifications/1" do
@ -224,7 +257,7 @@ defmodule Pleroma.NotificationTest do
task =
Task.async(fn ->
{:ok, _topic} = Streamer.get_topic_and_add_socket("user", user, oauth_token)
assert_receive {:render_with_user, _, _, _}, 4_000
assert_receive {:render_with_user, _, _, _, _}, 4_000
end)
task_user_notification =
@ -232,7 +265,7 @@ defmodule Pleroma.NotificationTest do
{:ok, _topic} =
Streamer.get_topic_and_add_socket("user:notification", user, oauth_token)
assert_receive {:render_with_user, _, _, _}, 4_000
assert_receive {:render_with_user, _, _, _, _}, 4_000
end)
activity = insert(:note_activity)
@ -306,6 +339,32 @@ defmodule Pleroma.NotificationTest do
refute Notification.create_notification(activity, followed)
end
test "it disables notifications from non-followees" do
follower = insert(:user)
followed =
insert(:user,
notification_settings: %Pleroma.User.NotificationSetting{block_from_strangers: true}
)
CommonAPI.follow(follower, followed)
{:ok, activity} = CommonAPI.post(follower, %{status: "hey @#{followed.nickname}"})
refute Notification.create_notification(activity, followed)
end
test "it allows notifications from followees" do
poster = insert(:user)
receiver =
insert(:user,
notification_settings: %Pleroma.User.NotificationSetting{block_from_strangers: true}
)
CommonAPI.follow(receiver, poster)
{:ok, activity} = CommonAPI.post(poster, %{status: "hey @#{receiver.nickname}"})
assert Notification.create_notification(activity, receiver)
end
test "it doesn't create a notification for user if he is the activity author" do
activity = insert(:note_activity)
author = User.get_cached_by_ap_id(activity.data["actor"])
@ -520,25 +579,6 @@ defmodule Pleroma.NotificationTest do
end
end
describe "destroy_multiple_from_types/2" do
test "clears all notifications of a certain type for a given user" do
report_activity = insert(:report_activity)
user1 = insert(:user, is_moderator: true, is_admin: true)
user2 = insert(:user, is_moderator: true, is_admin: true)
{:ok, _} = Notification.create_notifications(report_activity)
{:ok, _} =
CommonAPI.post(user2, %{
status: "hey @#{user1.nickname} !"
})
Notification.destroy_multiple_from_types(user1, ["pleroma:report"])
assert [%Pleroma.Notification{type: "mention"}] = Notification.for_user(user1)
assert [%Pleroma.Notification{type: "pleroma:report"}] = Notification.for_user(user2)
end
end
describe "set_read_up_to()" do
test "it sets all notifications as read up to a specified notification ID" do
user = insert(:user)
@ -839,6 +879,30 @@ defmodule Pleroma.NotificationTest do
assert [other_user] == enabled_receivers
assert [] == disabled_receivers
end
test "it sends edited notifications to those who repeated a status" do
user = insert(:user)
repeated_user = insert(:user)
other_user = insert(:user)
{:ok, activity_one} =
CommonAPI.post(user, %{
status: "hey @#{other_user.nickname}!"
})
{:ok, _activity_two} = CommonAPI.repeat(activity_one.id, repeated_user)
{:ok, edit_activity} =
CommonAPI.update(user, activity_one, %{
status: "hey @#{other_user.nickname}! mew mew"
})
{enabled_receivers, _disabled_receivers} =
Notification.get_notified_from_activity(edit_activity)
assert repeated_user in enabled_receivers
assert other_user not in enabled_receivers
end
end
describe "notification lifecycle" do
@ -1192,5 +1256,32 @@ defmodule Pleroma.NotificationTest do
assert length(Notification.for_user(user)) == 1
end
test "it returns notifications when related object is without content and filters are defined",
%{user: user} do
followed_user = insert(:user, is_locked: true)
insert(:filter, user: followed_user, phrase: "test", hide: true)
{:ok, _, _, _activity} = CommonAPI.follow(user, followed_user)
refute FollowingRelationship.following?(user, followed_user)
assert [notification] = Notification.for_user(followed_user)
assert %{type: "follow_request"} =
NotificationView.render("show.json", %{
notification: notification,
for: followed_user
})
assert {:ok, _} = CommonAPI.accept_follow_request(user, followed_user)
assert [notification] = Notification.for_user(followed_user)
assert %{type: "follow"} =
NotificationView.render("show.json", %{
notification: notification,
for: followed_user
})
end
end
end

View file

@ -6,10 +6,15 @@ defmodule Pleroma.Object.FetcherTest do
use Pleroma.DataCase
alias Pleroma.Activity
alias Pleroma.Instances
alias Pleroma.Object
alias Pleroma.Object.Fetcher
alias Pleroma.Web.ActivityPub.ObjectValidator
require Pleroma.Constants
import Mock
import Pleroma.Factory
import Tesla.Mock
setup do
@ -159,6 +164,17 @@ defmodule Pleroma.Object.FetcherTest do
"https://patch.cx/media/03ca3c8b4ac3ddd08bf0f84be7885f2f88de0f709112131a22d83650819e36c2.json"
)
end
test "it resets instance reachability on successful fetch" do
id = "http://mastodon.example.org/@admin/99541947525187367"
Instances.set_consistently_unreachable(id)
refute Instances.reachable?(id)
{:ok, _object} =
Fetcher.fetch_object_from_id("http://mastodon.example.org/@admin/99541947525187367")
assert Instances.reachable?(id)
end
end
describe "implementation quirks" do
@ -269,4 +285,331 @@ defmodule Pleroma.Object.FetcherTest do
refute called(Pleroma.Signature.sign(:_, :_))
end
end
describe "refetching" do
setup do
insert(:user, ap_id: "https://mastodon.social/users/emelie")
object1 = %{
"id" => "https://mastodon.social/1",
"actor" => "https://mastodon.social/users/emelie",
"attributedTo" => "https://mastodon.social/users/emelie",
"type" => "Note",
"content" => "test 1",
"bcc" => [],
"bto" => [],
"cc" => [],
"to" => [Pleroma.Constants.as_public()],
"summary" => "",
"published" => "2023-05-08 23:43:20Z",
"updated" => "2023-05-09 23:43:20Z"
}
{:ok, local_object1, _} = ObjectValidator.validate(object1, [])
object2 = %{
"id" => "https://mastodon.social/2",
"actor" => "https://mastodon.social/users/emelie",
"attributedTo" => "https://mastodon.social/users/emelie",
"type" => "Note",
"content" => "test 2",
"bcc" => [],
"bto" => [],
"cc" => [],
"to" => [Pleroma.Constants.as_public()],
"summary" => "",
"published" => "2023-05-08 23:43:20Z",
"updated" => "2023-05-09 23:43:25Z",
"formerRepresentations" => %{
"type" => "OrderedCollection",
"orderedItems" => [
%{
"type" => "Note",
"content" => "orig 2",
"actor" => "https://mastodon.social/users/emelie",
"attributedTo" => "https://mastodon.social/users/emelie",
"bcc" => [],
"bto" => [],
"cc" => [],
"to" => [Pleroma.Constants.as_public()],
"summary" => "",
"published" => "2023-05-08 23:43:20Z",
"updated" => "2023-05-09 23:43:21Z"
}
],
"totalItems" => 1
}
}
{:ok, local_object2, _} = ObjectValidator.validate(object2, [])
mock(fn
%{
method: :get,
url: "https://mastodon.social/1"
} ->
%Tesla.Env{
status: 200,
headers: [{"content-type", "application/activity+json"}],
body: Jason.encode!(object1 |> Map.put("updated", "2023-05-09 23:44:20Z"))
}
%{
method: :get,
url: "https://mastodon.social/2"
} ->
%Tesla.Env{
status: 200,
headers: [{"content-type", "application/activity+json"}],
body: Jason.encode!(object2 |> Map.put("updated", "2023-05-09 23:44:20Z"))
}
%{
method: :get,
url: "https://mastodon.social/users/emelie/collections/featured"
} ->
%Tesla.Env{
status: 200,
headers: [{"content-type", "application/activity+json"}],
body:
Jason.encode!(%{
"id" => "https://mastodon.social/users/emelie/collections/featured",
"type" => "OrderedCollection",
"actor" => "https://mastodon.social/users/emelie",
"attributedTo" => "https://mastodon.social/users/emelie",
"orderedItems" => [],
"totalItems" => 0
})
}
env ->
apply(HttpRequestMock, :request, [env])
end)
%{object1: local_object1, object2: local_object2}
end
test "it keeps formerRepresentations if remote does not have this attr", %{object1: object1} do
full_object1 =
object1
|> Map.merge(%{
"formerRepresentations" => %{
"type" => "OrderedCollection",
"orderedItems" => [
%{
"type" => "Note",
"content" => "orig 2",
"actor" => "https://mastodon.social/users/emelie",
"attributedTo" => "https://mastodon.social/users/emelie",
"bcc" => [],
"bto" => [],
"cc" => [],
"to" => [Pleroma.Constants.as_public()],
"summary" => "",
"published" => "2023-05-08 23:43:20Z"
}
],
"totalItems" => 1
}
})
{:ok, o} = Object.create(full_object1)
assert {:ok, refetched} = Fetcher.refetch_object(o)
assert %{"formerRepresentations" => %{"orderedItems" => [%{"content" => "orig 2"}]}} =
refetched.data
end
test "it uses formerRepresentations from remote if possible", %{object2: object2} do
{:ok, o} = Object.create(object2)
assert {:ok, refetched} = Fetcher.refetch_object(o)
assert %{"formerRepresentations" => %{"orderedItems" => [%{"content" => "orig 2"}]}} =
refetched.data
end
test "it replaces formerRepresentations with the one from remote", %{object2: object2} do
full_object2 =
object2
|> Map.merge(%{
"content" => "mew mew #def",
"formerRepresentations" => %{
"type" => "OrderedCollection",
"orderedItems" => [
%{"type" => "Note", "content" => "mew mew 2"}
],
"totalItems" => 1
}
})
{:ok, o} = Object.create(full_object2)
assert {:ok, refetched} = Fetcher.refetch_object(o)
assert %{
"content" => "test 2",
"formerRepresentations" => %{"orderedItems" => [%{"content" => "orig 2"}]}
} = refetched.data
end
test "it adds to formerRepresentations if the remote does not have one and the object has changed",
%{object1: object1} do
full_object1 =
object1
|> Map.merge(%{
"content" => "mew mew #def",
"formerRepresentations" => %{
"type" => "OrderedCollection",
"orderedItems" => [
%{"type" => "Note", "content" => "mew mew 1"}
],
"totalItems" => 1
}
})
{:ok, o} = Object.create(full_object1)
assert {:ok, refetched} = Fetcher.refetch_object(o)
assert %{
"content" => "test 1",
"formerRepresentations" => %{
"orderedItems" => [
%{"content" => "mew mew #def"},
%{"content" => "mew mew 1"}
],
"totalItems" => 2
}
} = refetched.data
end
test "it keeps the history intact if only updated time has changed",
%{object1: object1} do
full_object1 =
object1
|> Map.merge(%{
"updated" => "2023-05-08 23:43:47Z",
"formerRepresentations" => %{
"type" => "OrderedCollection",
"orderedItems" => [
%{"type" => "Note", "content" => "mew mew 1"}
],
"totalItems" => 1
}
})
{:ok, o} = Object.create(full_object1)
assert {:ok, refetched} = Fetcher.refetch_object(o)
assert %{
"content" => "test 1",
"formerRepresentations" => %{
"orderedItems" => [
%{"content" => "mew mew 1"}
],
"totalItems" => 1
}
} = refetched.data
end
test "it goes through ObjectValidator and MRF", %{object2: object2} do
with_mock Pleroma.Web.ActivityPub.MRF, [:passthrough],
filter: fn
%{"type" => "Note"} = object ->
{:ok, Map.put(object, "content", "MRFd content")}
arg ->
passthrough([arg])
end do
{:ok, o} = Object.create(object2)
assert {:ok, refetched} = Fetcher.refetch_object(o)
assert %{"content" => "MRFd content"} = refetched.data
end
end
end
describe "fetch with history" do
setup do
object2 = %{
"id" => "https://mastodon.social/2",
"actor" => "https://mastodon.social/users/emelie",
"attributedTo" => "https://mastodon.social/users/emelie",
"type" => "Note",
"content" => "test 2",
"bcc" => [],
"bto" => [],
"cc" => ["https://mastodon.social/users/emelie/followers"],
"to" => [],
"summary" => "",
"formerRepresentations" => %{
"type" => "OrderedCollection",
"orderedItems" => [
%{
"type" => "Note",
"content" => "orig 2",
"actor" => "https://mastodon.social/users/emelie",
"attributedTo" => "https://mastodon.social/users/emelie",
"bcc" => [],
"bto" => [],
"cc" => ["https://mastodon.social/users/emelie/followers"],
"to" => [],
"summary" => ""
}
],
"totalItems" => 1
}
}
mock(fn
%{
method: :get,
url: "https://mastodon.social/2"
} ->
%Tesla.Env{
status: 200,
headers: [{"content-type", "application/activity+json"}],
body: Jason.encode!(object2)
}
%{
method: :get,
url: "https://mastodon.social/users/emelie/collections/featured"
} ->
%Tesla.Env{
status: 200,
headers: [{"content-type", "application/activity+json"}],
body:
Jason.encode!(%{
"id" => "https://mastodon.social/users/emelie/collections/featured",
"type" => "OrderedCollection",
"actor" => "https://mastodon.social/users/emelie",
"attributedTo" => "https://mastodon.social/users/emelie",
"orderedItems" => [],
"totalItems" => 0
})
}
env ->
apply(HttpRequestMock, :request, [env])
end)
%{object2: object2}
end
test "it gets history", %{object2: object2} do
{:ok, object} = Fetcher.fetch_object_from_id(object2["id"])
assert %{
"formerRepresentations" => %{
"type" => "OrderedCollection",
"orderedItems" => [%{}]
}
} = object.data
end
end
end

View file

@ -0,0 +1,76 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Object.UpdaterTest do
use Pleroma.DataCase
use Oban.Testing, repo: Pleroma.Repo
import Pleroma.Factory
alias Pleroma.Object.Updater
describe "make_update_object_data/3" do
setup do
note = insert(:note)
%{original_data: note.data}
end
test "it makes an updated field", %{original_data: original_data} do
new_data = Map.put(original_data, "content", "new content")
date = Pleroma.Web.ActivityPub.Utils.make_date()
update_object_data = Updater.make_update_object_data(original_data, new_data, date)
assert %{"updated" => ^date} = update_object_data
end
test "it creates formerRepresentations", %{original_data: original_data} do
new_data = Map.put(original_data, "content", "new content")
date = Pleroma.Web.ActivityPub.Utils.make_date()
update_object_data = Updater.make_update_object_data(original_data, new_data, date)
history_item = original_data |> Map.drop(["id", "formerRepresentations"])
assert %{
"formerRepresentations" => %{
"totalItems" => 1,
"orderedItems" => [^history_item]
}
} = update_object_data
end
end
describe "make_new_object_data_from_update_object/2" do
test "it reuses formerRepresentations if it exists" do
%{data: original_data} = insert(:note)
new_data =
original_data
|> Map.put("content", "edited")
date = Pleroma.Web.ActivityPub.Utils.make_date()
update_object_data = Updater.make_update_object_data(original_data, new_data, date)
history = update_object_data["formerRepresentations"]["orderedItems"]
update_object_data =
update_object_data
|> put_in(
["formerRepresentations", "orderedItems"],
history ++ [Map.put(original_data, "summary", "additional summary")]
)
|> put_in(["formerRepresentations", "totalItems"], length(history) + 1)
%{
updated_data: updated_data,
updated: updated,
used_history_in_new_object?: used_history_in_new_object?
} = Updater.make_new_object_data_from_update_object(original_data, update_object_data)
assert updated
assert used_history_in_new_object?
assert updated_data["formerRepresentations"] == update_object_data["formerRepresentations"]
end
end
end

View file

@ -7,6 +7,7 @@ defmodule Pleroma.ObjectTest do
use Oban.Testing, repo: Pleroma.Repo
import ExUnit.CaptureLog
import Mox
import Pleroma.Factory
import Tesla.Mock
@ -15,10 +16,12 @@ defmodule Pleroma.ObjectTest do
alias Pleroma.Object
alias Pleroma.Repo
alias Pleroma.Tests.ObanHelpers
alias Pleroma.UnstubbedConfigMock, as: ConfigMock
alias Pleroma.Web.CommonAPI
setup do
mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
ConfigMock |> stub_with(Pleroma.Test.StaticConfig)
:ok
end
@ -444,4 +447,42 @@ defmodule Pleroma.ObjectTest do
Enum.sort_by(object.hashtags, & &1.name)
end
end
describe "get_emoji_reactions/1" do
test "3-tuple current format" do
object = %Object{
data: %{
"reactions" => [
["x", ["https://some/user"], "https://some/emoji"]
]
}
}
assert Object.get_emoji_reactions(object) == object.data["reactions"]
end
test "2-tuple legacy format" do
object = %Object{
data: %{
"reactions" => [
["x", ["https://some/user"]]
]
}
}
assert Object.get_emoji_reactions(object) == [["x", ["https://some/user"], nil]]
end
test "Map format" do
object = %Object{
data: %{
"reactions" => %{
"x" => ["https://some/user"]
}
}
}
assert Object.get_emoji_reactions(object) == [["x", ["https://some/user"], nil]]
end
end
end

View file

@ -13,4 +13,27 @@ defmodule Pleroma.ReportNoteTest do
assert {:ok, note} = ReportNote.create(user.id, report.id, "naughty boy")
assert note.content == "naughty boy"
end
test "create/3 with very long content" do
user = insert(:user)
report = insert(:report_activity)
very_long_content = """
] pwgen 25 15
eJ9eeceiquoolei2queeLeimi aiN9ie2iokie8chush7aiph5N ulaNgaighoPiequaipuzoog8F
Ohphei0hee6hoo0wah4Aasah9 ziel3Yo3eew4neiy3ekiesh8u ue9ShahTh7oongoPheeneijah
ohGheeCh6aloque0Neviopou3 ush2oobohxeec4aequeich3Oo Ze3eighoowiojadohch8iCa1n
Yu4yieBie9eengoich8fae4th chohqu6exooSiibogh3iefeez peephahtaik9quie5mohD9nee
eeQuur3rie5mei8ieng6iesie wei1meinguv0Heidoov8Ibaed deemo2Poh6ohc3eiBeez1uox2
] pwgen 25 15
eJ9eeceiquoolei2queeLeimi aiN9ie2iokie8chush7aiph5N ulaNgaighoPiequaipuzoog8F
Ohphei0hee6hoo0wah4Aasah9 ziel3Yo3eew4neiy3ekiesh8u ue9ShahTh7oongoPheeneijah
ohGheeCh6aloque0Neviopou3 ush2oobohxeec4aequeich3Oo Ze3eighoowiojadohch8iCa1n
Yu4yieBie9eengoich8fae4th chohqu6exooSiibogh3iefeez peephahtaik9quie5mohD9nee
eeQuur3rie5mei8ieng6iesie wei1meinguv0Heidoov8Ibaed deemo2Poh6ohc3eiBeez1uox2
"""
assert {:ok, note} = ReportNote.create(user.id, report.id, very_long_content)
assert note.content == very_long_content
end
end

View file

@ -0,0 +1,103 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.ResilienceTest do
use Pleroma.Web.ConnCase, async: true
import Pleroma.Factory
alias Pleroma.Activity
alias Pleroma.Repo
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.MastodonAPI.StatusView
setup do
# user = insert(:user)
%{user: user, conn: conn} = oauth_access(["write", "read"])
other_user = insert(:user)
{:ok, post_one} = CommonAPI.post(user, %{status: "Here is a post"})
{:ok, like} = CommonAPI.favorite(other_user, post_one.id)
%{
user: user,
other_user: other_user,
post_one: post_one,
like: like,
conn: conn
}
end
test "after destruction of like activities, things still work", %{
user: user,
post_one: post,
other_user: other_user,
conn: conn,
like: like
} do
post = Repo.get(Activity, post.id)
# Rendering the liked status
rendered_for_user = StatusView.render("show.json", %{activity: post, for: user})
assert rendered_for_user.favourites_count == 1
rendered_for_other_user = StatusView.render("show.json", %{activity: post, for: other_user})
assert rendered_for_other_user.favourites_count == 1
assert rendered_for_other_user.favourited
# Getting the favourited by
[liking_user] =
conn
|> get("/api/v1/statuses/#{post.id}/favourited_by")
|> json_response(200)
assert liking_user["id"] == other_user.id
# We have one notification
[notification] =
conn
|> get("/api/v1/notifications")
|> json_response(200)
assert notification["type"] == "favourite"
# Destroying the like
Repo.delete(like)
post = Repo.get(Activity, post.id)
# Rendering the liked status
rendered_for_user = StatusView.render("show.json", %{activity: post, for: user})
assert rendered_for_user.favourites_count == 1
rendered_for_other_user = StatusView.render("show.json", %{activity: post, for: other_user})
assert rendered_for_other_user.favourites_count == 1
assert rendered_for_other_user.favourited
# Getting the favourited by
[liking_user] =
conn
|> get("/api/v1/statuses/#{post.id}/favourited_by")
|> json_response(200)
assert liking_user["id"] == other_user.id
# Notification is removed
assert [] ==
conn
|> get("/api/v1/notifications")
|> json_response(200)
# Favoriting again doesn't hurt
{:ok, _like_two} = CommonAPI.favorite(other_user, post.id)
post = Repo.get(Activity, post.id)
# Rendering the liked status
rendered_for_user = StatusView.render("show.json", %{activity: post, for: user})
assert rendered_for_user.favourites_count == 1
# General fallout: Can't unfavorite stuff anymore. Acceptable for remote users.
end
end

View file

@ -3,19 +3,23 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.ScheduledActivityTest do
use Pleroma.DataCase
use Pleroma.DataCase, async: true
alias Pleroma.ScheduledActivity
alias Pleroma.Test.StaticConfig
alias Pleroma.UnstubbedConfigMock, as: ConfigMock
import Mox
import Pleroma.Factory
setup do: clear_config([ScheduledActivity, :enabled])
setup [:ensure_local_uploader]
describe "creation" do
test "scheduled activities with jobs when ScheduledActivity enabled" do
clear_config([ScheduledActivity, :enabled], true)
ConfigMock
|> stub(:get, fn
[ScheduledActivity, :enabled] -> true
path -> StaticConfig.get(path)
end)
user = insert(:user)
today =
@ -34,7 +38,12 @@ defmodule Pleroma.ScheduledActivityTest do
end
test "scheduled activities without jobs when ScheduledActivity disabled" do
clear_config([ScheduledActivity, :enabled], false)
ConfigMock
|> stub(:get, fn
[ScheduledActivity, :enabled] -> false
path -> StaticConfig.get(path)
end)
user = insert(:user)
today =
@ -53,6 +62,9 @@ defmodule Pleroma.ScheduledActivityTest do
end
test "when daily user limit is exceeded" do
ConfigMock
|> stub_with(StaticConfig)
user = insert(:user)
today =
@ -69,6 +81,9 @@ defmodule Pleroma.ScheduledActivityTest do
end
test "when total user limit is exceeded" do
ConfigMock
|> stub_with(StaticConfig)
user = insert(:user)
today =
@ -89,6 +104,9 @@ defmodule Pleroma.ScheduledActivityTest do
end
test "when scheduled_at is earlier than 5 minute from now" do
ConfigMock
|> stub_with(StaticConfig)
user = insert(:user)
scheduled_at =

View file

@ -2,8 +2,8 @@
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Activity.SearchTest do
alias Pleroma.Activity.Search
defmodule Pleroma.Search.DatabaseSearchTest do
alias Pleroma.Search.DatabaseSearch, as: Search
alias Pleroma.Web.CommonAPI
import Pleroma.Factory
@ -18,6 +18,23 @@ defmodule Pleroma.Activity.SearchTest do
assert result.id == post.id
end
test "it finds local-only posts for authenticated users" do
user = insert(:user)
reader = insert(:user)
{:ok, post} = CommonAPI.post(user, %{status: "it's wednesday my dudes", visibility: "local"})
[result] = Search.search(reader, "wednesday")
assert result.id == post.id
end
test "it does not find local-only posts for anonymous users" do
user = insert(:user)
{:ok, _post} = CommonAPI.post(user, %{status: "it's wednesday my dudes", visibility: "local"})
assert [] = Search.search(nil, "wednesday")
end
test "using plainto_tsquery on postgres < 11" do
old_version = :persistent_term.get({Pleroma.Repo, :postgres_version})
:persistent_term.put({Pleroma.Repo, :postgres_version}, 10.0)

View file

@ -0,0 +1,160 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Search.MeilisearchTest do
require Pleroma.Constants
use Pleroma.DataCase, async: true
use Oban.Testing, repo: Pleroma.Repo
import Pleroma.Factory
import Tesla.Mock
import Mox
alias Pleroma.Search.Meilisearch
alias Pleroma.UnstubbedConfigMock, as: Config
alias Pleroma.Web.CommonAPI
alias Pleroma.Workers.SearchIndexingWorker
describe "meilisearch" do
test "indexes a local post on creation" do
user = insert(:user)
Tesla.Mock.mock(fn
%{
method: :put,
url: "http://127.0.0.1:7700/indexes/objects/documents",
body: body
} ->
assert match?(
[%{"content" => "guys i just don&#39;t wanna leave the swamp"}],
Jason.decode!(body)
)
# To make sure that the worker is called
send(self(), "posted_to_meilisearch")
%{
"enqueuedAt" => "2023-11-12T12:36:46.927517Z",
"indexUid" => "objects",
"status" => "enqueued",
"taskUid" => 6,
"type" => "documentAdditionOrUpdate"
}
|> json()
end)
Config
|> expect(:get, 3, fn
[Pleroma.Search, :module], nil ->
Meilisearch
[Pleroma.Search.Meilisearch, :url], nil ->
"http://127.0.0.1:7700"
[Pleroma.Search.Meilisearch, :private_key], nil ->
"secret"
end)
{:ok, activity} =
CommonAPI.post(user, %{
status: "guys i just don't wanna leave the swamp",
visibility: "public"
})
args = %{"op" => "add_to_index", "activity" => activity.id}
assert_enqueued(
worker: SearchIndexingWorker,
args: args
)
assert :ok = perform_job(SearchIndexingWorker, args)
assert_received("posted_to_meilisearch")
end
test "doesn't index posts that are not public" do
user = insert(:user)
Enum.each(["private", "direct"], fn visibility ->
{:ok, activity} =
CommonAPI.post(user, %{
status: "guys i just don't wanna leave the swamp",
visibility: visibility
})
args = %{"op" => "add_to_index", "activity" => activity.id}
Config
|> expect(:get, fn
[Pleroma.Search, :module], nil ->
Meilisearch
end)
assert_enqueued(worker: SearchIndexingWorker, args: args)
assert :ok = perform_job(SearchIndexingWorker, args)
end)
end
test "deletes posts from index when deleted locally" do
user = insert(:user)
Tesla.Mock.mock(fn
%{
method: :put,
url: "http://127.0.0.1:7700/indexes/objects/documents",
body: body
} ->
assert match?(
[%{"content" => "guys i just don&#39;t wanna leave the swamp"}],
Jason.decode!(body)
)
%{
"enqueuedAt" => "2023-11-12T12:36:46.927517Z",
"indexUid" => "objects",
"status" => "enqueued",
"taskUid" => 6,
"type" => "documentAdditionOrUpdate"
}
|> json()
%{method: :delete, url: "http://127.0.0.1:7700/indexes/objects/documents/" <> id} ->
send(self(), "called_delete")
assert String.length(id) > 1
json(%{})
end)
Config
|> expect(:get, 6, fn
[Pleroma.Search, :module], nil ->
Meilisearch
[Pleroma.Search.Meilisearch, :url], nil ->
"http://127.0.0.1:7700"
[Pleroma.Search.Meilisearch, :private_key], nil ->
"secret"
end)
{:ok, activity} =
CommonAPI.post(user, %{
status: "guys i just don't wanna leave the swamp",
visibility: "public"
})
args = %{"op" => "add_to_index", "activity" => activity.id}
assert_enqueued(worker: SearchIndexingWorker, args: args)
assert :ok = perform_job(SearchIndexingWorker, args)
{:ok, _} = CommonAPI.delete(activity.id, user)
delete_args = %{"op" => "remove_from_index", "object" => activity.object.id}
assert_enqueued(worker: SearchIndexingWorker, args: delete_args)
assert :ok = perform_job(SearchIndexingWorker, delete_args)
assert_received("called_delete")
end
end
end

View file

@ -43,10 +43,7 @@ defmodule Pleroma.SignatureTest do
end
test "it returns error when not found user" do
assert capture_log(fn ->
assert Signature.fetch_public_key(make_fake_conn("https://test-ap-id")) ==
{:error, :error}
end) =~ "[error] Could not decode user"
assert Signature.fetch_public_key(make_fake_conn("https://test-ap-id")) == {:error, :error}
end
test "it returns error if public key is nil" do
@ -109,6 +106,11 @@ defmodule Pleroma.SignatureTest do
{:ok, "https://example.com/users/1234"}
end
test "it deduces the actor id for gotoSocial" do
assert Signature.key_id_to_actor_id("https://example.com/users/1234/main-key") ==
{:ok, "https://example.com/users/1234"}
end
test "it calls webfinger for 'acct:' accounts" do
with_mock(Pleroma.Web.WebFinger,
finger: fn _ -> %{"ap_id" => "https://gensokyo.2hu/users/raymoo"} end

View file

@ -20,6 +20,20 @@ defmodule Pleroma.Upload.Filter.AnalyzeMetadataTest do
assert meta.blurhash
end
test "it blurhashes images with an alpha component" do
upload = %Pleroma.Upload{
name: "an… image.jpg",
content_type: "image/jpeg",
path: Path.absname("test/fixtures/png_with_transparency.png"),
tempfile: Path.absname("test/fixtures/png_with_transparency.png")
}
{:ok, :filtered, meta} = AnalyzeMetadata.filter(upload)
assert %{width: 320, height: 320} = meta
assert meta.blurhash == "eXJi-E:SwCEm5rCmn$+YWYn+15K#5A$xxCi{SiV]s*W:Efa#s.jE-T"
end
test "adds the dimensions for videos" do
upload = %Pleroma.Upload{
name: "coolvideo.mp4",

View file

@ -0,0 +1,144 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Upload.Filter.Exiftool.ReadDescriptionTest do
use Pleroma.DataCase
alias Pleroma.Upload.Filter
@uploads %Pleroma.Upload{
name: "image_with_imagedescription_and_caption-abstract.jpg",
content_type: "image/jpeg",
path: Path.absname("test/fixtures/image_with_imagedescription_and_caption-abstract.jpg"),
tempfile: Path.absname("test/fixtures/image_with_imagedescription_and_caption-abstract.jpg"),
description: nil
}
test "keeps description when not empty" do
uploads = %Pleroma.Upload{
name: "image_with_imagedescription_and_caption-abstract.jpg",
content_type: "image/jpeg",
path: Path.absname("test/fixtures/image_with_imagedescription_and_caption-abstract.jpg"),
tempfile:
Path.absname("test/fixtures/image_with_imagedescription_and_caption-abstract.jpg"),
description: "Some description"
}
assert Filter.Exiftool.ReadDescription.filter(uploads) ==
{:ok, :noop}
end
test "otherwise returns ImageDescription when present" do
uploads_after = %Pleroma.Upload{
name: "image_with_imagedescription_and_caption-abstract.jpg",
content_type: "image/jpeg",
path: Path.absname("test/fixtures/image_with_imagedescription_and_caption-abstract.jpg"),
tempfile:
Path.absname("test/fixtures/image_with_imagedescription_and_caption-abstract.jpg"),
description: "a descriptive white pixel"
}
assert Filter.Exiftool.ReadDescription.filter(@uploads) ==
{:ok, :filtered, uploads_after}
end
test "Ignores warnings" do
uploads = %Pleroma.Upload{
name: "image_with_imagedescription_and_caption-abstract_and_stray_data_after.png",
content_type: "image/png",
path:
Path.absname(
"test/fixtures/image_with_imagedescription_and_caption-abstract_and_stray_data_after.png"
),
tempfile:
Path.absname(
"test/fixtures/image_with_imagedescription_and_caption-abstract_and_stray_data_after.png"
)
}
assert {:ok, :filtered, %{description: "a descriptive white pixel"}} =
Filter.Exiftool.ReadDescription.filter(uploads)
uploads = %Pleroma.Upload{
name: "image_with_stray_data_after.png",
content_type: "image/png",
path: Path.absname("test/fixtures/image_with_stray_data_after.png"),
tempfile: Path.absname("test/fixtures/image_with_stray_data_after.png")
}
assert {:ok, :filtered, %{description: nil}} = Filter.Exiftool.ReadDescription.filter(uploads)
end
test "otherwise returns iptc:Caption-Abstract when present" do
upload = %Pleroma.Upload{
name: "image_with_caption-abstract.jpg",
content_type: "image/jpeg",
path: Path.absname("test/fixtures/image_with_caption-abstract.jpg"),
tempfile: Path.absname("test/fixtures/image_with_caption-abstract.jpg"),
description: nil
}
upload_after = %Pleroma.Upload{
name: "image_with_caption-abstract.jpg",
content_type: "image/jpeg",
path: Path.absname("test/fixtures/image_with_caption-abstract.jpg"),
tempfile: Path.absname("test/fixtures/image_with_caption-abstract.jpg"),
description: "an abstract white pixel"
}
assert Filter.Exiftool.ReadDescription.filter(upload) ==
{:ok, :filtered, upload_after}
end
test "otherwise returns nil" do
uploads = %Pleroma.Upload{
name: "image_with_no_description.jpg",
content_type: "image/jpeg",
path: Path.absname("test/fixtures/image_with_no_description.jpg"),
tempfile: Path.absname("test/fixtures/image_with_no_description.jpg"),
description: nil
}
assert Filter.Exiftool.ReadDescription.filter(uploads) ==
{:ok, :filtered, uploads}
end
test "Return nil when image description from EXIF data exceeds the maximum length" do
clear_config([:instance, :description_limit], 5)
assert Filter.Exiftool.ReadDescription.filter(@uploads) ==
{:ok, :filtered, @uploads}
end
test "Ignores content with only whitespace" do
uploads = %Pleroma.Upload{
name: "non-existant.jpg",
content_type: "image/jpeg",
path:
Path.absname(
"test/fixtures/image_with_imagedescription_and_caption-abstract_whitespaces.jpg"
),
tempfile:
Path.absname(
"test/fixtures/image_with_imagedescription_and_caption-abstract_whitespaces.jpg"
),
description: nil
}
assert Filter.Exiftool.ReadDescription.filter(uploads) ==
{:ok, :filtered, uploads}
end
test "Return nil when image description from EXIF data can't be read" do
uploads = %Pleroma.Upload{
name: "non-existant.jpg",
content_type: "image/jpeg",
path: Path.absname("test/fixtures/non-existant.jpg"),
tempfile: Path.absname("test/fixtures/non-existant_tmp.jpg"),
description: nil
}
assert Filter.Exiftool.ReadDescription.filter(uploads) ==
{:ok, :filtered, uploads}
end
end

View file

@ -2,7 +2,7 @@
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Upload.Filter.ExiftoolTest do
defmodule Pleroma.Upload.Filter.Exiftool.StripLocationTest do
use Pleroma.DataCase, async: true
alias Pleroma.Upload.Filter
@ -21,7 +21,7 @@ defmodule Pleroma.Upload.Filter.ExiftoolTest do
tempfile: Path.absname("test/fixtures/DSCN0010_tmp.jpg")
}
assert Filter.Exiftool.filter(upload) == {:ok, :filtered}
assert Filter.Exiftool.StripLocation.filter(upload) == {:ok, :filtered}
{exif_original, 0} = System.cmd("exiftool", ["test/fixtures/DSCN0010.jpg"])
{exif_filtered, 0} = System.cmd("exiftool", ["test/fixtures/DSCN0010_tmp.jpg"])
@ -31,12 +31,19 @@ defmodule Pleroma.Upload.Filter.ExiftoolTest do
refute String.match?(exif_filtered, ~r/GPS/)
end
test "verify webp files are skipped" do
upload = %Pleroma.Upload{
name: "sample.webp",
content_type: "image/webp"
}
test "verify webp, heic, svg files are skipped" do
uploads =
~w{webp heic svg svg+xml}
|> Enum.map(fn type ->
%Pleroma.Upload{
name: "sample.#{type}",
content_type: "image/#{type}"
}
end)
assert Filter.Exiftool.filter(upload) == {:ok, :noop}
uploads
|> Enum.each(fn upload ->
assert Filter.Exiftool.StripLocation.filter(upload) == {:ok, :noop}
end)
end
end

View file

@ -0,0 +1,32 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2023 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Upload.Filter.OnlyMediaTest do
use Pleroma.DataCase, async: true
alias Pleroma.Upload
alias Pleroma.Upload.Filter.OnlyMedia
test "Allows media Content-Type" do
["audio/mpeg", "image/jpeg", "video/mp4"]
|> Enum.each(fn type ->
upload = %Upload{
content_type: type
}
assert {:ok, :noop} = OnlyMedia.filter(upload)
end)
end
test "Disallows non-media Content-Type" do
["application/javascript", "application/pdf", "text/html"]
|> Enum.each(fn type ->
upload = %Upload{
content_type: type
}
assert {:error, _} = OnlyMedia.filter(upload)
end)
end
end

View file

@ -6,10 +6,19 @@ defmodule Pleroma.UploadTest do
use Pleroma.DataCase
import ExUnit.CaptureLog
import Mox
alias Pleroma.UnstubbedConfigMock, as: ConfigMock
alias Pleroma.Upload
alias Pleroma.Uploaders.Uploader
setup do
ConfigMock
|> stub_with(Pleroma.Test.StaticConfig)
:ok
end
@upload_file %Plug.Upload{
content_type: "image/jpeg",
path: Path.absname("test/fixtures/image_tmp.jpg"),
@ -49,20 +58,22 @@ defmodule Pleroma.UploadTest do
test "it returns file" do
File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg")
assert Upload.store(@upload_file) ==
{:ok,
%{
"name" => "image.jpg",
"type" => "Document",
"mediaType" => "image/jpeg",
"url" => [
%{
"href" => "http://localhost:4001/media/post-process-file.jpg",
"mediaType" => "image/jpeg",
"type" => "Link"
}
]
}}
assert {:ok, result} = Upload.store(@upload_file)
assert result ==
%{
"id" => result["id"],
"name" => "image.jpg",
"type" => "Document",
"mediaType" => "image/jpeg",
"url" => [
%{
"href" => "http://localhost:4001/media/post-process-file.jpg",
"mediaType" => "image/jpeg",
"type" => "Link"
}
]
}
Task.await(Agent.get(TestUploaderSuccess, fn task_pid -> task_pid end))
end
@ -234,6 +245,8 @@ defmodule Pleroma.UploadTest do
describe "Setting a custom base_url for uploaded media" do
setup do: clear_config([Pleroma.Upload, :base_url], "https://cache.pleroma.social")
# This seems to be backwards. Skipped for that reason
@tag skip: true
test "returns a media url with configured base_url" do
base_url = Pleroma.Config.get([Pleroma.Upload, :base_url])

View file

@ -3,22 +3,27 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Uploaders.S3Test do
use Pleroma.DataCase
use Pleroma.DataCase, async: true
alias Pleroma.UnstubbedConfigMock, as: ConfigMock
alias Pleroma.Uploaders.S3
alias Pleroma.Uploaders.S3.ExAwsMock
import Mock
import Mox
import ExUnit.CaptureLog
setup do
clear_config([Pleroma.Upload, :uploader], Pleroma.Uploaders.S3)
clear_config([Pleroma.Upload, :base_url], "https://s3.amazonaws.com")
clear_config([Pleroma.Uploaders.S3])
clear_config([Pleroma.Uploaders.S3, :bucket], "test_bucket")
end
describe "get_file/1" do
test "it returns path to local folder for files" do
test "it returns url for files" do
ConfigMock
|> expect(:get, 6, fn key ->
[
{Pleroma.Upload,
[uploader: Pleroma.Uploaders.S3, base_url: "https://s3.amazonaws.com"]},
{Pleroma.Uploaders.S3, [bucket: "test_bucket"]}
]
|> get_in(key)
end)
assert S3.get_file("test_image.jpg") == {
:ok,
{:url, "https://s3.amazonaws.com/test_bucket/test_image.jpg"}
@ -26,13 +31,16 @@ defmodule Pleroma.Uploaders.S3Test do
end
test "it returns path without bucket when truncated_namespace set to ''" do
clear_config([Pleroma.Uploaders.S3],
bucket: "test_bucket",
bucket_namespace: "myaccount",
truncated_namespace: ""
)
clear_config([Pleroma.Upload, :base_url], "https://s3.amazonaws.com")
ConfigMock
|> expect(:get, 6, fn key ->
[
{Pleroma.Upload,
[uploader: Pleroma.Uploaders.S3, base_url: "https://s3.amazonaws.com"]},
{Pleroma.Uploaders.S3,
[bucket: "test_bucket", truncated_namespace: "", bucket_namespace: "myaccount"]}
]
|> get_in(key)
end)
assert S3.get_file("test_image.jpg") == {
:ok,
@ -41,10 +49,15 @@ defmodule Pleroma.Uploaders.S3Test do
end
test "it returns path with bucket namespace when namespace is set" do
clear_config([Pleroma.Uploaders.S3],
bucket: "test_bucket",
bucket_namespace: "family"
)
ConfigMock
|> expect(:get, 6, fn key ->
[
{Pleroma.Upload,
[uploader: Pleroma.Uploaders.S3, base_url: "https://s3.amazonaws.com"]},
{Pleroma.Uploaders.S3, [bucket: "test_bucket", bucket_namespace: "family"]}
]
|> get_in(key)
end)
assert S3.get_file("test_image.jpg") == {
:ok,
@ -62,28 +75,42 @@ defmodule Pleroma.Uploaders.S3Test do
tempfile: Path.absname("test/instance_static/add/shortcode.png")
}
ConfigMock
|> expect(:get, fn [Pleroma.Uploaders.S3] ->
[
bucket: "test_bucket"
]
end)
[file_upload: file_upload]
end
test "save file", %{file_upload: file_upload} do
with_mock ExAws, request: fn _ -> {:ok, :ok} end do
assert S3.put_file(file_upload) == {:ok, {:file, "test_folder/image-tet.jpg"}}
end
ExAwsMock
|> expect(:request, fn _req -> {:ok, %{status_code: 200}} end)
assert S3.put_file(file_upload) == {:ok, {:file, "test_folder/image-tet.jpg"}}
end
test "returns error", %{file_upload: file_upload} do
with_mock ExAws, request: fn _ -> {:error, "S3 Upload failed"} end do
assert capture_log(fn ->
assert S3.put_file(file_upload) == {:error, "S3 Upload failed"}
end) =~ "Elixir.Pleroma.Uploaders.S3: {:error, \"S3 Upload failed\"}"
end
ExAwsMock
|> expect(:request, fn _req -> {:error, "S3 Upload failed"} end)
assert capture_log(fn ->
assert S3.put_file(file_upload) == {:error, "S3 Upload failed"}
end) =~ "Elixir.Pleroma.Uploaders.S3: {:error, \"S3 Upload failed\"}"
end
end
describe "delete_file/1" do
test_with_mock "deletes file", ExAws, request: fn _req -> {:ok, %{status_code: 204}} end do
test "deletes file" do
ExAwsMock
|> expect(:request, fn _req -> {:ok, %{status_code: 204}} end)
ConfigMock
|> expect(:get, fn [Pleroma.Uploaders.S3, :bucket] -> "test_bucket" end)
assert :ok = S3.delete_file("image.jpg")
assert_called(ExAws.request(:_))
end
end
end

View file

@ -0,0 +1,51 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2023 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.User.BackupAsyncTest do
use Pleroma.DataCase, async: true
import Pleroma.Factory
import Mox
alias Pleroma.UnstubbedConfigMock, as: ConfigMock
alias Pleroma.User.Backup
alias Pleroma.User.Backup.ProcessorMock
setup do
user = insert(:user, %{nickname: "cofe", name: "Cofe", ap_id: "http://cofe.io/users/cofe"})
{:ok, backup} = user |> Backup.new() |> Repo.insert()
%{backup: backup}
end
@tag capture_log: true
test "it handles unrecoverable exceptions", %{backup: backup} do
ProcessorMock
|> expect(:do_process, fn _, _ ->
raise "mock exception"
end)
ConfigMock
|> stub_with(Pleroma.Config)
{:error, %{backup: backup, reason: :exit}} = Backup.process(backup, ProcessorMock)
assert backup.state == :failed
end
@tag capture_log: true
test "it handles timeouts", %{backup: backup} do
ProcessorMock
|> expect(:do_process, fn _, _ ->
Process.sleep(:timer.seconds(4))
end)
ConfigMock
|> expect(:get, fn [Pleroma.User.Backup, :process_wait_time] -> :timer.seconds(2) end)
{:error, %{backup: backup, reason: :timeout}} = Backup.process(backup, ProcessorMock)
assert backup.state == :failed
end
end

View file

@ -9,10 +9,14 @@ defmodule Pleroma.User.BackupTest do
import Mock
import Pleroma.Factory
import Swoosh.TestAssertions
import Mox
alias Pleroma.Bookmark
alias Pleroma.Tests.ObanHelpers
alias Pleroma.UnstubbedConfigMock, as: ConfigMock
alias Pleroma.Uploaders.S3.ExAwsMock
alias Pleroma.User.Backup
alias Pleroma.User.Backup.ProcessorMock
alias Pleroma.Web.CommonAPI
alias Pleroma.Workers.BackupWorker
@ -20,17 +24,25 @@ defmodule Pleroma.User.BackupTest do
clear_config([Pleroma.Upload, :uploader])
clear_config([Backup, :limit_days])
clear_config([Pleroma.Emails.Mailer, :enabled], true)
ConfigMock
|> stub_with(Pleroma.Config)
ProcessorMock
|> stub_with(Pleroma.User.Backup.Processor)
:ok
end
test "it requries enabled email" do
test "it does not requrie enabled email" do
clear_config([Pleroma.Emails.Mailer, :enabled], false)
user = insert(:user)
assert {:error, "Backups require enabled email"} == Backup.create(user)
assert {:ok, _} = Backup.create(user)
end
test "it requries user's email" do
test "it does not require user's email" do
user = insert(:user, %{email: nil})
assert {:error, "Email is required"} == Backup.create(user)
assert {:ok, _} = Backup.create(user)
end
test "it creates a backup record and an Oban job" do
@ -39,7 +51,7 @@ defmodule Pleroma.User.BackupTest do
assert_enqueued(worker: BackupWorker, args: args)
backup = Backup.get(args["backup_id"])
assert %Backup{user_id: ^user_id, processed: false, file_size: 0} = backup
assert %Backup{user_id: ^user_id, processed: false, file_size: 0, state: :pending} = backup
end
test "it return an error if the export limit is over" do
@ -59,7 +71,7 @@ defmodule Pleroma.User.BackupTest do
assert {:ok, %Oban.Job{args: %{"backup_id" => backup_id} = args}} = Backup.create(user)
assert {:ok, backup} = perform_job(BackupWorker, args)
assert backup.file_size > 0
assert %Backup{id: ^backup_id, processed: true, user_id: ^user_id} = backup
assert %Backup{id: ^backup_id, processed: true, user_id: ^user_id, state: :complete} = backup
delete_job_args = %{"op" => "delete", "backup_id" => backup_id}
@ -75,6 +87,66 @@ defmodule Pleroma.User.BackupTest do
)
end
test "it updates states of the backup" do
clear_config([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)
%{id: user_id} = user = insert(:user)
assert {:ok, %Oban.Job{args: %{"backup_id" => backup_id} = args}} = Backup.create(user)
assert {:ok, backup} = perform_job(BackupWorker, args)
assert backup.file_size > 0
assert %Backup{id: ^backup_id, processed: true, user_id: ^user_id, state: :complete} = backup
delete_job_args = %{"op" => "delete", "backup_id" => backup_id}
assert_enqueued(worker: BackupWorker, args: delete_job_args)
assert {:ok, backup} = perform_job(BackupWorker, delete_job_args)
refute Backup.get(backup_id)
email = Pleroma.Emails.UserEmail.backup_is_ready_email(backup)
assert_email_sent(
to: {user.name, user.email},
html_body: email.html_body
)
end
test "it does not send an email if the user does not have an email" do
clear_config([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)
%{id: user_id} = user = insert(:user, %{email: nil})
assert {:ok, %Oban.Job{args: %{"backup_id" => backup_id} = args}} = Backup.create(user)
assert {:ok, backup} = perform_job(BackupWorker, args)
assert backup.file_size > 0
assert %Backup{id: ^backup_id, processed: true, user_id: ^user_id} = backup
assert_no_email_sent()
end
test "it does not send an email if mailer is not on" do
clear_config([Pleroma.Emails.Mailer, :enabled], false)
clear_config([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)
%{id: user_id} = user = insert(:user)
assert {:ok, %Oban.Job{args: %{"backup_id" => backup_id} = args}} = Backup.create(user)
assert {:ok, backup} = perform_job(BackupWorker, args)
assert backup.file_size > 0
assert %Backup{id: ^backup_id, processed: true, user_id: ^user_id} = backup
assert_no_email_sent()
end
test "it does not send an email if the user has an empty email" do
clear_config([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)
%{id: user_id} = user = insert(:user, %{email: ""})
assert {:ok, %Oban.Job{args: %{"backup_id" => backup_id} = args}} = Backup.create(user)
assert {:ok, backup} = perform_job(BackupWorker, args)
assert backup.file_size > 0
assert %Backup{id: ^backup_id, processed: true, user_id: ^user_id} = backup
assert_no_email_sent()
end
test "it removes outdated backups after creating a fresh one" do
clear_config([Backup, :limit_days], -1)
clear_config([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)
@ -111,7 +183,7 @@ defmodule Pleroma.User.BackupTest do
Bookmark.create(user.id, status3.id)
assert {:ok, backup} = user |> Backup.new() |> Repo.insert()
assert {:ok, path} = Backup.export(backup)
assert {:ok, path} = Backup.export(backup, self())
assert {:ok, zipfile} = :zip.zip_open(String.to_charlist(path), [:memory])
assert {:ok, {'actor.json', json}} = :zip.zip_get('actor.json', zipfile)
@ -193,6 +265,55 @@ defmodule Pleroma.User.BackupTest do
File.rm!(path)
end
test "it counts the correct number processed" do
user = insert(:user, %{nickname: "cofe", name: "Cofe", ap_id: "http://cofe.io/users/cofe"})
Enum.map(1..120, fn i ->
{:ok, status} = CommonAPI.post(user, %{status: "status #{i}"})
CommonAPI.favorite(user, status.id)
Bookmark.create(user.id, status.id)
end)
assert {:ok, backup} = user |> Backup.new() |> Repo.insert()
{:ok, backup} = Backup.process(backup)
assert backup.processed_number == 1 + 120 + 120 + 120
Backup.delete(backup)
end
test "it handles errors" do
user = insert(:user, %{nickname: "cofe", name: "Cofe", ap_id: "http://cofe.io/users/cofe"})
Enum.map(1..120, fn i ->
{:ok, _status} = CommonAPI.post(user, %{status: "status #{i}"})
end)
assert {:ok, backup} = user |> Backup.new() |> Repo.insert()
with_mock Pleroma.Web.ActivityPub.Transmogrifier,
[:passthrough],
prepare_outgoing: fn data ->
object =
data["object"]
|> Pleroma.Object.normalize(fetch: false)
|> Map.get(:data)
data = data |> Map.put("object", object)
if String.contains?(data["object"]["content"], "119"),
do: raise(%Postgrex.Error{}),
else: {:ok, data}
end do
{:ok, backup} = Backup.process(backup)
assert backup.processed
assert backup.state == :complete
assert backup.processed_number == 1 + 119
Backup.delete(backup)
end
end
describe "it uploads and deletes a backup archive" do
setup do
clear_config([Pleroma.Upload, :base_url], "https://s3.amazonaws.com")
@ -209,7 +330,7 @@ defmodule Pleroma.User.BackupTest do
Bookmark.create(user.id, status3.id)
assert {:ok, backup} = user |> Backup.new() |> Repo.insert()
assert {:ok, path} = Backup.export(backup)
assert {:ok, path} = Backup.export(backup, self())
[path: path, backup: backup]
end
@ -218,14 +339,14 @@ defmodule Pleroma.User.BackupTest do
clear_config([Pleroma.Upload, :uploader], Pleroma.Uploaders.S3)
clear_config([Pleroma.Uploaders.S3, :streaming_enabled], false)
with_mock ExAws,
request: fn
%{http_method: :put} -> {:ok, :ok}
%{http_method: :delete} -> {:ok, %{status_code: 204}}
end do
assert {:ok, %Pleroma.Upload{}} = Backup.upload(backup, path)
assert {:ok, _backup} = Backup.delete(backup)
end
ExAwsMock
|> expect(:request, 2, fn
%{http_method: :put} -> {:ok, :ok}
%{http_method: :delete} -> {:ok, %{status_code: 204}}
end)
assert {:ok, %Pleroma.Upload{}} = Backup.upload(backup, path)
assert {:ok, _backup} = Backup.delete(backup)
end
test "Local", %{path: path, backup: backup} do

View file

@ -3,7 +3,6 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.User.ImportTest do
alias Pleroma.Repo
alias Pleroma.Tests.ObanHelpers
alias Pleroma.User

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.User.QueryTest do
use Pleroma.DataCase, async: true
use Pleroma.DataCase, async: false
alias Pleroma.Repo
alias Pleroma.User
@ -44,4 +44,63 @@ defmodule Pleroma.User.QueryTest do
|> User.Query.build()
|> Repo.all()
end
describe "is_privileged param" do
setup do
%{
user: insert(:user, local: true, is_admin: false, is_moderator: false),
moderator_user: insert(:user, local: true, is_admin: false, is_moderator: true),
admin_user: insert(:user, local: true, is_admin: true, is_moderator: false),
admin_moderator_user: insert(:user, local: true, is_admin: true, is_moderator: true),
remote_user: insert(:user, local: false, is_admin: true, is_moderator: true),
non_active_user:
insert(:user, local: true, is_admin: true, is_moderator: true, is_active: false)
}
end
test "doesn't return any users when there are no privileged roles" do
clear_config([:instance, :admin_privileges], [])
clear_config([:instance, :moderator_privileges], [])
assert [] = User.Query.build(%{is_privileged: :cofe}) |> Repo.all()
end
test "returns moderator users if they are privileged", %{
moderator_user: moderator_user,
admin_moderator_user: admin_moderator_user
} do
clear_config([:instance, :admin_privileges], [])
clear_config([:instance, :moderator_privileges], [:cofe])
assert [_, _] = User.Query.build(%{is_privileged: :cofe}) |> Repo.all()
assert moderator_user in (User.Query.build(%{is_privileged: :cofe}) |> Repo.all())
assert admin_moderator_user in (User.Query.build(%{is_privileged: :cofe}) |> Repo.all())
end
test "returns admin users if they are privileged", %{
admin_user: admin_user,
admin_moderator_user: admin_moderator_user
} do
clear_config([:instance, :admin_privileges], [:cofe])
clear_config([:instance, :moderator_privileges], [])
assert [_, _] = User.Query.build(%{is_privileged: :cofe}) |> Repo.all()
assert admin_user in (User.Query.build(%{is_privileged: :cofe}) |> Repo.all())
assert admin_moderator_user in (User.Query.build(%{is_privileged: :cofe}) |> Repo.all())
end
test "returns admin and moderator users if they are both privileged", %{
moderator_user: moderator_user,
admin_user: admin_user,
admin_moderator_user: admin_moderator_user
} do
clear_config([:instance, :admin_privileges], [:cofe])
clear_config([:instance, :moderator_privileges], [:cofe])
assert [_, _, _] = User.Query.build(%{is_privileged: :cofe}) |> Repo.all()
assert admin_user in (User.Query.build(%{is_privileged: :cofe}) |> Repo.all())
assert moderator_user in (User.Query.build(%{is_privileged: :cofe}) |> Repo.all())
assert admin_moderator_user in (User.Query.build(%{is_privileged: :cofe}) |> Repo.all())
end
end
end

View file

@ -5,8 +5,9 @@
defmodule Pleroma.UserRelationshipTest do
alias Pleroma.UserRelationship
use Pleroma.DataCase, async: true
use Pleroma.DataCase, async: false
import Mock
import Pleroma.Factory
describe "*_exists?/2" do
@ -79,7 +80,12 @@ defmodule Pleroma.UserRelationshipTest do
end
test "if record already exists, returns it", %{users: [user1, user2]} do
user_block = UserRelationship.create_block(user1, user2)
user_block =
with_mock NaiveDateTime, [:passthrough], utc_now: fn -> ~N[2017-03-17 17:09:58] end do
{:ok, %{inserted_at: ~N[2017-03-17 17:09:58]}} =
UserRelationship.create_block(user1, user2)
end
assert user_block == UserRelationship.create_block(user1, user2)
end
end

View file

@ -3,7 +3,6 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.UserSearchTest do
alias Pleroma.Repo
alias Pleroma.User
use Pleroma.DataCase
@ -65,6 +64,14 @@ defmodule Pleroma.UserSearchTest do
assert found_user.id == user.id
end
test "excludes deactivated users from results" do
user = insert(:user, %{nickname: "john t1000"})
insert(:user, %{is_active: false, nickname: "john t800"})
[found_user] = User.search("john")
assert found_user.id == user.id
end
# Note: as in Mastodon, `is_discoverable` doesn't anyhow relate to user searchability
test "includes non-discoverable users in results" do
insert(:user, %{nickname: "john 3000", is_discoverable: false})

View file

@ -5,7 +5,6 @@
defmodule Pleroma.UserTest do
alias Pleroma.Activity
alias Pleroma.Builders.UserBuilder
alias Pleroma.Notification
alias Pleroma.Object
alias Pleroma.Repo
alias Pleroma.Tests.ObanHelpers
@ -13,13 +12,18 @@ defmodule Pleroma.UserTest do
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.CommonAPI
use Pleroma.DataCase
use Pleroma.DataCase, async: false
use Oban.Testing, repo: Pleroma.Repo
import Pleroma.Factory
import ExUnit.CaptureLog
import Swoosh.TestAssertions
setup do
Mox.stub_with(Pleroma.UnstubbedConfigMock, Pleroma.Config)
:ok
end
setup_all do
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
:ok
@ -311,7 +315,7 @@ defmodule Pleroma.UserTest do
describe "unfollow/2" do
setup do: clear_config([:instance, :external_user_synchronization])
test "unfollow with syncronizes external user" do
test "unfollow with synchronizes external user" do
clear_config([:instance, :external_user_synchronization], true)
followed =
@ -473,12 +477,7 @@ defmodule Pleroma.UserTest do
reject_deletes: []
)
setup do:
clear_config(:mrf,
policies: [
Pleroma.Web.ActivityPub.MRF.SimplePolicy
]
)
setup do: clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.SimplePolicy])
test "it sends a welcome chat message when Simple policy applied to local instance" do
clear_config([:mrf_simple, :media_nsfw], [{"localhost", ""}])
@ -591,6 +590,21 @@ defmodule Pleroma.UserTest do
refute_email_sent()
end
test "it works when the registering user does not provide an email" do
clear_config([Pleroma.Emails.Mailer, :enabled], false)
clear_config([:instance, :account_activation_required], false)
clear_config([:instance, :account_approval_required], true)
cng = User.register_changeset(%User{}, @full_user_data |> Map.put(:email, ""))
# The user is still created
assert {:ok, %User{nickname: "nick"}} = User.register(cng)
# No emails are sent
ObanHelpers.perform_all()
refute_email_sent()
end
test "it requires an email, name, nickname and password, bio is optional when account_activation_required is enabled" do
clear_config([:instance, :account_activation_required], true)
@ -618,9 +632,10 @@ defmodule Pleroma.UserTest do
end
test "it restricts certain nicknames" do
clear_config([User, :restricted_nicknames], ["about"])
[restricted_name | _] = Pleroma.Config.get([User, :restricted_nicknames])
assert is_bitstring(restricted_name)
assert is_binary(restricted_name)
params =
@full_user_data
@ -631,6 +646,23 @@ defmodule Pleroma.UserTest do
refute changeset.valid?
end
test "it is case-insensitive when restricting nicknames" do
clear_config([User, :restricted_nicknames], ["about"])
[restricted_name | _] = Pleroma.Config.get([User, :restricted_nicknames])
assert is_binary(restricted_name)
restricted_upcase_name = String.upcase(restricted_name)
params =
@full_user_data
|> Map.put(:nickname, restricted_upcase_name)
changeset = User.register_changeset(%User{}, params)
refute changeset.valid?
end
test "it blocks blacklisted email domains" do
clear_config([User, :email_blacklist], ["trolling.world"])
@ -639,6 +671,11 @@ defmodule Pleroma.UserTest do
changeset = User.register_changeset(%User{}, params)
refute changeset.valid?
# Block with case-insensitive match
params = Map.put(@full_user_data, :email, "troll@TrOlLing.wOrld")
changeset = User.register_changeset(%User{}, params)
refute changeset.valid?
# Block with subdomain match
params = Map.put(@full_user_data, :email, "troll@gnomes.trolling.world")
changeset = User.register_changeset(%User{}, params)
@ -654,14 +691,14 @@ defmodule Pleroma.UserTest do
assert changeset.valid?
end
test "it sets the password_hash and ap_id" do
test "it sets the password_hash, ap_id, private key and followers collection address" do
changeset = User.register_changeset(%User{}, @full_user_data)
assert changeset.valid?
assert is_binary(changeset.changes[:password_hash])
assert is_binary(changeset.changes[:keys])
assert changeset.changes[:ap_id] == User.ap_id(%User{nickname: @full_user_data.nickname})
assert changeset.changes.follower_address == "#{changeset.changes.ap_id}/followers"
end
@ -827,6 +864,123 @@ defmodule Pleroma.UserTest do
freshed_user = refresh_record(user)
assert freshed_user == fetched_user
end
test "gets an existing user by nickname starting with http" do
user = insert(:user, nickname: "httpssome")
{:ok, fetched_user} = User.get_or_fetch("httpssome")
assert user == fetched_user
end
end
describe "get_or_fetch/1 remote users with tld, while BE is runned on subdomain" do
setup do: clear_config([Pleroma.Web.WebFinger, :update_nickname_on_user_fetch], true)
test "for mastodon" do
Tesla.Mock.mock(fn
%{url: "https://example.com/.well-known/host-meta"} ->
%Tesla.Env{
status: 302,
headers: [{"location", "https://sub.example.com/.well-known/host-meta"}]
}
%{url: "https://sub.example.com/.well-known/host-meta"} ->
%Tesla.Env{
status: 200,
body:
"test/fixtures/webfinger/masto-host-meta.xml"
|> File.read!()
|> String.replace("{{domain}}", "sub.example.com")
}
%{url: "https://sub.example.com/.well-known/webfinger?resource=acct:a@example.com"} ->
%Tesla.Env{
status: 200,
body:
"test/fixtures/webfinger/masto-webfinger.json"
|> File.read!()
|> String.replace("{{nickname}}", "a")
|> String.replace("{{domain}}", "example.com")
|> String.replace("{{subdomain}}", "sub.example.com"),
headers: [{"content-type", "application/jrd+json"}]
}
%{url: "https://sub.example.com/users/a"} ->
%Tesla.Env{
status: 200,
body:
"test/fixtures/webfinger/masto-user.json"
|> File.read!()
|> String.replace("{{nickname}}", "a")
|> String.replace("{{domain}}", "sub.example.com"),
headers: [{"content-type", "application/activity+json"}]
}
%{url: "https://sub.example.com/users/a/collections/featured"} ->
%Tesla.Env{
status: 200,
body:
File.read!("test/fixtures/users_mock/masto_featured.json")
|> String.replace("{{domain}}", "sub.example.com")
|> String.replace("{{nickname}}", "a"),
headers: [{"content-type", "application/activity+json"}]
}
end)
ap_id = "a@example.com"
{:ok, fetched_user} = User.get_or_fetch(ap_id)
assert fetched_user.ap_id == "https://sub.example.com/users/a"
assert fetched_user.nickname == "a@example.com"
end
test "for pleroma" do
Tesla.Mock.mock(fn
%{url: "https://example.com/.well-known/host-meta"} ->
%Tesla.Env{
status: 302,
headers: [{"location", "https://sub.example.com/.well-known/host-meta"}]
}
%{url: "https://sub.example.com/.well-known/host-meta"} ->
%Tesla.Env{
status: 200,
body:
"test/fixtures/webfinger/pleroma-host-meta.xml"
|> File.read!()
|> String.replace("{{domain}}", "sub.example.com")
}
%{url: "https://sub.example.com/.well-known/webfinger?resource=acct:a@example.com"} ->
%Tesla.Env{
status: 200,
body:
"test/fixtures/webfinger/pleroma-webfinger.json"
|> File.read!()
|> String.replace("{{nickname}}", "a")
|> String.replace("{{domain}}", "example.com")
|> String.replace("{{subdomain}}", "sub.example.com"),
headers: [{"content-type", "application/jrd+json"}]
}
%{url: "https://sub.example.com/users/a"} ->
%Tesla.Env{
status: 200,
body:
"test/fixtures/webfinger/pleroma-user.json"
|> File.read!()
|> String.replace("{{nickname}}", "a")
|> String.replace("{{domain}}", "sub.example.com"),
headers: [{"content-type", "application/activity+json"}]
}
end)
ap_id = "a@example.com"
{:ok, fetched_user} = User.get_or_fetch(ap_id)
assert fetched_user.ap_id == "https://sub.example.com/users/a"
assert fetched_user.nickname == "a@example.com"
end
end
describe "fetching a user from nickname or trying to build one" do
@ -1123,7 +1277,7 @@ defmodule Pleroma.UserTest do
user = insert(:user)
muted_user = insert(:user)
{:ok, _user_relationships} = User.mute(user, muted_user, %{expires_in: 60})
{:ok, _user_relationships} = User.mute(user, muted_user, %{duration: 60})
assert User.mutes?(user, muted_user)
worker = Pleroma.Workers.MuteExpireWorker
@ -1695,7 +1849,6 @@ defmodule Pleroma.UserTest do
confirmation_token: "qqqq",
domain_blocks: ["lain.com"],
is_active: false,
ap_enabled: true,
is_moderator: true,
is_admin: true,
mascot: %{"a" => "b"},
@ -1736,7 +1889,6 @@ defmodule Pleroma.UserTest do
confirmation_token: nil,
domain_blocks: [],
is_active: false,
ap_enabled: false,
is_moderator: false,
is_admin: false,
mascot: nil,
@ -1799,8 +1951,8 @@ defmodule Pleroma.UserTest do
end
end
test "get_public_key_for_ap_id fetches a user that's not in the db" do
assert {:ok, _key} = User.get_public_key_for_ap_id("http://mastodon.example.org/users/admin")
test "get_public_key_for_ap_id returns correctly for user that's not in the db" do
assert :error = User.get_public_key_for_ap_id("http://mastodon.example.org/users/admin")
end
describe "per-user rich-text filtering" do
@ -1883,31 +2035,82 @@ defmodule Pleroma.UserTest do
end
end
describe "superuser?/1" do
describe "privileged?/1" do
setup do
clear_config([:instance, :admin_privileges], [:cofe, :suya])
clear_config([:instance, :moderator_privileges], [:cofe, :suya])
end
test "returns false for unprivileged users" do
user = insert(:user, local: true)
refute User.superuser?(user)
refute User.privileged?(user, :cofe)
end
test "returns false for remote users" do
user = insert(:user, local: false)
remote_admin_user = insert(:user, local: false, is_admin: true)
refute User.superuser?(user)
refute User.superuser?(remote_admin_user)
refute User.privileged?(user, :cofe)
refute User.privileged?(remote_admin_user, :cofe)
end
test "returns true for local moderators" do
test "returns true for local moderators if, and only if, they are privileged" do
user = insert(:user, local: true, is_moderator: true)
assert User.superuser?(user)
assert User.privileged?(user, :cofe)
clear_config([:instance, :moderator_privileges], [])
refute User.privileged?(user, :cofe)
end
test "returns true for local admins" do
test "returns true for local admins if, and only if, they are privileged" do
user = insert(:user, local: true, is_admin: true)
assert User.superuser?(user)
assert User.privileged?(user, :cofe)
clear_config([:instance, :admin_privileges], [])
refute User.privileged?(user, :cofe)
end
end
describe "privileges/1" do
setup do
clear_config([:instance, :moderator_privileges], [:cofe, :only_moderator])
clear_config([:instance, :admin_privileges], [:cofe, :only_admin])
end
test "returns empty list for users without roles" do
user = insert(:user, local: true)
assert [] == User.privileges(user)
end
test "returns list of privileges for moderators" do
moderator = insert(:user, is_moderator: true, local: true)
assert [:cofe, :only_moderator] == User.privileges(moderator) |> Enum.sort()
end
test "returns list of privileges for admins" do
admin = insert(:user, is_admin: true, local: true)
assert [:cofe, :only_admin] == User.privileges(admin) |> Enum.sort()
end
test "returns list of unique privileges for users who are both moderator and admin" do
moderator_admin = insert(:user, is_moderator: true, is_admin: true, local: true)
assert [:cofe, :only_admin, :only_moderator] ==
User.privileges(moderator_admin) |> Enum.sort()
end
test "returns empty list for remote users" do
remote_moderator_admin = insert(:user, is_moderator: true, is_admin: true, local: false)
assert [] == User.privileges(remote_moderator_admin)
end
end
@ -1950,13 +2153,77 @@ defmodule Pleroma.UserTest do
assert User.visible_for(user, other_user) == :visible
end
test "returns true when the account is unconfirmed and being viewed by a privileged account (confirmation required)" do
test "returns true when the account is unconfirmed and being viewed by a privileged account (privilege :users_manage_activation_state, confirmation required)" do
clear_config([:instance, :account_activation_required], true)
clear_config([:instance, :admin_privileges], [:users_manage_activation_state])
user = insert(:user, local: true, is_confirmed: false)
other_user = insert(:user, local: true, is_admin: true)
assert User.visible_for(user, other_user) == :visible
clear_config([:instance, :admin_privileges], [])
refute User.visible_for(user, other_user) == :visible
end
end
describe "all_users_with_privilege/1" do
setup do
%{
user: insert(:user, local: true, is_admin: false, is_moderator: false),
moderator_user: insert(:user, local: true, is_admin: false, is_moderator: true),
admin_user: insert(:user, local: true, is_admin: true, is_moderator: false),
admin_moderator_user: insert(:user, local: true, is_admin: true, is_moderator: true),
remote_user: insert(:user, local: false, is_admin: true, is_moderator: true),
non_active_user:
insert(:user, local: true, is_admin: true, is_moderator: true, is_active: false)
}
end
test "doesn't return any users when there are no privileged roles" do
clear_config([:instance, :admin_privileges], [])
clear_config([:instance, :moderator_privileges], [])
assert [] = User.Query.build(%{is_privileged: :cofe}) |> Repo.all()
end
test "returns moderator users if they are privileged", %{
moderator_user: moderator_user,
admin_moderator_user: admin_moderator_user
} do
clear_config([:instance, :admin_privileges], [])
clear_config([:instance, :moderator_privileges], [:cofe])
assert [_, _] = User.Query.build(%{is_privileged: :cofe}) |> Repo.all()
assert moderator_user in User.all_users_with_privilege(:cofe)
assert admin_moderator_user in User.all_users_with_privilege(:cofe)
end
test "returns admin users if they are privileged", %{
admin_user: admin_user,
admin_moderator_user: admin_moderator_user
} do
clear_config([:instance, :admin_privileges], [:cofe])
clear_config([:instance, :moderator_privileges], [])
assert [_, _] = User.Query.build(%{is_privileged: :cofe}) |> Repo.all()
assert admin_user in User.all_users_with_privilege(:cofe)
assert admin_moderator_user in User.all_users_with_privilege(:cofe)
end
test "returns admin and moderator users if they are both privileged", %{
moderator_user: moderator_user,
admin_user: admin_user,
admin_moderator_user: admin_moderator_user
} do
clear_config([:instance, :admin_privileges], [:cofe])
clear_config([:instance, :moderator_privileges], [:cofe])
assert [_, _, _] = User.Query.build(%{is_privileged: :cofe}) |> Repo.all()
assert admin_user in User.all_users_with_privilege(:cofe)
assert moderator_user in User.all_users_with_privilege(:cofe)
assert admin_moderator_user in User.all_users_with_privilege(:cofe)
end
end
@ -2108,21 +2375,6 @@ defmodule Pleroma.UserTest do
end
end
describe "ensure_keys_present" do
test "it creates keys for a user and stores them in info" do
user = insert(:user)
refute is_binary(user.keys)
{:ok, user} = User.ensure_keys_present(user)
assert is_binary(user.keys)
end
test "it doesn't create keys if there already are some" do
user = insert(:user, keys: "xxx")
{:ok, user} = User.ensure_keys_present(user)
assert user.keys == "xxx"
end
end
describe "get_ap_ids_by_nicknames" do
test "it returns a list of AP ids for a given set of nicknames" do
user = insert(:user)
@ -2211,26 +2463,6 @@ defmodule Pleroma.UserTest do
assert {:ok, user} = Cachex.get(:user_cache, "ap_id:#{user.ap_id}")
assert %User{bio: "test-bio"} = User.get_cached_by_ap_id(user.ap_id)
end
test "removes report notifs when user isn't superuser any more" do
report_activity = insert(:report_activity)
user = insert(:user, is_moderator: true, is_admin: true)
{:ok, _} = Notification.create_notifications(report_activity)
assert [%Pleroma.Notification{type: "pleroma:report"}] = Notification.for_user(user)
{:ok, user} = user |> User.admin_api_update(%{is_moderator: false})
# is still superuser because still admin
assert [%Pleroma.Notification{type: "pleroma:report"}] = Notification.for_user(user)
{:ok, user} = user |> User.admin_api_update(%{is_moderator: true, is_admin: false})
# is still superuser because still moderator
assert [%Pleroma.Notification{type: "pleroma:report"}] = Notification.for_user(user)
{:ok, user} = user |> User.admin_api_update(%{is_moderator: false})
# is not a superuser any more
assert [] = Notification.for_user(user)
end
end
describe "following/followers synchronization" do
@ -2244,8 +2476,7 @@ defmodule Pleroma.UserTest do
insert(:user,
local: false,
follower_address: "http://localhost:4001/users/masto_closed/followers",
following_address: "http://localhost:4001/users/masto_closed/following",
ap_enabled: true
following_address: "http://localhost:4001/users/masto_closed/following"
)
assert other_user.following_count == 0
@ -2257,7 +2488,7 @@ defmodule Pleroma.UserTest do
assert other_user.follower_count == 1
end
test "syncronizes the counters with the remote instance for the followed when enabled" do
test "synchronizes the counters with the remote instance for the followed when enabled" do
clear_config([:instance, :external_user_synchronization], false)
user = insert(:user)
@ -2266,8 +2497,7 @@ defmodule Pleroma.UserTest do
insert(:user,
local: false,
follower_address: "http://localhost:4001/users/masto_closed/followers",
following_address: "http://localhost:4001/users/masto_closed/following",
ap_enabled: true
following_address: "http://localhost:4001/users/masto_closed/following"
)
assert other_user.following_count == 0
@ -2279,7 +2509,7 @@ defmodule Pleroma.UserTest do
assert other_user.follower_count == 437
end
test "syncronizes the counters with the remote instance for the follower when enabled" do
test "synchronizes the counters with the remote instance for the follower when enabled" do
clear_config([:instance, :external_user_synchronization], false)
user = insert(:user)
@ -2288,8 +2518,7 @@ defmodule Pleroma.UserTest do
insert(:user,
local: false,
follower_address: "http://localhost:4001/users/masto_closed/followers",
following_address: "http://localhost:4001/users/masto_closed/following",
ap_enabled: true
following_address: "http://localhost:4001/users/masto_closed/following"
)
assert other_user.following_count == 0
@ -2579,6 +2808,82 @@ defmodule Pleroma.UserTest do
object_id
end
describe "add_alias/2" do
test "should add alias for another user" do
user = insert(:user)
user2 = insert(:user)
assert {:ok, user_updated} = user |> User.add_alias(user2)
assert user_updated.also_known_as |> length() == 1
assert user2.ap_id in user_updated.also_known_as
end
test "should add multiple aliases" do
user = insert(:user)
user2 = insert(:user)
user3 = insert(:user)
assert {:ok, user} = user |> User.add_alias(user2)
assert {:ok, user_updated} = user |> User.add_alias(user3)
assert user_updated.also_known_as |> length() == 2
assert user2.ap_id in user_updated.also_known_as
assert user3.ap_id in user_updated.also_known_as
end
test "should not add duplicate aliases" do
user = insert(:user)
user2 = insert(:user)
assert {:ok, user} = user |> User.add_alias(user2)
assert {:ok, user_updated} = user |> User.add_alias(user2)
assert user_updated.also_known_as |> length() == 1
assert user2.ap_id in user_updated.also_known_as
end
end
describe "alias_users/1" do
test "should get aliases for a user" do
user = insert(:user)
user2 = insert(:user, also_known_as: [user.ap_id])
aliases = user2 |> User.alias_users()
assert aliases |> length() == 1
alias_user = aliases |> Enum.at(0)
assert alias_user.ap_id == user.ap_id
end
end
describe "delete_alias/2" do
test "should delete existing alias" do
user = insert(:user)
user2 = insert(:user, also_known_as: [user.ap_id])
assert {:ok, user_updated} = user2 |> User.delete_alias(user)
assert user_updated.also_known_as == []
end
test "should report error on non-existing alias" do
user = insert(:user)
user2 = insert(:user)
user3 = insert(:user, also_known_as: [user.ap_id])
assert {:error, :no_such_alias} = user3 |> User.delete_alias(user2)
user3_updated = User.get_cached_by_ap_id(user3.ap_id)
assert user3_updated.also_known_as |> length() == 1
assert user.ap_id in user3_updated.also_known_as
end
end
describe "account endorsements" do
test "it pins people" do
user = insert(:user)

View file

@ -25,6 +25,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
require Pleroma.Constants
setup do
Mox.stub_with(Pleroma.UnstubbedConfigMock, Pleroma.Config)
:ok
end
setup_all do
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
:ok
@ -247,6 +252,27 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
assert json_response(response, 200) == ObjectView.render("object.json", %{object: object})
end
test "does not return local-only objects for remote users", %{conn: conn} do
user = insert(:user)
reader = insert(:user, local: false)
{:ok, post} =
CommonAPI.post(user, %{status: "test @#{reader.nickname}", visibility: "local"})
assert Pleroma.Web.ActivityPub.Visibility.is_local_public?(post)
object = Object.normalize(post, fetch: false)
uuid = String.split(object.data["id"], "/") |> List.last()
assert response =
conn
|> assign(:user, reader)
|> put_req_header("accept", "application/activity+json")
|> get("/objects/#{uuid}")
json_response(response, 404)
end
test "it returns a json representation of the object with accept application/json", %{
conn: conn
} do
@ -554,7 +580,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
user =
insert(:user,
ap_id: "https://mastodon.example.org/users/raymoo",
ap_enabled: true,
local: false,
last_refreshed_at: nil
)
@ -870,6 +895,23 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
assert Activity.get_by_ap_id(data["id"])
end
test "it rejects an invalid incoming activity", %{conn: conn, data: data} do
user = insert(:user, is_active: false)
data =
data
|> Map.put("bcc", [user.ap_id])
|> Kernel.put_in(["object", "bcc"], [user.ap_id])
conn =
conn
|> assign(:valid_signature, true)
|> put_req_header("content-type", "application/activity+json")
|> post("/users/#{user.nickname}/inbox", data)
assert "Invalid request." == json_response(conn, 400)
end
test "it accepts messages with to as string instead of array", %{conn: conn, data: data} do
user = insert(:user)
@ -1297,6 +1339,35 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
assert outbox_endpoint == result["id"]
end
test "it returns a local note activity when authenticated as local user", %{conn: conn} do
user = insert(:user)
reader = insert(:user)
{:ok, note_activity} = CommonAPI.post(user, %{status: "mew mew", visibility: "local"})
ap_id = note_activity.data["id"]
resp =
conn
|> assign(:user, reader)
|> put_req_header("accept", "application/activity+json")
|> get("/users/#{user.nickname}/outbox?page=true")
|> json_response(200)
assert %{"orderedItems" => [%{"id" => ^ap_id}]} = resp
end
test "it does not return a local note activity when unauthenticated", %{conn: conn} do
user = insert(:user)
{:ok, _note_activity} = CommonAPI.post(user, %{status: "mew mew", visibility: "local"})
resp =
conn
|> put_req_header("accept", "application/activity+json")
|> get("/users/#{user.nickname}/outbox?page=true")
|> json_response(200)
assert %{"orderedItems" => []} = resp
end
test "it returns a note activity in a collection", %{conn: conn} do
note_activity = insert(:note_activity)
note_object = Object.normalize(note_activity, fetch: false)

View file

@ -11,6 +11,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
alias Pleroma.Config
alias Pleroma.Notification
alias Pleroma.Object
alias Pleroma.UnstubbedConfigMock, as: ConfigMock
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Utils
@ -19,11 +20,16 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
import ExUnit.CaptureLog
import Mock
import Mox
import Pleroma.Factory
import Tesla.Mock
setup do
mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
ConfigMock
|> stub_with(Pleroma.Test.StaticConfig)
:ok
end
@ -174,7 +180,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
{:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
assert user.ap_id == user_id
assert user.nickname == "admin@mastodon.example.org"
assert user.ap_enabled
assert user.follower_address == "http://mastodon.example.org/users/admin/followers"
end
@ -554,7 +559,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
assert activity.data["ok"] == data["ok"]
assert activity.data["id"] == given_id
assert activity.data["context"] == "blabla"
assert activity.data["context_id"]
end
test "adds a context when none is there" do
@ -576,8 +580,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
assert is_binary(activity.data["context"])
assert is_binary(object.data["context"])
assert activity.data["context_id"]
assert object.data["context_id"]
end
test "adds an id to a given object if it lacks one and is a note and inserts it to the object database" do
@ -774,6 +776,34 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
assert %{data: _data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
assert object.data["repliesCount"] == 2
end
test "increates quotes count", %{user: user} do
user2 = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{status: "1", visibility: "public"})
ap_id = activity.data["id"]
quote_data = %{status: "1", quote_id: activity.id}
# public
{:ok, _} = CommonAPI.post(user2, Map.put(quote_data, :visibility, "public"))
assert %{data: _data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
assert object.data["quotesCount"] == 1
# unlisted
{:ok, _} = CommonAPI.post(user2, Map.put(quote_data, :visibility, "unlisted"))
assert %{data: _data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
assert object.data["quotesCount"] == 2
# private
{:ok, _} = CommonAPI.post(user2, Map.put(quote_data, :visibility, "private"))
assert %{data: _data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
assert object.data["quotesCount"] == 2
# direct
{:ok, _} = CommonAPI.post(user2, Map.put(quote_data, :visibility, "direct"))
assert %{data: _data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
assert object.data["quotesCount"] == 2
end
end
describe "fetch activities for recipients" do
@ -1345,6 +1375,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
%{test_file: test_file}
end
test "strips / from filename", %{test_file: file} do
file = %Plug.Upload{file | filename: "../../../../../nested/bad.jpg"}
{:ok, %Object{} = object} = ActivityPub.upload(file)
[%{"href" => href}] = object.data["url"]
assert Regex.match?(~r"/bad.jpg$", href)
refute Regex.match?(~r"/nested/", href)
end
test "sets a description if given", %{test_file: file} do
{:ok, %Object{} = object} = ActivityPub.upload(file, description: "a cool file")
assert object.data["name"] == "a cool file"
@ -1507,6 +1545,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
reporter_ap_id = reporter.ap_id
target_ap_id = target_account.ap_id
activity_ap_id = activity.data["id"]
object_ap_id = activity.object.data["id"]
activity_with_object = Activity.get_by_ap_id_with_object(activity_ap_id)
@ -1518,6 +1557,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
reported_activity: activity,
content: content,
activity_ap_id: activity_ap_id,
object_ap_id: object_ap_id,
activity_with_object: activity_with_object,
reporter_ap_id: reporter_ap_id,
target_ap_id: target_ap_id
@ -1531,7 +1571,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
target_account: target_account,
reported_activity: reported_activity,
content: content,
activity_ap_id: activity_ap_id,
object_ap_id: object_ap_id,
activity_with_object: activity_with_object,
reporter_ap_id: reporter_ap_id,
target_ap_id: target_ap_id
@ -1547,7 +1587,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
note_obj = %{
"type" => "Note",
"id" => activity_ap_id,
"id" => object_ap_id,
"content" => content,
"published" => activity_with_object.object.data["published"],
"actor" =>
@ -1571,6 +1611,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
context: context,
target_account: target_account,
reported_activity: reported_activity,
object_ap_id: object_ap_id,
content: content
},
Utils,
@ -1585,8 +1626,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
content: content
})
new_data =
put_in(activity.data, ["object"], [target_account.ap_id, reported_activity.data["id"]])
new_data = put_in(activity.data, ["object"], [target_account.ap_id, object_ap_id])
assert_called(Utils.maybe_federate(%{activity | data: new_data}))
end
@ -1612,7 +1652,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
})
assert Repo.aggregate(Activity, :count, :id) == 1
assert Repo.aggregate(Object, :count, :id) == 2
assert Repo.aggregate(Object, :count, :id) == 1
assert Repo.aggregate(Notification, :count, :id) == 0
end
end
@ -1665,7 +1705,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
end
describe "fetch_follow_information_for_user" do
test "syncronizes following/followers counters" do
test "synchronizes following/followers counters" do
user =
insert(:user,
local: false,
@ -1836,9 +1876,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
"target" => ^new_ap_id,
"type" => "Move"
},
local: true
local: true,
recipients: recipients
} = activity
assert old_user.follower_address in recipients
params = %{
"op" => "move_following",
"origin_id" => old_user.id,
@ -1869,6 +1912,42 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
assert {:error, "Target account must have the origin in `alsoKnownAs`"} =
ActivityPub.move(old_user, new_user)
end
test "do not move remote user following relationships" do
%{ap_id: old_ap_id} = old_user = insert(:user)
%{ap_id: new_ap_id} = new_user = insert(:user, also_known_as: [old_ap_id])
follower_remote = insert(:user, local: false)
User.follow(follower_remote, old_user)
assert User.following?(follower_remote, old_user)
assert {:ok, activity} = ActivityPub.move(old_user, new_user)
assert %Activity{
actor: ^old_ap_id,
data: %{
"actor" => ^old_ap_id,
"object" => ^old_ap_id,
"target" => ^new_ap_id,
"type" => "Move"
},
local: true
} = activity
params = %{
"op" => "move_following",
"origin_id" => old_user.id,
"target_id" => new_user.id
}
assert_enqueued(worker: Pleroma.Workers.BackgroundWorker, args: params)
Pleroma.Workers.BackgroundWorker.perform(%Oban.Job{args: params})
assert User.following?(follower_remote, old_user)
refute User.following?(follower_remote, new_user)
end
end
test "doesn't retrieve replies activities with exclude_replies" do
@ -2607,4 +2686,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
{:ok, user} = ActivityPub.make_user_from_ap_id("https://princess.cat/users/mewmew")
assert user.name == " "
end
@tag capture_log: true
test "pin_data_from_featured_collection will ignore unsupported values" do
assert %{} ==
ActivityPub.pin_data_from_featured_collection(%{
"type" => "OrderedCollection",
"first" => "https://social.example/users/alice/collections/featured?page=true"
})
end
end

View file

@ -44,5 +44,34 @@ defmodule Pleroma.Web.ActivityPub.BuilderTest do
assert {:ok, ^expected, []} = Builder.note(draft)
end
test "quote post" do
user = insert(:user)
note = insert(:note)
draft = %ActivityDraft{
user: user,
context: "2hu",
content_html: "<h1>This is :moominmamma: note</h1>",
quote_post: note,
extra: %{}
}
expected = %{
"actor" => user.ap_id,
"attachment" => [],
"content" => "<h1>This is :moominmamma: note</h1>",
"context" => "2hu",
"sensitive" => false,
"type" => "Note",
"quoteUrl" => note.data["id"],
"cc" => [],
"summary" => nil,
"tag" => [],
"to" => []
}
assert {:ok, ^expected, []} = Builder.note(draft)
end
end
end

View file

@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicyTest do
import Pleroma.Factory
import ExUnit.CaptureLog
alias Pleroma.Web.ActivityPub.MRF
alias Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy
@linkless_message %{
@ -49,11 +50,23 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicyTest do
assert user.note_count == 0
message =
@linkful_message
|> Map.put("actor", user.ap_id)
message = %{
"type" => "Create",
"actor" => user.ap_id,
"object" => %{
"formerRepresentations" => %{
"type" => "OrderedCollection",
"orderedItems" => [
%{
"content" => "<a href='https://example.com'>hi world!</a>"
}
]
},
"content" => "mew"
}
}
{:reject, _} = AntiLinkSpamPolicy.filter(message)
{:reject, _} = MRF.filter_one(AntiLinkSpamPolicy, message)
end
test "it allows posts with links for local users" do
@ -67,6 +80,18 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicyTest do
{:ok, _message} = AntiLinkSpamPolicy.filter(message)
end
test "it disallows posts with links in history" do
user = insert(:user, local: false)
assert user.note_count == 0
message =
@linkful_message
|> Map.put("actor", user.ap_id)
{:reject, _} = AntiLinkSpamPolicy.filter(message)
end
end
describe "with old user" do

View file

@ -0,0 +1,425 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2023 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.EmojiPolicyTest do
use Pleroma.DataCase
require Pleroma.Constants
alias Pleroma.Web.ActivityPub.MRF
alias Pleroma.Web.ActivityPub.MRF.EmojiPolicy
setup do: clear_config(:mrf_emoji)
setup do
clear_config([:mrf_emoji], %{
remove_url: [],
remove_shortcode: [],
federated_timeline_removal_url: [],
federated_timeline_removal_shortcode: []
})
end
@emoji_tags [
%{
"icon" => %{
"type" => "Image",
"url" => "https://example.org/emoji/biribiri/mikoto_smile2.png"
},
"id" => "https://example.org/emoji/biribiri/mikoto_smile2.png",
"name" => ":mikoto_smile2:",
"type" => "Emoji",
"updated" => "1970-01-01T00:00:00Z"
},
%{
"icon" => %{
"type" => "Image",
"url" => "https://example.org/emoji/biribiri/mikoto_smile3.png"
},
"id" => "https://example.org/emoji/biribiri/mikoto_smile3.png",
"name" => ":mikoto_smile3:",
"type" => "Emoji",
"updated" => "1970-01-01T00:00:00Z"
},
%{
"icon" => %{
"type" => "Image",
"url" => "https://example.org/emoji/nekomimi_girl_emoji/nekomimi_girl_emoji_007.png"
},
"id" => "https://example.org/emoji/nekomimi_girl_emoji/nekomimi_girl_emoji_007.png",
"name" => ":nekomimi_girl_emoji_007:",
"type" => "Emoji",
"updated" => "1970-01-01T00:00:00Z"
},
%{
"icon" => %{
"type" => "Image",
"url" => "https://example.org/test.png"
},
"id" => "https://example.org/test.png",
"name" => ":test:",
"type" => "Emoji",
"updated" => "1970-01-01T00:00:00Z"
}
]
@misc_tags [%{"type" => "Placeholder"}]
@user_data %{
"type" => "Person",
"id" => "https://example.org/placeholder",
"name" => "lol",
"tag" => @emoji_tags ++ @misc_tags
}
@status_data %{
"type" => "Create",
"object" => %{
"type" => "Note",
"id" => "https://example.org/placeholder",
"content" => "lol",
"tag" => @emoji_tags ++ @misc_tags,
"emoji" => %{
"mikoto_smile2" => "https://example.org/emoji/biribiri/mikoto_smile2.png",
"mikoto_smile3" => "https://example.org/emoji/biribiri/mikoto_smile3.png",
"nekomimi_girl_emoji_007" =>
"https://example.org/emoji/nekomimi_girl_emoji/nekomimi_girl_emoji_007.png",
"test" => "https://example.org/test.png"
},
"to" => ["https://example.org/self", Pleroma.Constants.as_public()],
"cc" => ["https://example.org/someone"]
},
"to" => ["https://example.org/self", Pleroma.Constants.as_public()],
"cc" => ["https://example.org/someone"]
}
@status_data_with_history %{
"type" => "Create",
"object" =>
@status_data["object"]
|> Map.merge(%{
"formerRepresentations" => %{
"type" => "OrderedCollection",
"orderedItems" => [@status_data["object"] |> Map.put("content", "older")],
"totalItems" => 1
}
}),
"to" => ["https://example.org/self", Pleroma.Constants.as_public()],
"cc" => ["https://example.org/someone"]
}
@emoji_react_data %{
"type" => "EmojiReact",
"tag" => [@emoji_tags |> Enum.at(3)],
"object" => "https://example.org/someobject",
"to" => ["https://example.org/self"],
"cc" => ["https://example.org/someone"]
}
@emoji_react_data_matching_regex %{
"type" => "EmojiReact",
"tag" => [@emoji_tags |> Enum.at(1)],
"object" => "https://example.org/someobject",
"to" => ["https://example.org/self"],
"cc" => ["https://example.org/someone"]
}
@emoji_react_data_matching_nothing %{
"type" => "EmojiReact",
"tag" => [@emoji_tags |> Enum.at(2)],
"object" => "https://example.org/someobject",
"to" => ["https://example.org/self"],
"cc" => ["https://example.org/someone"]
}
@emoji_react_data_unicode %{
"type" => "EmojiReact",
"content" => "😍",
"object" => "https://example.org/someobject",
"to" => ["https://example.org/self"],
"cc" => ["https://example.org/someone"]
}
describe "remove_url" do
setup do
clear_config([:mrf_emoji, :remove_url], [
"https://example.org/test.png",
~r{/biribiri/mikoto_smile[23]\.png},
"nekomimi_girl_emoji"
])
:ok
end
test "processes user" do
{:ok, filtered} = MRF.filter_one(EmojiPolicy, @user_data)
expected_tags = [@emoji_tags |> Enum.at(2)] ++ @misc_tags
assert %{"tag" => ^expected_tags} = filtered
end
test "processes status" do
{:ok, filtered} = MRF.filter_one(EmojiPolicy, @status_data)
expected_tags = [@emoji_tags |> Enum.at(2)] ++ @misc_tags
expected_emoji = %{
"nekomimi_girl_emoji_007" =>
"https://example.org/emoji/nekomimi_girl_emoji/nekomimi_girl_emoji_007.png"
}
assert %{"object" => %{"tag" => ^expected_tags, "emoji" => ^expected_emoji}} = filtered
end
test "processes status with history" do
{:ok, filtered} = MRF.filter_one(EmojiPolicy, @status_data_with_history)
expected_tags = [@emoji_tags |> Enum.at(2)] ++ @misc_tags
expected_emoji = %{
"nekomimi_girl_emoji_007" =>
"https://example.org/emoji/nekomimi_girl_emoji/nekomimi_girl_emoji_007.png"
}
assert %{
"object" => %{
"tag" => ^expected_tags,
"emoji" => ^expected_emoji,
"formerRepresentations" => %{"orderedItems" => [item]}
}
} = filtered
assert %{"tag" => ^expected_tags, "emoji" => ^expected_emoji} = item
end
test "processes updates" do
{:ok, filtered} =
MRF.filter_one(EmojiPolicy, @status_data_with_history |> Map.put("type", "Update"))
expected_tags = [@emoji_tags |> Enum.at(2)] ++ @misc_tags
expected_emoji = %{
"nekomimi_girl_emoji_007" =>
"https://example.org/emoji/nekomimi_girl_emoji/nekomimi_girl_emoji_007.png"
}
assert %{
"object" => %{
"tag" => ^expected_tags,
"emoji" => ^expected_emoji,
"formerRepresentations" => %{"orderedItems" => [item]}
}
} = filtered
assert %{"tag" => ^expected_tags, "emoji" => ^expected_emoji} = item
end
test "processes EmojiReact" do
assert {:reject, "[EmojiPolicy] Rejected for having disallowed emoji"} ==
MRF.filter_one(EmojiPolicy, @emoji_react_data)
assert {:reject, "[EmojiPolicy] Rejected for having disallowed emoji"} ==
MRF.filter_one(EmojiPolicy, @emoji_react_data_matching_regex)
assert {:ok, @emoji_react_data_matching_nothing} ==
MRF.filter_one(EmojiPolicy, @emoji_react_data_matching_nothing)
assert {:ok, @emoji_react_data_unicode} ==
MRF.filter_one(EmojiPolicy, @emoji_react_data_unicode)
end
end
describe "remove_shortcode" do
setup do
clear_config([:mrf_emoji, :remove_shortcode], [
"test",
~r{mikoto_s},
"nekomimi_girl_emoji"
])
:ok
end
test "processes user" do
{:ok, filtered} = MRF.filter_one(EmojiPolicy, @user_data)
expected_tags = [@emoji_tags |> Enum.at(2)] ++ @misc_tags
assert %{"tag" => ^expected_tags} = filtered
end
test "processes status" do
{:ok, filtered} = MRF.filter_one(EmojiPolicy, @status_data)
expected_tags = [@emoji_tags |> Enum.at(2)] ++ @misc_tags
expected_emoji = %{
"nekomimi_girl_emoji_007" =>
"https://example.org/emoji/nekomimi_girl_emoji/nekomimi_girl_emoji_007.png"
}
assert %{"object" => %{"tag" => ^expected_tags, "emoji" => ^expected_emoji}} = filtered
end
test "processes status with history" do
{:ok, filtered} = MRF.filter_one(EmojiPolicy, @status_data_with_history)
expected_tags = [@emoji_tags |> Enum.at(2)] ++ @misc_tags
expected_emoji = %{
"nekomimi_girl_emoji_007" =>
"https://example.org/emoji/nekomimi_girl_emoji/nekomimi_girl_emoji_007.png"
}
assert %{
"object" => %{
"tag" => ^expected_tags,
"emoji" => ^expected_emoji,
"formerRepresentations" => %{"orderedItems" => [item]}
}
} = filtered
assert %{"tag" => ^expected_tags, "emoji" => ^expected_emoji} = item
end
test "processes updates" do
{:ok, filtered} =
MRF.filter_one(EmojiPolicy, @status_data_with_history |> Map.put("type", "Update"))
expected_tags = [@emoji_tags |> Enum.at(2)] ++ @misc_tags
expected_emoji = %{
"nekomimi_girl_emoji_007" =>
"https://example.org/emoji/nekomimi_girl_emoji/nekomimi_girl_emoji_007.png"
}
assert %{
"object" => %{
"tag" => ^expected_tags,
"emoji" => ^expected_emoji,
"formerRepresentations" => %{"orderedItems" => [item]}
}
} = filtered
assert %{"tag" => ^expected_tags, "emoji" => ^expected_emoji} = item
end
test "processes EmojiReact" do
assert {:reject, "[EmojiPolicy] Rejected for having disallowed emoji"} ==
MRF.filter_one(EmojiPolicy, @emoji_react_data)
assert {:reject, "[EmojiPolicy] Rejected for having disallowed emoji"} ==
MRF.filter_one(EmojiPolicy, @emoji_react_data_matching_regex)
assert {:ok, @emoji_react_data_matching_nothing} ==
MRF.filter_one(EmojiPolicy, @emoji_react_data_matching_nothing)
assert {:ok, @emoji_react_data_unicode} ==
MRF.filter_one(EmojiPolicy, @emoji_react_data_unicode)
end
end
describe "federated_timeline_removal_url" do
setup do
clear_config([:mrf_emoji, :federated_timeline_removal_url], [
"https://example.org/test.png",
~r{/biribiri/mikoto_smile[23]\.png},
"nekomimi_girl_emoji"
])
:ok
end
test "processes status" do
{:ok, filtered} = MRF.filter_one(EmojiPolicy, @status_data)
expected_tags = @status_data["object"]["tag"]
expected_emoji = @status_data["object"]["emoji"]
expected_to = ["https://example.org/self"]
expected_cc = [Pleroma.Constants.as_public(), "https://example.org/someone"]
assert %{
"to" => ^expected_to,
"cc" => ^expected_cc,
"object" => %{"tag" => ^expected_tags, "emoji" => ^expected_emoji}
} = filtered
end
test "ignore updates" do
{:ok, filtered} = MRF.filter_one(EmojiPolicy, @status_data |> Map.put("type", "Update"))
expected_tags = @status_data["object"]["tag"]
expected_emoji = @status_data["object"]["emoji"]
expected_to = ["https://example.org/self", Pleroma.Constants.as_public()]
expected_cc = ["https://example.org/someone"]
assert %{
"to" => ^expected_to,
"cc" => ^expected_cc,
"object" => %{"tag" => ^expected_tags, "emoji" => ^expected_emoji}
} = filtered
end
test "processes status with history" do
status =
@status_data_with_history
|> put_in(["object", "tag"], @misc_tags)
|> put_in(["object", "emoji"], %{})
{:ok, filtered} = MRF.filter_one(EmojiPolicy, status)
expected_tags = @status_data["object"]["tag"]
expected_emoji = @status_data["object"]["emoji"]
expected_to = ["https://example.org/self"]
expected_cc = [Pleroma.Constants.as_public(), "https://example.org/someone"]
assert %{
"to" => ^expected_to,
"cc" => ^expected_cc,
"object" => %{
"formerRepresentations" => %{
"orderedItems" => [%{"tag" => ^expected_tags, "emoji" => ^expected_emoji}]
}
}
} = filtered
end
end
describe "edge cases" do
setup do
clear_config([:mrf_emoji, :remove_url], [
"https://example.org/test.png",
~r{/biribiri/mikoto_smile[23]\.png},
"nekomimi_girl_emoji"
])
:ok
end
test "non-statuses" do
answer = @status_data |> put_in(["object", "type"], "Answer")
{:ok, filtered} = MRF.filter_one(EmojiPolicy, answer)
assert filtered == answer
end
test "without tag" do
status = @status_data |> Map.put("object", Map.drop(@status_data["object"], ["tag"]))
{:ok, filtered} = MRF.filter_one(EmojiPolicy, status)
refute Map.has_key?(filtered["object"], "tag")
end
test "without emoji" do
status = @status_data |> Map.put("object", Map.drop(@status_data["object"], ["emoji"]))
{:ok, filtered} = MRF.filter_one(EmojiPolicy, status)
refute Map.has_key?(filtered["object"], "emoji")
end
end
end

View file

@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrependedTest do
alias Pleroma.Activity
alias Pleroma.Object
alias Pleroma.Web.ActivityPub.MRF
alias Pleroma.Web.ActivityPub.MRF.EnsureRePrepended
describe "rewrites summary" do
@ -35,10 +36,58 @@ defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrependedTest do
assert {:ok, res} = EnsureRePrepended.filter(message)
assert res["object"]["summary"] == "re: object-summary"
end
test "it adds `re:` to history" do
message = %{
"type" => "Create",
"object" => %{
"summary" => "object-summary",
"inReplyTo" => %Activity{object: %Object{data: %{"summary" => "object-summary"}}},
"formerRepresentations" => %{
"orderedItems" => [
%{
"summary" => "object-summary",
"inReplyTo" => %Activity{object: %Object{data: %{"summary" => "object-summary"}}}
}
]
}
}
}
assert {:ok, res} = MRF.filter_one(EnsureRePrepended, message)
assert res["object"]["summary"] == "re: object-summary"
assert Enum.at(res["object"]["formerRepresentations"]["orderedItems"], 0)["summary"] ==
"re: object-summary"
end
test "it accepts Updates" do
message = %{
"type" => "Update",
"object" => %{
"summary" => "object-summary",
"inReplyTo" => %Activity{object: %Object{data: %{"summary" => "object-summary"}}},
"formerRepresentations" => %{
"orderedItems" => [
%{
"summary" => "object-summary",
"inReplyTo" => %Activity{object: %Object{data: %{"summary" => "object-summary"}}}
}
]
}
}
}
assert {:ok, res} = MRF.filter_one(EnsureRePrepended, message)
assert res["object"]["summary"] == "re: object-summary"
assert Enum.at(res["object"]["formerRepresentations"]["orderedItems"], 0)["summary"] ==
"re: object-summary"
end
end
describe "skip filter" do
test "it skip if type isn't 'Create'" do
test "it skip if type isn't 'Create' or 'Update'" do
message = %{
"type" => "Annotation",
"object" => %{"summary" => "object-summary"}

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.FollowBotPolicyTest do
use Pleroma.DataCase, async: true
use Pleroma.DataCase
alias Pleroma.User
alias Pleroma.Web.ActivityPub.MRF.FollowBotPolicy

View file

@ -8,6 +8,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.ForceMentionsInContentTest do
alias Pleroma.Constants
alias Pleroma.Object
alias Pleroma.Web.ActivityPub.MRF
alias Pleroma.Web.ActivityPub.MRF.ForceMentionsInContent
alias Pleroma.Web.CommonAPI
@ -161,4 +162,149 @@ defmodule Pleroma.Web.ActivityPub.MRF.ForceMentionsInContentTest do
assert filtered ==
"<p><span class=\"recipients-inline\"><span class=\"h-card\"><a class=\"u-url mention\" data-user=\"#{luigi.id}\" href=\"#{luigi.ap_id}\" rel=\"ugc\">@<span>luigi</span></a></span> </span>I'ma tired...</p>"
end
test "aware of history" do
mario = insert(:user, nickname: "mario")
wario = insert(:user, nickname: "wario")
{:ok, post1} = CommonAPI.post(mario, %{status: "Letsa go!"})
activity = %{
"type" => "Create",
"actor" => wario.ap_id,
"object" => %{
"type" => "Note",
"actor" => wario.ap_id,
"content" => "WHA-HA!",
"to" => [
mario.ap_id,
Constants.as_public()
],
"inReplyTo" => post1.object.data["id"],
"formerRepresentations" => %{
"orderedItems" => [
%{
"type" => "Note",
"actor" => wario.ap_id,
"content" => "WHA-HA!",
"to" => [
mario.ap_id,
Constants.as_public()
],
"inReplyTo" => post1.object.data["id"]
}
]
}
}
}
expected =
"<span class=\"recipients-inline\"><span class=\"h-card\"><a class=\"u-url mention\" data-user=\"#{mario.id}\" href=\"#{mario.ap_id}\" rel=\"ugc\">@<span>mario</span></a></span> </span>WHA-HA!"
assert {:ok,
%{
"object" => %{
"content" => ^expected,
"formerRepresentations" => %{"orderedItems" => [%{"content" => ^expected}]}
}
}} = MRF.filter_one(ForceMentionsInContent, activity)
end
test "works with Updates" do
mario = insert(:user, nickname: "mario")
wario = insert(:user, nickname: "wario")
{:ok, post1} = CommonAPI.post(mario, %{status: "Letsa go!"})
activity = %{
"type" => "Update",
"actor" => wario.ap_id,
"object" => %{
"type" => "Note",
"actor" => wario.ap_id,
"content" => "WHA-HA!",
"to" => [
mario.ap_id,
Constants.as_public()
],
"inReplyTo" => post1.object.data["id"],
"formerRepresentations" => %{
"orderedItems" => [
%{
"type" => "Note",
"actor" => wario.ap_id,
"content" => "WHA-HA!",
"to" => [
mario.ap_id,
Constants.as_public()
],
"inReplyTo" => post1.object.data["id"]
}
]
}
}
}
expected =
"<span class=\"recipients-inline\"><span class=\"h-card\"><a class=\"u-url mention\" data-user=\"#{mario.id}\" href=\"#{mario.ap_id}\" rel=\"ugc\">@<span>mario</span></a></span> </span>WHA-HA!"
assert {:ok,
%{
"object" => %{
"content" => ^expected,
"formerRepresentations" => %{"orderedItems" => [%{"content" => ^expected}]}
}
}} = MRF.filter_one(ForceMentionsInContent, activity)
end
test "don't add duplicate mentions for mastodon or misskey posts" do
[zero, rogerick, greg] = [
insert(:user,
ap_id: "https://pleroma.example.com/users/zero",
uri: "https://pleroma.example.com/users/zero",
nickname: "zero@pleroma.example.com",
local: false
),
insert(:user,
ap_id: "https://misskey.example.com/users/104ab42f11",
uri: "https://misskey.example.com/@rogerick",
nickname: "rogerick@misskey.example.com",
local: false
),
insert(:user,
ap_id: "https://mastodon.example.com/users/greg",
uri: "https://mastodon.example.com/@greg",
nickname: "greg@mastodon.example.com",
local: false
)
]
{:ok, post} = CommonAPI.post(rogerick, %{status: "eugh"})
inline_mentions = [
"<span class=\"h-card\"><a class=\"u-url mention\" data-user=\"#{rogerick.id}\" href=\"#{rogerick.ap_id}\" rel=\"ugc\">@<span>rogerick</span></a></span>",
"<span class=\"h-card\"><a class=\"u-url mention\" data-user=\"#{greg.id}\" href=\"#{greg.uri}\" rel=\"ugc\">@<span>greg</span></a></span>"
]
activity = %{
"type" => "Create",
"actor" => zero.ap_id,
"object" => %{
"type" => "Note",
"actor" => zero.ap_id,
"content" => "#{Enum.at(inline_mentions, 0)} #{Enum.at(inline_mentions, 1)} erm",
"to" => [
rogerick.ap_id,
greg.ap_id,
Constants.as_public()
],
"inReplyTo" => Object.normalize(post).data["id"]
}
}
{:ok, %{"object" => %{"content" => filtered}}} = ForceMentionsInContent.filter(activity)
assert filtered ==
"#{Enum.at(inline_mentions, 0)} #{Enum.at(inline_mentions, 1)} erm"
end
end

View file

@ -20,6 +20,76 @@ defmodule Pleroma.Web.ActivityPub.MRF.HashtagPolicyTest do
assert modified["object"]["sensitive"]
end
test "it is history-aware" do
activity = %{
"type" => "Create",
"object" => %{
"content" => "hey",
"tag" => []
}
}
activity_data =
activity
|> put_in(
["object", "formerRepresentations"],
%{
"type" => "OrderedCollection",
"orderedItems" => [
Map.put(
activity["object"],
"tag",
[%{"type" => "Hashtag", "name" => "#nsfw"}]
)
]
}
)
{:ok, modified} =
Pleroma.Web.ActivityPub.MRF.filter_one(
Pleroma.Web.ActivityPub.MRF.HashtagPolicy,
activity_data
)
refute modified["object"]["sensitive"]
assert Enum.at(modified["object"]["formerRepresentations"]["orderedItems"], 0)["sensitive"]
end
test "it works with Update" do
activity = %{
"type" => "Update",
"object" => %{
"content" => "hey",
"tag" => []
}
}
activity_data =
activity
|> put_in(
["object", "formerRepresentations"],
%{
"type" => "OrderedCollection",
"orderedItems" => [
Map.put(
activity["object"],
"tag",
[%{"type" => "Hashtag", "name" => "#nsfw"}]
)
]
}
)
{:ok, modified} =
Pleroma.Web.ActivityPub.MRF.filter_one(
Pleroma.Web.ActivityPub.MRF.HashtagPolicy,
activity_data
)
refute modified["object"]["sensitive"]
assert Enum.at(modified["object"]["formerRepresentations"]["orderedItems"], 0)["sensitive"]
end
test "it doesn't sets the sensitive property with irrelevant hashtags" do
user = insert(:user)

View file

@ -0,0 +1,112 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.InlineQuotePolicyTest do
alias Pleroma.Web.ActivityPub.MRF.InlineQuotePolicy
use Pleroma.DataCase
test "adds quote URL to post content" do
quote_url = "https://gleasonator.com/objects/1234"
activity = %{
"type" => "Create",
"actor" => "https://gleasonator.com/users/alex",
"object" => %{
"type" => "Note",
"content" => "Nice post",
"quoteUrl" => quote_url
}
}
{:ok, %{"object" => %{"content" => filtered}}} = InlineQuotePolicy.filter(activity)
assert filtered ==
"Nice post<span class=\"quote-inline\"><br/><br/><bdi>RT:</bdi> <a href=\"https://gleasonator.com/objects/1234\">https://gleasonator.com/objects/1234</a></span>"
end
test "adds quote URL to post content, custom template" do
clear_config([:mrf_inline_quote, :template], "{url}'s quoting")
quote_url = "https://gleasonator.com/objects/1234"
activity = %{
"type" => "Create",
"actor" => "https://gleasonator.com/users/alex",
"object" => %{
"type" => "Note",
"content" => "Nice post",
"quoteUrl" => quote_url
}
}
{:ok, %{"object" => %{"content" => filtered}}} = InlineQuotePolicy.filter(activity)
assert filtered ==
"Nice post<span class=\"quote-inline\"><br/><br/><a href=\"https://gleasonator.com/objects/1234\">https://gleasonator.com/objects/1234</a>'s quoting</span>"
end
test "doesn't add line breaks to markdown posts" do
quote_url = "https://gleasonator.com/objects/1234"
activity = %{
"type" => "Create",
"actor" => "https://gleasonator.com/users/alex",
"object" => %{
"type" => "Note",
"content" => "<p>Nice post</p>",
"quoteUrl" => quote_url
}
}
{:ok, %{"object" => %{"content" => filtered}}} = InlineQuotePolicy.filter(activity)
assert filtered ==
"<p>Nice post<span class=\"quote-inline\"><br/><br/><bdi>RT:</bdi> <a href=\"https://gleasonator.com/objects/1234\">https://gleasonator.com/objects/1234</a></span></p>"
end
test "ignores Misskey quote posts" do
object = File.read!("test/fixtures/quote_post/misskey_quote_post.json") |> Jason.decode!()
activity = %{
"type" => "Create",
"actor" => "https://misskey.io/users/7rkrarq81i",
"object" => object
}
{:ok, filtered} = InlineQuotePolicy.filter(activity)
assert filtered == activity
end
test "ignores Fedibird quote posts" do
object = File.read!("test/fixtures/quote_post/fedibird_quote_post.json") |> Jason.decode!()
# Normally the ObjectValidator will fix this before it reaches MRF
object = Map.put(object, "quoteUrl", object["quoteURL"])
activity = %{
"type" => "Create",
"actor" => "https://fedibird.com/users/noellabo",
"object" => object
}
{:ok, filtered} = InlineQuotePolicy.filter(activity)
assert filtered == activity
end
test "skips objects which already have an .inline-quote span" do
object =
File.read!("test/fixtures/quote_post/fedibird_quote_mismatched.json") |> Jason.decode!()
# Normally the ObjectValidator will fix this before it reaches MRF
object = Map.put(object, "quoteUrl", object["quoteUri"])
activity = %{
"type" => "Create",
"actor" => "https://fedibird.com/users/noellabo",
"object" => object
}
{:ok, filtered} = InlineQuotePolicy.filter(activity)
assert filtered == activity
end
end

View file

@ -79,6 +79,54 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicyTest do
KeywordPolicy.filter(message)
end)
end
test "rejects if string matches in history" do
clear_config([:mrf_keyword, :reject], ["pun"])
message = %{
"type" => "Create",
"object" => %{
"content" => "just a daily reminder that compLAINer is a good",
"summary" => "",
"formerRepresentations" => %{
"type" => "OrderedCollection",
"orderedItems" => [
%{
"content" => "just a daily reminder that compLAINer is a good pun",
"summary" => ""
}
]
}
}
}
assert {:reject, "[KeywordPolicy] Matches with rejected keyword"} =
KeywordPolicy.filter(message)
end
test "rejects Updates" do
clear_config([:mrf_keyword, :reject], ["pun"])
message = %{
"type" => "Update",
"object" => %{
"content" => "just a daily reminder that compLAINer is a good",
"summary" => "",
"formerRepresentations" => %{
"type" => "OrderedCollection",
"orderedItems" => [
%{
"content" => "just a daily reminder that compLAINer is a good pun",
"summary" => ""
}
]
}
}
}
assert {:reject, "[KeywordPolicy] Matches with rejected keyword"} =
KeywordPolicy.filter(message)
end
end
describe "delisting from ftl based on keywords" do
@ -157,6 +205,31 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicyTest do
not (["https://www.w3.org/ns/activitystreams#Public"] == result["to"])
end)
end
test "delists if string matches in history" do
clear_config([:mrf_keyword, :federated_timeline_removal], ["pun"])
message = %{
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
"type" => "Create",
"object" => %{
"content" => "just a daily reminder that compLAINer is a good",
"summary" => "",
"formerRepresentations" => %{
"orderedItems" => [
%{
"content" => "just a daily reminder that compLAINer is a good pun",
"summary" => ""
}
]
}
}
}
{:ok, result} = KeywordPolicy.filter(message)
assert ["https://www.w3.org/ns/activitystreams#Public"] == result["cc"]
refute ["https://www.w3.org/ns/activitystreams#Public"] == result["to"]
end
end
describe "replacing keywords" do
@ -221,5 +294,63 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicyTest do
result == "ZFS is free software"
end)
end
test "replaces keyword if string matches in history" do
clear_config([:mrf_keyword, :replace], [{"opensource", "free software"}])
message = %{
"type" => "Create",
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
"object" => %{
"content" => "ZFS is opensource",
"summary" => "",
"formerRepresentations" => %{
"type" => "OrderedCollection",
"orderedItems" => [
%{"content" => "ZFS is opensource mew mew", "summary" => ""}
]
}
}
}
{:ok,
%{
"object" => %{
"content" => "ZFS is free software",
"formerRepresentations" => %{
"orderedItems" => [%{"content" => "ZFS is free software mew mew"}]
}
}
}} = KeywordPolicy.filter(message)
end
test "replaces keyword in Updates" do
clear_config([:mrf_keyword, :replace], [{"opensource", "free software"}])
message = %{
"type" => "Update",
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
"object" => %{
"content" => "ZFS is opensource",
"summary" => "",
"formerRepresentations" => %{
"type" => "OrderedCollection",
"orderedItems" => [
%{"content" => "ZFS is opensource mew mew", "summary" => ""}
]
}
}
}
{:ok,
%{
"object" => %{
"content" => "ZFS is free software",
"formerRepresentations" => %{
"orderedItems" => [%{"content" => "ZFS is free software mew mew"}]
}
}
}} = KeywordPolicy.filter(message)
end
end
end

View file

@ -7,9 +7,12 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicyTest do
use Pleroma.Tests.Helpers
alias Pleroma.HTTP
alias Pleroma.UnstubbedConfigMock, as: ConfigMock
alias Pleroma.Web.ActivityPub.MRF
alias Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy
import Mock
import Mox
@message %{
"type" => "Create",
@ -22,6 +25,32 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicyTest do
}
}
@message_with_history %{
"type" => "Create",
"object" => %{
"type" => "Note",
"content" => "content",
"formerRepresentations" => %{
"orderedItems" => [
%{
"type" => "Note",
"content" => "content",
"attachment" => [
%{"url" => [%{"href" => "http://example.com/image.jpg"}]}
]
}
]
}
}
}
setup do
ConfigMock
|> stub_with(Pleroma.Test.StaticConfig)
:ok
end
setup do: clear_config([:media_proxy, :enabled], true)
test "it prefetches media proxy URIs" do
@ -50,4 +79,28 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicyTest do
refute called(HTTP.get(:_, :_, :_))
end
end
test "history-aware" do
Tesla.Mock.mock(fn %{method: :get, url: "http://example.com/image.jpg"} ->
{:ok, %Tesla.Env{status: 200, body: ""}}
end)
with_mock HTTP, get: fn _, _, _ -> {:ok, []} end do
MRF.filter_one(MediaProxyWarmingPolicy, @message_with_history)
assert called(HTTP.get(:_, :_, :_))
end
end
test "works with Updates" do
Tesla.Mock.mock(fn %{method: :get, url: "http://example.com/image.jpg"} ->
{:ok, %Tesla.Env{status: 200, body: ""}}
end)
with_mock HTTP, get: fn _, _, _ -> {:ok, []} end do
MRF.filter_one(MediaProxyWarmingPolicy, @message_with_history |> Map.put("type", "Update"))
assert called(HTTP.get(:_, :_, :_))
end
end
end

View file

@ -151,4 +151,27 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoEmptyPolicyTest do
assert NoEmptyPolicy.filter(message) == {:reject, "[NoEmptyPolicy]"}
end
test "works with Update" do
message = %{
"actor" => "http://localhost:4001/users/testuser",
"cc" => ["http://localhost:4001/users/testuser/followers"],
"object" => %{
"actor" => "http://localhost:4001/users/testuser",
"attachment" => [],
"cc" => ["http://localhost:4001/users/testuser/followers"],
"source" => "",
"to" => [
"https://www.w3.org/ns/activitystreams#Public"
],
"type" => "Note"
},
"to" => [
"https://www.w3.org/ns/activitystreams#Public"
],
"type" => "Update"
}
assert NoEmptyPolicy.filter(message) == {:reject, "[NoEmptyPolicy]"}
end
end

View file

@ -4,6 +4,7 @@
defmodule Pleroma.Web.ActivityPub.MRF.NoPlaceholderTextPolicyTest do
use Pleroma.DataCase, async: true
alias Pleroma.Web.ActivityPub.MRF
alias Pleroma.Web.ActivityPub.MRF.NoPlaceholderTextPolicy
test "it clears content object" do
@ -20,6 +21,46 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoPlaceholderTextPolicyTest do
assert res["object"]["content"] == ""
end
test "history-aware" do
message = %{
"type" => "Create",
"object" => %{
"content" => ".",
"attachment" => "image",
"formerRepresentations" => %{
"orderedItems" => [%{"content" => ".", "attachment" => "image"}]
}
}
}
assert {:ok, res} = MRF.filter_one(NoPlaceholderTextPolicy, message)
assert %{
"content" => "",
"formerRepresentations" => %{"orderedItems" => [%{"content" => ""}]}
} = res["object"]
end
test "works with Updates" do
message = %{
"type" => "Update",
"object" => %{
"content" => ".",
"attachment" => "image",
"formerRepresentations" => %{
"orderedItems" => [%{"content" => ".", "attachment" => "image"}]
}
}
}
assert {:ok, res} = MRF.filter_one(NoPlaceholderTextPolicy, message)
assert %{
"content" => "",
"formerRepresentations" => %{"orderedItems" => [%{"content" => ""}]}
} = res["object"]
end
@messages [
%{
"type" => "Create",

View file

@ -4,6 +4,7 @@
defmodule Pleroma.Web.ActivityPub.MRF.NormalizeMarkupTest do
use Pleroma.DataCase, async: true
alias Pleroma.Web.ActivityPub.MRF
alias Pleroma.Web.ActivityPub.MRF.NormalizeMarkup
@html_sample """
@ -16,24 +17,58 @@ defmodule Pleroma.Web.ActivityPub.MRF.NormalizeMarkupTest do
<script>alert('hacked')</script>
"""
test "it filter html tags" do
expected = """
<b>this is in bold</b>
<p>this is a paragraph</p>
this is a linebreak<br/>
this is a link with allowed &quot;rel&quot; attribute: <a href="http://example.com/" rel="tag">example.com</a>
this is a link with not allowed &quot;rel&quot; attribute: <a href="http://example.com/">example.com</a>
this is an image: <img src="http://example.com/image.jpg"/><br/>
alert(&#39;hacked&#39;)
"""
@expected """
<b>this is in bold</b>
<p>this is a paragraph</p>
this is a linebreak<br/>
this is a link with allowed &quot;rel&quot; attribute: <a href="http://example.com/" rel="tag">example.com</a>
this is a link with not allowed &quot;rel&quot; attribute: <a href="http://example.com/">example.com</a>
this is an image: <img src="http://example.com/image.jpg"/><br/>
alert(&#39;hacked&#39;)
"""
test "it filter html tags" do
message = %{"type" => "Create", "object" => %{"content" => @html_sample}}
assert {:ok, res} = NormalizeMarkup.filter(message)
assert res["object"]["content"] == expected
assert res["object"]["content"] == @expected
end
test "it skips filter if type isn't `Create`" do
test "history-aware" do
message = %{
"type" => "Create",
"object" => %{
"content" => @html_sample,
"formerRepresentations" => %{"orderedItems" => [%{"content" => @html_sample}]}
}
}
assert {:ok, res} = MRF.filter_one(NormalizeMarkup, message)
assert %{
"content" => @expected,
"formerRepresentations" => %{"orderedItems" => [%{"content" => @expected}]}
} = res["object"]
end
test "works with Updates" do
message = %{
"type" => "Update",
"object" => %{
"content" => @html_sample,
"formerRepresentations" => %{"orderedItems" => [%{"content" => @html_sample}]}
}
}
assert {:ok, res} = MRF.filter_one(NormalizeMarkup, message)
assert %{
"content" => @expected,
"formerRepresentations" => %{"orderedItems" => [%{"content" => @expected}]}
} = res["object"]
end
test "it skips filter if type isn't `Create` or `Update`" do
message = %{"type" => "Note", "object" => %{}}
assert {:ok, res} = NormalizeMarkup.filter(message)

View file

@ -0,0 +1,73 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2023 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.QuoteToLinkTagPolicyTest do
alias Pleroma.Web.ActivityPub.MRF.QuoteToLinkTagPolicy
use Pleroma.DataCase
require Pleroma.Constants
test "Add quote url to Link tag" do
quote_url = "https://gleasonator.com/objects/1234"
activity = %{
"type" => "Create",
"actor" => "https://gleasonator.com/users/alex",
"object" => %{
"type" => "Note",
"content" => "Nice post",
"quoteUrl" => quote_url
}
}
{:ok, %{"object" => object}} = QuoteToLinkTagPolicy.filter(activity)
assert object["tag"] == [
%{
"type" => "Link",
"href" => quote_url,
"mediaType" => Pleroma.Constants.activity_json_canonical_mime_type()
}
]
end
test "Add quote url to Link tag, append to the end" do
quote_url = "https://gleasonator.com/objects/1234"
activity = %{
"type" => "Create",
"actor" => "https://gleasonator.com/users/alex",
"object" => %{
"type" => "Note",
"content" => "Nice post",
"quoteUrl" => quote_url,
"tag" => [%{"type" => "Hashtag", "name" => "#foo"}]
}
}
{:ok, %{"object" => object}} = QuoteToLinkTagPolicy.filter(activity)
assert [_, tag] = object["tag"]
assert tag == %{
"type" => "Link",
"href" => quote_url,
"mediaType" => Pleroma.Constants.activity_json_canonical_mime_type()
}
end
test "Bypass posts without quoteUrl" do
activity = %{
"type" => "Create",
"actor" => "https://gleasonator.com/users/alex",
"object" => %{
"type" => "Note",
"content" => "Nice post"
}
}
assert {:ok, ^activity} = QuoteToLinkTagPolicy.filter(activity)
end
end

View file

@ -57,6 +57,16 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
assert SimplePolicy.filter(local_message) == {:ok, local_message}
end
test "works with Updates" do
clear_config([:mrf_simple, :media_removal], [{"remote.instance", "Some reason"}])
media_message = build_media_message(type: "Update")
assert SimplePolicy.filter(media_message) ==
{:ok,
media_message
|> Map.put("object", Map.delete(media_message["object"], "attachment"))}
end
end
describe "when :media_nsfw" do
@ -90,12 +100,20 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
assert SimplePolicy.filter(local_message) == {:ok, local_message}
end
test "works with Updates" do
clear_config([:mrf_simple, :media_nsfw], [{"remote.instance", "Whetever"}])
media_message = build_media_message(type: "Update")
assert SimplePolicy.filter(media_message) ==
{:ok, put_in(media_message, ["object", "sensitive"], true)}
end
end
defp build_media_message do
defp build_media_message(opts \\ []) do
%{
"actor" => "https://remote.instance/users/bob",
"type" => "Create",
"type" => opts[:type] || "Create",
"object" => %{
"attachment" => [%{}],
"tag" => ["foo"],

View file

@ -60,7 +60,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicyTest do
|> File.exists?()
end
test "reject shortcode", %{message: message} do
test "reject regex shortcode", %{message: message} do
refute "firedfox" in installed()
clear_config(:mrf_steal_emoji,
@ -74,6 +74,20 @@ defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicyTest do
refute "firedfox" in installed()
end
test "reject string shortcode", %{message: message} do
refute "firedfox" in installed()
clear_config(:mrf_steal_emoji,
hosts: ["example.org"],
size_limit: 284_468,
rejected_shortcodes: ["firedfox"]
)
assert {:ok, _message} = StealEmojiPolicy.filter(message)
refute "firedfox" in installed()
end
test "reject if size is above the limit", %{message: message} do
refute "firedfox" in installed()

View file

@ -99,6 +99,24 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicyTest do
assert TagPolicy.filter(message) == {:ok, except_message}
end
test "removes attachments in Updates" do
actor = insert(:user, tags: ["mrf_tag:media-strip"])
message = %{
"actor" => actor.ap_id,
"type" => "Update",
"object" => %{"attachment" => ["file1"]}
}
except_message = %{
"actor" => actor.ap_id,
"type" => "Update",
"object" => %{}
}
assert TagPolicy.filter(message) == {:ok, except_message}
end
end
describe "mrf_tag:media-force-nsfw" do
@ -119,5 +137,23 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicyTest do
assert TagPolicy.filter(message) == {:ok, except_message}
end
test "Mark as sensitive on presence of attachments in Updates" do
actor = insert(:user, tags: ["mrf_tag:media-force-nsfw"])
message = %{
"actor" => actor.ap_id,
"type" => "Update",
"object" => %{"tag" => ["test"], "attachment" => ["file1"]}
}
except_message = %{
"actor" => actor.ap_id,
"type" => "Update",
"object" => %{"tag" => ["test"], "attachment" => ["file1"], "sensitive" => true}
}
assert TagPolicy.filter(message) == {:ok, except_message}
end
end
end

View file

@ -0,0 +1,19 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2023 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.UtilsTest do
use Pleroma.DataCase, async: true
alias Pleroma.Web.ActivityPub.MRF.Utils
describe "describe_regex_or_string/1" do
test "describes regex" do
assert "~r/foo/i" == Utils.describe_regex_or_string(~r/foo/i)
end
test "returns string as-is" do
assert "foo" == Utils.describe_regex_or_string("foo")
end
end
end

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRFTest do
use ExUnit.Case, async: true
use ExUnit.Case
use Pleroma.Tests.Helpers
alias Pleroma.Web.ActivityPub.MRF

View file

@ -5,6 +5,7 @@
defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidatorTest do
use Pleroma.DataCase, async: true
alias Pleroma.Web.ActivityPub.ObjectValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator
alias Pleroma.Web.ActivityPub.Utils
@ -31,6 +32,54 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidatorTest
test "a basic note validates", %{note: note} do
%{valid?: true} = ArticleNotePageValidator.cast_and_validate(note)
end
test "a note from factory validates" do
note = insert(:note)
%{valid?: true} = ArticleNotePageValidator.cast_and_validate(note.data)
end
end
describe "Note with history" do
setup do
user = insert(:user)
{:ok, activity} = Pleroma.Web.CommonAPI.post(user, %{status: "mew mew :dinosaur:"})
{:ok, edit} = Pleroma.Web.CommonAPI.update(user, activity, %{status: "edited :blank:"})
{:ok, %{"object" => external_rep}} =
Pleroma.Web.ActivityPub.Transmogrifier.prepare_outgoing(edit.data)
%{external_rep: external_rep}
end
test "edited note", %{external_rep: external_rep} do
assert %{"formerRepresentations" => %{"orderedItems" => [%{"tag" => [_]}]}} = external_rep
{:ok, validate_res, []} = ObjectValidator.validate(external_rep, [])
assert %{"formerRepresentations" => %{"orderedItems" => [%{"emoji" => %{"dinosaur" => _}}]}} =
validate_res
end
test "edited note, badly-formed formerRepresentations", %{external_rep: external_rep} do
external_rep = Map.put(external_rep, "formerRepresentations", %{})
assert {:error, _} = ObjectValidator.validate(external_rep, [])
end
test "edited note, badly-formed history item", %{external_rep: external_rep} do
history_item =
Enum.at(external_rep["formerRepresentations"]["orderedItems"], 0)
|> Map.put("type", "Foo")
external_rep =
put_in(
external_rep,
["formerRepresentations", "orderedItems"],
[history_item]
)
assert {:error, _} = ObjectValidator.validate(external_rep, [])
end
end
test "a Note from Roadhouse validates" do
@ -43,4 +92,77 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidatorTest
%{valid?: true} = ArticleNotePageValidator.cast_and_validate(note)
end
test "a note with an attachment should work", _ do
insert(:user, %{ap_id: "https://owncast.localhost.localdomain/federation/user/streamer"})
note =
"test/fixtures/owncast-note-with-attachment.json"
|> File.read!()
|> Jason.decode!()
%{valid?: true} = ArticleNotePageValidator.cast_and_validate(note)
end
test "a Note without replies/first/items validates" do
insert(:user, ap_id: "https://mastodon.social/users/emelie")
note =
"test/fixtures/tesla_mock/status.emelie.json"
|> File.read!()
|> Jason.decode!()
|> pop_in(["replies", "first", "items"])
|> elem(1)
%{valid?: true} = ArticleNotePageValidator.cast_and_validate(note)
end
test "Fedibird quote post" do
insert(:user, ap_id: "https://fedibird.com/users/noellabo")
data = File.read!("test/fixtures/quote_post/fedibird_quote_post.json") |> Jason.decode!()
cng = ArticleNotePageValidator.cast_and_validate(data)
assert cng.valid?
assert cng.changes.quoteUrl == "https://misskey.io/notes/8vsn2izjwh"
end
test "Fedibird quote post with quoteUri field" do
insert(:user, ap_id: "https://fedibird.com/users/noellabo")
data = File.read!("test/fixtures/quote_post/fedibird_quote_uri.json") |> Jason.decode!()
cng = ArticleNotePageValidator.cast_and_validate(data)
assert cng.valid?
assert cng.changes.quoteUrl == "https://fedibird.com/users/yamako/statuses/107699333438289729"
end
test "Misskey quote post" do
insert(:user, ap_id: "https://misskey.io/users/7rkrarq81i")
data = File.read!("test/fixtures/quote_post/misskey_quote_post.json") |> Jason.decode!()
cng = ArticleNotePageValidator.cast_and_validate(data)
assert cng.valid?
assert cng.changes.quoteUrl == "https://misskey.io/notes/8vs6wxufd0"
end
test "Parse tag as quote" do
# https://codeberg.org/fediverse/fep/src/branch/main/fep/e232/fep-e232.md
insert(:user, ap_id: "https://server.example/users/1")
data = File.read!("test/fixtures/quote_post/fep-e232-tag-example.json") |> Jason.decode!()
cng = ArticleNotePageValidator.cast_and_validate(data)
assert cng.valid?
assert cng.changes.quoteUrl == "https://server.example/objects/123"
assert Enum.at(cng.changes.tag, 0).changes == %{
type: "Link",
mediaType: "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"",
href: "https://server.example/objects/123",
name: "RE: https://server.example/objects/123"
}
end
end

View file

@ -5,12 +5,27 @@
defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidatorTest do
use Pleroma.DataCase, async: true
alias Pleroma.UnstubbedConfigMock, as: ConfigMock
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator
import Mox
import Pleroma.Factory
describe "attachments" do
test "fails without url" do
attachment = %{
"mediaType" => "",
"name" => "",
"summary" => "298p3RG7j27tfsZ9RQ.jpg",
"type" => "Document"
}
assert {:error, _cng} =
AttachmentValidator.cast_and_validate(attachment)
|> Ecto.Changeset.apply_action(:insert)
end
test "works with honkerific attachments" do
attachment = %{
"mediaType" => "",
@ -27,6 +42,46 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidatorTest do
assert attachment.mediaType == "application/octet-stream"
end
test "works with an unknown but valid mime type" do
attachment = %{
"mediaType" => "x-custom/x-type",
"type" => "Document",
"url" => "https://example.org"
}
assert {:ok, attachment} =
AttachmentValidator.cast_and_validate(attachment)
|> Ecto.Changeset.apply_action(:insert)
assert attachment.mediaType == "x-custom/x-type"
end
test "works with invalid mime types" do
attachment = %{
"mediaType" => "x-customx-type",
"type" => "Document",
"url" => "https://example.org"
}
assert {:ok, attachment} =
AttachmentValidator.cast_and_validate(attachment)
|> Ecto.Changeset.apply_action(:insert)
assert attachment.mediaType == "application/octet-stream"
attachment = %{
"mediaType" => "https://example.org",
"type" => "Document",
"url" => "https://example.org"
}
assert {:ok, attachment} =
AttachmentValidator.cast_and_validate(attachment)
|> Ecto.Changeset.apply_action(:insert)
assert attachment.mediaType == "application/octet-stream"
end
test "it turns mastodon attachments into our attachments" do
attachment = %{
"url" =>
@ -63,6 +118,9 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidatorTest do
filename: "an_image.jpg"
}
ConfigMock
|> stub_with(Pleroma.Test.StaticConfig)
{:ok, attachment} = ActivityPub.upload(file, actor: user.ap_id)
{:ok, attachment} =

View file

@ -5,11 +5,13 @@
defmodule Pleroma.Web.ActivityPub.ObjectValidators.ChatValidationTest do
use Pleroma.DataCase
alias Pleroma.Object
alias Pleroma.UnstubbedConfigMock, as: ConfigMock
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Builder
alias Pleroma.Web.ActivityPub.ObjectValidator
alias Pleroma.Web.CommonAPI
import Mox
import Pleroma.Factory
describe "chat message create activities" do
@ -82,6 +84,9 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ChatValidationTest do
filename: "an_image.jpg"
}
ConfigMock
|> stub_with(Pleroma.Test.StaticConfig)
{:ok, attachment} = ActivityPub.upload(file, actor: user.ap_id)
valid_chat_message =
@ -103,6 +108,9 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ChatValidationTest do
filename: "an_image.jpg"
}
ConfigMock
|> stub_with(Pleroma.Test.StaticConfig)
{:ok, attachment} = ActivityPub.upload(file, actor: user.ap_id)
valid_chat_message =
@ -124,6 +132,9 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ChatValidationTest do
filename: "an_image.jpg"
}
ConfigMock
|> stub_with(Pleroma.Test.StaticConfig)
{:ok, attachment} = ActivityPub.upload(file, actor: user.ap_id)
valid_chat_message =

View file

@ -23,10 +23,10 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateGenericValidatorTest do
{:ok, object_data} = ObjectValidator.cast_and_apply(note_activity["object"])
meta = [object_data: ObjectValidator.stringify_keys(object_data)]
%{valid?: true} = CreateGenericValidator.cast_and_validate(note_activity, meta)
assert %{valid?: true} = CreateGenericValidator.cast_and_validate(note_activity, meta)
end
test "a Create/Note with mismatched context is invalid" do
test "a Create/Note with mismatched context uses the Note's context" do
user = insert(:user)
note = %{
@ -54,6 +54,9 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateGenericValidatorTest do
{:ok, object_data} = ObjectValidator.cast_and_apply(note_activity["object"])
meta = [object_data: ObjectValidator.stringify_keys(object_data)]
%{valid?: false} = CreateGenericValidator.cast_and_validate(note_activity, meta)
validated = CreateGenericValidator.cast_and_validate(note_activity, meta)
assert validated.valid?
assert {:context, note["context"]} in validated.changes
end
end

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidationTest do
use Pleroma.DataCase, async: true
use Pleroma.DataCase, async: false
alias Pleroma.Object
alias Pleroma.Web.ActivityPub.Builder
@ -90,17 +90,26 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidationTest do
assert {:actor, {"is not allowed to modify object", []}} in cng.errors
end
test "it's valid if the actor of the object is a local superuser",
test "it's only valid if the actor of the object is a privileged local user",
%{valid_post_delete: valid_post_delete} do
clear_config([:instance, :moderator_privileges], [:messages_delete])
user =
insert(:user, local: true, is_moderator: true, ap_id: "https://gensokyo.2hu/users/raymoo")
valid_other_actor =
post_delete_with_moderator_actor =
valid_post_delete
|> Map.put("actor", user.ap_id)
{:ok, _, meta} = ObjectValidator.validate(valid_other_actor, [])
{:ok, _, meta} = ObjectValidator.validate(post_delete_with_moderator_actor, [])
assert meta[:do_not_federate]
clear_config([:instance, :moderator_privileges], [])
{:error, cng} = ObjectValidator.validate(post_delete_with_moderator_actor, [])
assert {:actor, {"is not allowed to modify object", []}} in cng.errors
end
end
end

View file

@ -38,16 +38,70 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactHandlingTest do
assert {:content, {"can't be blank", [validation: :required]}} in cng.errors
end
test "it is not valid with a non-emoji content field", %{valid_emoji_react: valid_emoji_react} do
test "it is valid when custom emoji is used", %{valid_emoji_react: valid_emoji_react} do
without_emoji_content =
valid_emoji_react
|> Map.put("content", "x")
|> Map.put("content", ":hello:")
|> Map.put("tag", [
%{
"type" => "Emoji",
"name" => ":hello:",
"icon" => %{"url" => "http://somewhere", "type" => "Image"}
}
])
{:ok, _, _} = ObjectValidator.validate(without_emoji_content, [])
end
test "it is not valid when custom emoji don't have a matching tag", %{
valid_emoji_react: valid_emoji_react
} do
without_emoji_content =
valid_emoji_react
|> Map.put("content", ":hello:")
|> Map.put("tag", [
%{
"type" => "Emoji",
"name" => ":whoops:",
"icon" => %{"url" => "http://somewhere", "type" => "Image"}
}
])
{:error, cng} = ObjectValidator.validate(without_emoji_content, [])
refute cng.valid?
assert {:content, {"must be a single character emoji", []}} in cng.errors
assert {:tag, {"does not contain an Emoji tag", []}} in cng.errors
end
test "it is not valid when custom emoji have no tags", %{
valid_emoji_react: valid_emoji_react
} do
without_emoji_content =
valid_emoji_react
|> Map.put("content", ":hello:")
|> Map.put("tag", [])
{:error, cng} = ObjectValidator.validate(without_emoji_content, [])
refute cng.valid?
assert {:tag, {"does not contain an Emoji tag", []}} in cng.errors
end
test "it is not valid when custom emoji doesn't match a shortcode format", %{
valid_emoji_react: valid_emoji_react
} do
without_emoji_content =
valid_emoji_react
|> Map.put("content", "hello")
|> Map.put("tag", [])
{:error, cng} = ObjectValidator.validate(without_emoji_content, [])
refute cng.valid?
assert {:tag, {"does not contain an Emoji tag", []}} in cng.errors
end
end
end

View file

@ -32,7 +32,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.UpdateHandlingTest do
test "returns an error if the object can't be updated by the actor", %{
valid_update: valid_update
} do
other_user = insert(:user)
other_user = insert(:user, local: false)
update =
valid_update
@ -40,5 +40,129 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.UpdateHandlingTest do
assert {:error, _cng} = ObjectValidator.validate(update, [])
end
test "validates as long as the object is same-origin with the actor", %{
valid_update: valid_update
} do
other_user = insert(:user)
update =
valid_update
|> Map.put("actor", other_user.ap_id)
assert {:ok, _update, []} = ObjectValidator.validate(update, [])
end
test "validates if the object is not of an Actor type" do
note = insert(:note)
updated_note = note.data |> Map.put("content", "edited content")
other_user = insert(:user)
{:ok, update, _} = Builder.update(other_user, updated_note)
assert {:ok, _update, _} = ObjectValidator.validate(update, [])
end
end
describe "update note" do
test "converts object into Pleroma's format" do
mastodon_tags = [
%{
"icon" => %{
"mediaType" => "image/png",
"type" => "Image",
"url" => "https://somewhere.org/emoji/url/1.png"
},
"id" => "https://somewhere.org/emoji/1",
"name" => ":some_emoji:",
"type" => "Emoji",
"updated" => "2021-04-07T11:00:00Z"
}
]
user = insert(:user)
note = insert(:note, user: user)
updated_note =
note.data
|> Map.put("content", "edited content")
|> Map.put("tag", mastodon_tags)
{:ok, update, _} = Builder.update(user, updated_note)
assert {:ok, _update, meta} = ObjectValidator.validate(update, [])
assert %{"emoji" => %{"some_emoji" => "https://somewhere.org/emoji/url/1.png"}} =
meta[:object_data]
end
test "returns no object_data in meta for a local Update" do
user = insert(:user)
note = insert(:note, user: user)
updated_note =
note.data
|> Map.put("content", "edited content")
{:ok, update, _} = Builder.update(user, updated_note)
assert {:ok, _update, meta} = ObjectValidator.validate(update, local: true)
assert is_nil(meta[:object_data])
end
test "returns object_data in meta for a remote Update" do
user = insert(:user)
note = insert(:note, user: user)
updated_note =
note.data
|> Map.put("content", "edited content")
{:ok, update, _} = Builder.update(user, updated_note)
assert {:ok, _update, meta} = ObjectValidator.validate(update, local: false)
assert meta[:object_data]
assert {:ok, _update, meta} = ObjectValidator.validate(update, [])
assert meta[:object_data]
end
end
describe "update with history" do
setup do
user = insert(:user)
{:ok, activity} = Pleroma.Web.CommonAPI.post(user, %{status: "mew mew :dinosaur:"})
{:ok, edit} = Pleroma.Web.CommonAPI.update(user, activity, %{status: "edited :blank:"})
{:ok, external_rep} = Pleroma.Web.ActivityPub.Transmogrifier.prepare_outgoing(edit.data)
%{external_rep: external_rep}
end
test "edited note", %{external_rep: external_rep} do
{:ok, _validate_res, meta} = ObjectValidator.validate(external_rep, [])
assert %{"formerRepresentations" => %{"orderedItems" => [%{"emoji" => %{"dinosaur" => _}}]}} =
meta[:object_data]
end
test "edited note, badly-formed formerRepresentations", %{external_rep: external_rep} do
external_rep = put_in(external_rep, ["object", "formerRepresentations"], %{})
assert {:error, _} = ObjectValidator.validate(external_rep, [])
end
test "edited note, badly-formed history item", %{external_rep: external_rep} do
history_item =
Enum.at(external_rep["object"]["formerRepresentations"]["orderedItems"], 0)
|> Map.put("type", "Foo")
external_rep =
put_in(
external_rep,
["object", "formerRepresentations", "orderedItems"],
[history_item]
)
assert {:error, _} = ObjectValidator.validate(external_rep, [])
end
end
end

View file

@ -276,8 +276,7 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do
follower =
insert(:user, %{
local: false,
inbox: "https://domain.com/users/nick1/inbox",
ap_enabled: true
inbox: "https://domain.com/users/nick1/inbox"
})
actor = insert(:user, follower_address: follower.ap_id)
@ -313,8 +312,7 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do
follower =
insert(:user, %{
local: false,
inbox: "https://domain.com/users/nick1/inbox",
ap_enabled: true
inbox: "https://domain.com/users/nick1/inbox"
})
actor = insert(:user, follower_address: follower.ap_id)
@ -333,11 +331,40 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do
assert res == :ok
assert called(
Pleroma.Web.Federator.Publisher.enqueue_one(Publisher, %{
inbox: "https://domain.com/users/nick1/inbox",
actor_id: actor.id,
id: note_activity.data["id"]
})
Pleroma.Web.Federator.Publisher.enqueue_one(
Publisher,
%{
inbox: "https://domain.com/users/nick1/inbox",
actor_id: actor.id,
id: note_activity.data["id"]
},
priority: 1
)
)
end
test_with_mock "Publishes to directly addressed actors with higher priority.",
Pleroma.Web.Federator.Publisher,
[:passthrough],
[] do
note_activity = insert(:direct_note_activity)
actor = Pleroma.User.get_by_ap_id(note_activity.data["actor"])
res = Publisher.publish(actor, note_activity)
assert res == :ok
assert called(
Pleroma.Web.Federator.Publisher.enqueue_one(
Publisher,
%{
inbox: :_,
actor_id: actor.id,
id: note_activity.data["id"]
},
priority: 0
)
)
end
@ -348,8 +375,7 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do
follower =
insert(:user, %{
local: false,
inbox: "https://domain.com/users/nick1/inbox",
ap_enabled: true
inbox: "https://domain.com/users/nick1/inbox"
})
actor = insert(:user, follower_address: follower.ap_id)
@ -382,15 +408,13 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do
fetcher =
insert(:user,
local: false,
inbox: "https://domain.com/users/nick1/inbox",
ap_enabled: true
inbox: "https://domain.com/users/nick1/inbox"
)
another_fetcher =
insert(:user,
local: false,
inbox: "https://domain2.com/users/nick1/inbox",
ap_enabled: true
inbox: "https://domain2.com/users/nick1/inbox"
)
actor = insert(:user)
@ -419,19 +443,27 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do
assert res == :ok
assert called(
Pleroma.Web.Federator.Publisher.enqueue_one(Publisher, %{
inbox: "https://domain.com/users/nick1/inbox",
actor_id: actor.id,
id: delete.data["id"]
})
Pleroma.Web.Federator.Publisher.enqueue_one(
Publisher,
%{
inbox: "https://domain.com/users/nick1/inbox",
actor_id: actor.id,
id: delete.data["id"]
},
priority: 1
)
)
assert called(
Pleroma.Web.Federator.Publisher.enqueue_one(Publisher, %{
inbox: "https://domain2.com/users/nick1/inbox",
actor_id: actor.id,
id: delete.data["id"]
})
Pleroma.Web.Federator.Publisher.enqueue_one(
Publisher,
%{
inbox: "https://domain2.com/users/nick1/inbox",
actor_id: actor.id,
id: delete.data["id"]
},
priority: 1
)
)
end
end

View file

@ -118,7 +118,10 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do
describe "update users" do
setup do
user = insert(:user, local: false)
{:ok, update_data, []} = Builder.update(user, %{"id" => user.ap_id, "name" => "new name!"})
{:ok, update_data, []} =
Builder.update(user, %{"id" => user.ap_id, "type" => "Person", "name" => "new name!"})
{:ok, update, _meta} = ActivityPub.persist(update_data, local: true)
%{user: user, update_data: update_data, update: update}
@ -140,6 +143,298 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do
end
end
describe "update notes" do
setup do
make_time = fn ->
Pleroma.Web.ActivityPub.Utils.make_date()
end
user = insert(:user)
note = insert(:note, user: user, data: %{"published" => make_time.()})
_note_activity = insert(:note_activity, note: note)
updated_note =
note.data
|> Map.put("summary", "edited summary")
|> Map.put("content", "edited content")
|> Map.put("updated", make_time.())
{:ok, update_data, []} = Builder.update(user, updated_note)
{:ok, update, _meta} = ActivityPub.persist(update_data, local: true)
%{
user: user,
note: note,
object_id: note.id,
update_data: update_data,
update: update,
updated_note: updated_note
}
end
test "it updates the note", %{
object_id: object_id,
update: update,
updated_note: updated_note
} do
{:ok, _, _} = SideEffects.handle(update, object_data: updated_note)
updated_time = updated_note["updated"]
new_note = Pleroma.Object.get_by_id(object_id)
assert %{
"summary" => "edited summary",
"content" => "edited content",
"updated" => ^updated_time
} = new_note.data
end
test "it rejects updates with no updated attribute in object", %{
object_id: object_id,
update: update,
updated_note: updated_note
} do
old_note = Pleroma.Object.get_by_id(object_id)
updated_note = Map.drop(updated_note, ["updated"])
{:ok, _, _} = SideEffects.handle(update, object_data: updated_note)
new_note = Pleroma.Object.get_by_id(object_id)
assert old_note.data == new_note.data
end
test "it rejects updates with updated attribute older than what we have in the original object",
%{
object_id: object_id,
update: update,
updated_note: updated_note
} do
old_note = Pleroma.Object.get_by_id(object_id)
{:ok, creation_time, _} = DateTime.from_iso8601(old_note.data["published"])
updated_note =
Map.put(updated_note, "updated", DateTime.to_iso8601(DateTime.add(creation_time, -10)))
{:ok, _, _} = SideEffects.handle(update, object_data: updated_note)
new_note = Pleroma.Object.get_by_id(object_id)
assert old_note.data == new_note.data
end
test "it rejects updates with updated attribute older than the last Update", %{
object_id: object_id,
update: update,
updated_note: updated_note
} do
old_note = Pleroma.Object.get_by_id(object_id)
{:ok, creation_time, _} = DateTime.from_iso8601(old_note.data["published"])
updated_note =
Map.put(updated_note, "updated", DateTime.to_iso8601(DateTime.add(creation_time, +10)))
{:ok, _, _} = SideEffects.handle(update, object_data: updated_note)
old_note = Pleroma.Object.get_by_id(object_id)
{:ok, update_time, _} = DateTime.from_iso8601(old_note.data["updated"])
updated_note =
Map.put(updated_note, "updated", DateTime.to_iso8601(DateTime.add(update_time, -5)))
{:ok, _, _} = SideEffects.handle(update, object_data: updated_note)
new_note = Pleroma.Object.get_by_id(object_id)
assert old_note.data == new_note.data
end
test "it updates using object_data", %{
object_id: object_id,
update: update,
updated_note: updated_note
} do
updated_note = Map.put(updated_note, "summary", "mew mew")
{:ok, _, _} = SideEffects.handle(update, object_data: updated_note)
new_note = Pleroma.Object.get_by_id(object_id)
assert %{"summary" => "mew mew", "content" => "edited content"} = new_note.data
end
test "it records the original note in formerRepresentations", %{
note: note,
object_id: object_id,
update: update,
updated_note: updated_note
} do
{:ok, _, _} = SideEffects.handle(update, object_data: updated_note)
%{data: new_note} = Pleroma.Object.get_by_id(object_id)
assert %{"summary" => "edited summary", "content" => "edited content"} = new_note
assert [Map.drop(note.data, ["id", "formerRepresentations"])] ==
new_note["formerRepresentations"]["orderedItems"]
assert new_note["formerRepresentations"]["totalItems"] == 1
end
test "it puts the original note at the front of formerRepresentations", %{
user: user,
note: note,
object_id: object_id,
update: update,
updated_note: updated_note
} do
{:ok, _, _} = SideEffects.handle(update, object_data: updated_note)
%{data: first_edit} = Pleroma.Object.get_by_id(object_id)
second_updated_note =
note.data
|> Map.put("summary", "edited summary 2")
|> Map.put("content", "edited content 2")
|> Map.put(
"updated",
first_edit["updated"]
|> DateTime.from_iso8601()
|> elem(1)
|> DateTime.add(10)
|> DateTime.to_iso8601()
)
{:ok, second_update_data, []} = Builder.update(user, second_updated_note)
{:ok, update, _meta} = ActivityPub.persist(second_update_data, local: true)
{:ok, _, _} = SideEffects.handle(update, object_data: second_updated_note)
%{data: new_note} = Pleroma.Object.get_by_id(object_id)
assert %{"summary" => "edited summary 2", "content" => "edited content 2"} = new_note
original_version = Map.drop(note.data, ["id", "formerRepresentations"])
first_edit = Map.drop(first_edit, ["id", "formerRepresentations"])
assert [first_edit, original_version] ==
new_note["formerRepresentations"]["orderedItems"]
assert new_note["formerRepresentations"]["totalItems"] == 2
end
test "it does not prepend to formerRepresentations if no actual changes are made", %{
note: note,
object_id: object_id,
update: update,
updated_note: updated_note
} do
{:ok, _, _} = SideEffects.handle(update, object_data: updated_note)
%{data: first_edit} = Pleroma.Object.get_by_id(object_id)
updated_note =
updated_note
|> Map.put(
"updated",
first_edit["updated"]
|> DateTime.from_iso8601()
|> elem(1)
|> DateTime.add(10)
|> DateTime.to_iso8601()
)
{:ok, _, _} = SideEffects.handle(update, object_data: updated_note)
%{data: new_note} = Pleroma.Object.get_by_id(object_id)
assert %{"summary" => "edited summary", "content" => "edited content"} = new_note
original_version = Map.drop(note.data, ["id", "formerRepresentations"])
assert [original_version] ==
new_note["formerRepresentations"]["orderedItems"]
assert new_note["formerRepresentations"]["totalItems"] == 1
end
end
describe "update questions" do
setup do
user = insert(:user)
question =
insert(:question,
user: user,
data: %{"published" => Pleroma.Web.ActivityPub.Utils.make_date()}
)
%{user: user, data: question.data, id: question.id}
end
test "allows updating choice count without generating edit history", %{
user: user,
data: data,
id: id
} do
new_choices =
data["oneOf"]
|> Enum.map(fn choice -> put_in(choice, ["replies", "totalItems"], 5) end)
updated_question =
data
|> Map.put("oneOf", new_choices)
|> Map.put("updated", Pleroma.Web.ActivityPub.Utils.make_date())
{:ok, update_data, []} = Builder.update(user, updated_question)
{:ok, update, _meta} = ActivityPub.persist(update_data, local: true)
{:ok, _, _} = SideEffects.handle(update, object_data: updated_question)
%{data: new_question} = Pleroma.Object.get_by_id(id)
assert [%{"replies" => %{"totalItems" => 5}}, %{"replies" => %{"totalItems" => 5}}] =
new_question["oneOf"]
refute Map.has_key?(new_question, "formerRepresentations")
end
test "allows updating choice count without updated field", %{
user: user,
data: data,
id: id
} do
new_choices =
data["oneOf"]
|> Enum.map(fn choice -> put_in(choice, ["replies", "totalItems"], 5) end)
updated_question =
data
|> Map.put("oneOf", new_choices)
{:ok, update_data, []} = Builder.update(user, updated_question)
{:ok, update, _meta} = ActivityPub.persist(update_data, local: true)
{:ok, _, _} = SideEffects.handle(update, object_data: updated_question)
%{data: new_question} = Pleroma.Object.get_by_id(id)
assert [%{"replies" => %{"totalItems" => 5}}, %{"replies" => %{"totalItems" => 5}}] =
new_question["oneOf"]
refute Map.has_key?(new_question, "formerRepresentations")
end
test "allows updating choice count with updated field same as the creation date", %{
user: user,
data: data,
id: id
} do
new_choices =
data["oneOf"]
|> Enum.map(fn choice -> put_in(choice, ["replies", "totalItems"], 5) end)
updated_question =
data
|> Map.put("oneOf", new_choices)
|> Map.put("updated", data["published"])
{:ok, update_data, []} = Builder.update(user, updated_question)
{:ok, update, _meta} = ActivityPub.persist(update_data, local: true)
{:ok, _, _} = SideEffects.handle(update, object_data: updated_question)
%{data: new_question} = Pleroma.Object.get_by_id(id)
assert [%{"replies" => %{"totalItems" => 5}}, %{"replies" => %{"totalItems" => 5}}] =
new_question["oneOf"]
refute Map.has_key?(new_question, "formerRepresentations")
end
end
describe "EmojiReact objects" do
setup do
poster = insert(:user)
@ -158,7 +453,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do
object = Object.get_by_ap_id(emoji_react.data["object"])
assert object.data["reaction_count"] == 1
assert ["👌", [user.ap_id]] in object.data["reactions"]
assert ["👌", [user.ap_id], nil] in object.data["reactions"]
end
test "creates a notification", %{emoji_react: emoji_react, poster: poster} do
@ -544,9 +839,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do
]) do
{:ok, announce, _} = SideEffects.handle(announce)
assert called(
Pleroma.Web.Streamer.stream(["user", "list", "public", "public:local"], announce)
)
assert called(Pleroma.Web.Streamer.stream(["user", "list"], announce))
assert called(Pleroma.Web.Push.send(:_))
end

View file

@ -34,7 +34,87 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.EmojiReactHandlingTest do
object = Object.get_by_ap_id(data["object"])
assert object.data["reaction_count"] == 1
assert match?([["👌", _]], object.data["reactions"])
assert match?([["👌", _, nil]], object.data["reactions"])
end
test "it works for incoming custom emoji reactions" do
user = insert(:user)
other_user = insert(:user, local: false)
{:ok, activity} = CommonAPI.post(user, %{status: "hello"})
data =
File.read!("test/fixtures/custom-emoji-reaction.json")
|> Jason.decode!()
|> Map.put("object", activity.data["object"])
|> Map.put("actor", other_user.ap_id)
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
assert data["actor"] == other_user.ap_id
assert data["type"] == "EmojiReact"
assert data["id"] == "https://misskey.local.live/likes/917ocsybgp"
assert data["object"] == activity.data["object"]
assert data["content"] == ":hanapog:"
assert data["tag"] == [
%{
"id" => "https://misskey.local.live/emojis/hanapog",
"type" => "Emoji",
"name" => "hanapog",
"updated" => "2022-06-07T12:00:05.773Z",
"icon" => %{
"type" => "Image",
"url" =>
"https://misskey.local.live/files/webpublic-8f8a9768-7264-4171-88d6-2356aabeadcd"
}
}
]
object = Object.get_by_ap_id(data["object"])
assert object.data["reaction_count"] == 1
assert match?(
[
[
"hanapog",
_,
"https://misskey.local.live/files/webpublic-8f8a9768-7264-4171-88d6-2356aabeadcd"
]
],
object.data["reactions"]
)
end
test "it works for incoming unqualified emoji reactions" do
user = insert(:user)
other_user = insert(:user, local: false)
{:ok, activity} = CommonAPI.post(user, %{status: "hello"})
# woman detective emoji, unqualified
unqualified_emoji = [0x1F575, 0x200D, 0x2640] |> List.to_string()
data =
File.read!("test/fixtures/emoji-reaction.json")
|> Jason.decode!()
|> Map.put("object", activity.data["object"])
|> Map.put("actor", other_user.ap_id)
|> Map.put("content", unqualified_emoji)
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
assert data["actor"] == other_user.ap_id
assert data["type"] == "EmojiReact"
assert data["id"] == "http://mastodon.example.org/users/admin#reactions/2"
assert data["object"] == activity.data["object"]
# woman detective emoji, fully qualified
emoji = [0x1F575, 0xFE0F, 0x200D, 0x2640, 0xFE0F] |> List.to_string()
assert data["content"] == emoji
object = Object.get_by_ap_id(data["object"])
assert object.data["reaction_count"] == 1
assert match?([[^emoji, _, _]], object.data["reactions"])
end
test "it reject invalid emoji reactions" do

View file

@ -0,0 +1,50 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.Transmogrifier.ImageHandlingTest do
use Oban.Testing, repo: Pleroma.Repo
use Pleroma.DataCase
alias Pleroma.Activity
alias Pleroma.Object
alias Pleroma.Web.ActivityPub.Transmogrifier
test "Hubzilla Image object" do
Tesla.Mock.mock(fn
%{url: "https://hub.somaton.com/channel/testc6"} ->
%Tesla.Env{
status: 200,
body: File.read!("test/fixtures/hubzilla-actor.json"),
headers: HttpRequestMock.activitypub_object_headers()
}
end)
data = File.read!("test/fixtures/hubzilla-create-image.json") |> Poison.decode!()
{:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data)
assert object = Object.normalize(activity, fetch: false)
assert object.data["to"] == ["https://www.w3.org/ns/activitystreams#Public"]
assert object.data["cc"] == ["https://hub.somaton.com/followers/testc6"]
assert object.data["attachment"] == [
%{
"mediaType" => "image/jpeg",
"type" => "Link",
"url" => [
%{
"height" => 2200,
"href" =>
"https://hub.somaton.com/photo/452583b2-7e1f-4ac3-8334-ff666f134afe-0.jpg",
"mediaType" => "image/jpeg",
"type" => "Link",
"width" => 2200
}
]
}
]
end
end

View file

@ -104,6 +104,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.NoteHandlingTest do
end
end
@tag capture_log: true
test "it does not crash if the object in inReplyTo can't be fetched" do
data =
File.read!("test/fixtures/mastodon-post-activity.json")
@ -707,4 +708,43 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.NoteHandlingTest do
}
]
end
test "the standalone note uses its own ID when context is missing" do
insert(:user, ap_id: "https://mk.absturztau.be/users/8ozbzjs3o8")
activity =
"test/fixtures/tesla_mock/mk.absturztau.be-93e7nm8wqg-activity.json"
|> File.read!()
|> Jason.decode!()
{:ok, %Activity{} = modified} = Transmogrifier.handle_incoming(activity)
object = Object.normalize(modified, fetch: false)
assert object.data["context"] == object.data["id"]
assert modified.data["context"] == object.data["id"]
end
@tag capture_log: true
test "the reply note uses its parent's ID when context is missing and reply is unreachable" do
insert(:user, ap_id: "https://mk.absturztau.be/users/8ozbzjs3o8")
activity =
"test/fixtures/tesla_mock/mk.absturztau.be-93e7nm8wqg-activity.json"
|> File.read!()
|> Jason.decode!()
object =
activity["object"]
|> Map.put("inReplyTo", "https://404.site/object/went-to-buy-milk")
activity =
activity
|> Map.put("object", object)
{:ok, %Activity{} = modified} = Transmogrifier.handle_incoming(activity)
object = Object.normalize(modified, fetch: false)
assert object.data["context"] == object.data["inReplyTo"]
assert modified.data["context"] == object.data["inReplyTo"]
end
end

View file

@ -33,8 +33,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.QuestionHandlingTest do
assert object.data["context"] ==
"tag:mastodon.sdf.org,2019-05-10:objectId=15095122:objectType=Conversation"
assert object.data["context_id"]
assert object.data["anyOf"] == []
assert Enum.sort(object.data["oneOf"]) ==
@ -68,7 +66,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.QuestionHandlingTest do
reply_object = Object.normalize(reply_activity, fetch: false)
assert reply_object.data["context"] == object.data["context"]
assert reply_object.data["context_id"] == object.data["context_id"]
end
test "Mastodon Question activity with HTML tags in plaintext" do

View file

@ -8,7 +8,6 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
alias Pleroma.Activity
alias Pleroma.Object
alias Pleroma.Tests.ObanHelpers
alias Pleroma.User
alias Pleroma.Web.ActivityPub.Transmogrifier
alias Pleroma.Web.ActivityPub.Utils
@ -61,7 +60,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
note_obj = %{
"type" => "Note",
"id" => activity.data["id"],
"id" => activity.object.data["id"],
"content" => "test post",
"published" => object.data["published"],
"actor" => AccountView.render("show.json", %{user: user, skip_visibility_check: true})
@ -108,15 +107,54 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
assert activity.data["type"] == "Move"
end
test "a reply with mismatched context is rejected" do
insert(:user, ap_id: "https://macgirvin.com/channel/mike")
test "it fixes both the Create and object contexts in a reply" do
insert(:user, ap_id: "https://mk.absturztau.be/users/8ozbzjs3o8")
insert(:user, ap_id: "https://p.helene.moe/users/helene")
note_activity =
"test/fixtures/roadhouse-create-activity.json"
create_activity =
"test/fixtures/create-pleroma-reply-to-misskey-thread.json"
|> File.read!()
|> Jason.decode!()
assert {:error, _} = Transmogrifier.handle_incoming(note_activity)
assert {:ok, %Activity{} = activity} = Transmogrifier.handle_incoming(create_activity)
object = Object.normalize(activity, fetch: false)
assert activity.data["context"] == object.data["context"]
end
test "it keeps link tags" do
insert(:user, ap_id: "https://example.org/users/alice")
message = File.read!("test/fixtures/fep-e232.json") |> Jason.decode!()
assert capture_log(fn ->
assert {:ok, activity} = Transmogrifier.handle_incoming(message)
object = Object.normalize(activity)
assert [%{"type" => "Mention"}, %{"type" => "Link"}] = object.data["tag"]
end) =~ "Error while fetching"
end
test "it accepts quote posts" do
insert(:user, ap_id: "https://misskey.io/users/7rkrarq81i")
object = File.read!("test/fixtures/quote_post/misskey_quote_post.json") |> Jason.decode!()
message = %{
"@context" => "https://www.w3.org/ns/activitystreams",
"type" => "Create",
"actor" => "https://misskey.io/users/7rkrarq81i",
"object" => object
}
assert {:ok, activity} = Transmogrifier.handle_incoming(message)
# Object was created in the database
object = Object.normalize(activity)
assert object.data["quoteUrl"] == "https://misskey.io/notes/8vs6wxufd0"
# It fetched the quoted post
assert Object.normalize("https://misskey.io/notes/8vs6wxufd0")
end
end
@ -227,7 +265,6 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
assert is_nil(modified["object"]["like_count"])
assert is_nil(modified["object"]["announcements"])
assert is_nil(modified["object"]["announcement_count"])
assert is_nil(modified["object"]["context_id"])
assert is_nil(modified["object"]["generator"])
end
@ -242,7 +279,6 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
assert is_nil(modified["object"]["like_count"])
assert is_nil(modified["object"]["announcements"])
assert is_nil(modified["object"]["announcement_count"])
assert is_nil(modified["object"]["context_id"])
assert is_nil(modified["object"]["likes"])
end
@ -312,68 +348,41 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
assert url == "http://localhost:4001/emoji/dino%20walking.gif"
end
end
describe "user upgrade" do
test "it upgrades a user to activitypub" do
user =
insert(:user, %{
nickname: "rye@niu.moe",
local: false,
ap_id: "https://niu.moe/users/rye",
follower_address: User.ap_followers(%User{nickname: "rye@niu.moe"})
})
test "Updates of Notes are handled" do
user = insert(:user)
user_two = insert(:user)
Pleroma.FollowingRelationship.follow(user_two, user, :follow_accept)
{:ok, activity} = CommonAPI.post(user, %{status: "everybody do the dinosaur :dinosaur:"})
{:ok, update} = CommonAPI.update(user, activity, %{status: "mew mew :blank:"})
{:ok, activity} = CommonAPI.post(user, %{status: "test"})
{:ok, unrelated_activity} = CommonAPI.post(user_two, %{status: "test"})
assert "http://localhost:4001/users/rye@niu.moe/followers" in activity.recipients
user = User.get_cached_by_id(user.id)
assert user.note_count == 1
{:ok, user} = Transmogrifier.upgrade_user_from_ap_id("https://niu.moe/users/rye")
ObanHelpers.perform_all()
assert user.ap_enabled
assert user.note_count == 1
assert user.follower_address == "https://niu.moe/users/rye/followers"
assert user.following_address == "https://niu.moe/users/rye/following"
user = User.get_cached_by_id(user.id)
assert user.note_count == 1
activity = Activity.get_by_id(activity.id)
assert user.follower_address in activity.recipients
{:ok, prepared} = Transmogrifier.prepare_outgoing(update.data)
assert %{
"url" => [
%{
"href" =>
"https://cdn.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg"
}
]
} = user.avatar
"content" => "mew mew :blank:",
"tag" => [%{"name" => ":blank:", "type" => "Emoji"}],
"formerRepresentations" => %{
"orderedItems" => [
%{
"content" => "everybody do the dinosaur :dinosaur:",
"tag" => [%{"name" => ":dinosaur:", "type" => "Emoji"}]
}
]
}
} = prepared["object"]
end
assert %{
"url" => [
%{
"href" =>
"https://cdn.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"
}
]
} = user.banner
test "it prepares a quote post" do
user = insert(:user)
refute "..." in activity.recipients
{:ok, quoted_post} = CommonAPI.post(user, %{status: "hey"})
{:ok, quote_post} = CommonAPI.post(user, %{status: "hey", quote_id: quoted_post.id})
unrelated_activity = Activity.get_by_id(unrelated_activity.id)
refute user.follower_address in unrelated_activity.recipients
{:ok, modified} = Transmogrifier.prepare_outgoing(quote_post.data)
user_two = User.get_cached_by_id(user_two.id)
assert User.following?(user_two, user)
refute "..." in User.following(user_two)
%{data: %{"id" => quote_id}} = Object.normalize(quoted_post)
assert modified["object"]["quoteUrl"] == quote_id
assert modified["object"]["quoteUri"] == quote_id
end
end
@ -575,4 +584,43 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
assert Transmogrifier.fix_attachments(object) == expected
end
end
describe "prepare_object/1" do
test "it processes history" do
original = %{
"formerRepresentations" => %{
"orderedItems" => [
%{
"generator" => %{},
"emoji" => %{"blobcat" => "http://localhost:4001/emoji/blobcat.png"}
}
]
}
}
processed = Transmogrifier.prepare_object(original)
history_item = Enum.at(processed["formerRepresentations"]["orderedItems"], 0)
refute Map.has_key?(history_item, "generator")
assert [%{"name" => ":blobcat:"}] = history_item["tag"]
end
test "it works when there is no or bad history" do
original = %{
"formerRepresentations" => %{
"items" => [
%{
"generator" => %{},
"emoji" => %{"blobcat" => "http://localhost:4001/emoji/blobcat.png"}
}
]
}
}
processed = Transmogrifier.prepare_object(original)
assert processed["formerRepresentations"] == original["formerRepresentations"]
end
end
end

View file

@ -16,6 +16,41 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do
require Pleroma.Constants
describe "strip_report_status_data/1" do
test "does not break on issues with the reported activites" do
reporter = insert(:user)
target_account = insert(:user)
{:ok, activity} = CommonAPI.post(target_account, %{status: "foobar"})
context = Utils.generate_context_id()
content = "foobar"
post_id = activity.data["id"]
res =
Utils.make_flag_data(
%{
actor: reporter,
context: context,
account: target_account,
statuses: [%{"id" => post_id}],
content: content
},
%{}
)
res =
res
|> Map.put("object", res["object"] ++ [nil, 1, 5, "123"])
{:ok, activity} = Pleroma.Web.ActivityPub.ActivityPub.insert(res)
[user_id, object | _] = activity.data["object"]
{:ok, stripped} = Utils.strip_report_status_data(activity)
assert stripped.data["object"] == [user_id, object["id"]]
end
end
describe "fetch the latest Follow" do
test "fetches the latest Follow activity" do
%Activity{data: %{"type" => "Follow"}} = activity = insert(:follow_activity)
@ -429,7 +464,6 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do
object = Object.normalize(note_activity, fetch: false)
res = Utils.lazy_put_activity_defaults(%{"context" => object.data["id"]})
assert res["context"] == object.data["id"]
assert res["context_id"] == object.id
assert res["id"]
assert res["published"]
end
@ -437,7 +471,6 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do
test "returns map with fake id and published data" do
assert %{
"context" => "pleroma:fakecontext",
"context_id" => -1,
"id" => "pleroma:fakeid",
"published" => _
} = Utils.lazy_put_activity_defaults(%{}, true)
@ -454,13 +487,11 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do
})
assert res["context"] == object.data["id"]
assert res["context_id"] == object.id
assert res["id"]
assert res["published"]
assert res["object"]["id"]
assert res["object"]["published"]
assert res["object"]["context"] == object.data["id"]
assert res["object"]["context_id"] == object.id
end
end
@ -477,7 +508,7 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do
content = "foobar"
target_ap_id = target_account.ap_id
activity_ap_id = activity.data["id"]
object_ap_id = activity.object.data["id"]
res =
Utils.make_flag_data(
@ -493,7 +524,7 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do
note_obj = %{
"type" => "Note",
"id" => activity_ap_id,
"id" => object_ap_id,
"content" => content,
"published" => activity.object.data["published"],
"actor" =>
@ -508,6 +539,49 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do
"state" => "open"
} = res
end
test "returns map with Flag object with a non-Create Activity" do
reporter = insert(:user)
posting_account = insert(:user)
target_account = insert(:user)
{:ok, activity} = CommonAPI.post(posting_account, %{status: "foobar"})
{:ok, like} = CommonAPI.favorite(target_account, activity.id)
context = Utils.generate_context_id()
content = "foobar"
target_ap_id = target_account.ap_id
object_ap_id = activity.object.data["id"]
res =
Utils.make_flag_data(
%{
actor: reporter,
context: context,
account: target_account,
statuses: [%{"id" => like.data["id"]}],
content: content
},
%{}
)
note_obj = %{
"type" => "Note",
"id" => object_ap_id,
"content" => content,
"published" => activity.object.data["published"],
"actor" =>
AccountView.render("show.json", %{user: posting_account, skip_visibility_check: true})
}
assert %{
"type" => "Flag",
"content" => ^content,
"context" => ^context,
"object" => [^target_ap_id, ^note_obj],
"state" => "open"
} = res
end
end
describe "add_announce_to_object/2" do
@ -548,15 +622,38 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do
end
describe "get_cached_emoji_reactions/1" do
test "returns the data or an emtpy list" do
test "returns the normalized data or an emtpy list" do
object = insert(:note)
assert Utils.get_cached_emoji_reactions(object) == []
object = insert(:note, data: %{"reactions" => [["x", ["lain"]]]})
assert Utils.get_cached_emoji_reactions(object) == [["x", ["lain"]]]
assert Utils.get_cached_emoji_reactions(object) == [["x", ["lain"], nil]]
object = insert(:note, data: %{"reactions" => %{}})
assert Utils.get_cached_emoji_reactions(object) == []
end
end
describe "add_emoji_reaction_to_object/1" do
test "works with legacy 2-tuple format" do
user = insert(:user)
other_user = insert(:user)
third_user = insert(:user)
note =
insert(:note,
user: user,
data: %{
"reactions" => [["😿", [other_user.ap_id]]]
}
)
_activity = insert(:note_activity, user: user, note: note)
Utils.add_emoji_reaction_to_object(
%Activity{data: %{"content" => "😿", "actor" => third_user.ap_id}},
note
)
end
end
end

View file

@ -81,4 +81,18 @@ defmodule Pleroma.Web.ActivityPub.ObjectViewTest do
assert result["object"] == object.data["id"]
assert result["type"] == "Announce"
end
test "renders an undo announce activity" do
note = insert(:note_activity)
user = insert(:user)
{:ok, announce} = CommonAPI.repeat(note.id, user)
{:ok, undo} = CommonAPI.unrepeat(note.id, user)
result = ObjectView.render("object.json", %{object: undo})
assert result["id"] == undo.data["id"]
assert result["object"] == announce.data["id"]
assert result["type"] == "Undo"
end
end

View file

@ -12,7 +12,6 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
test "Renders a user, including the public key" do
user = insert(:user)
{:ok, user} = User.ensure_keys_present(user)
result = UserView.render("user.json", %{user: user})
@ -55,7 +54,6 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
test "Does not add an avatar image if the user hasn't set one" do
user = insert(:user)
{:ok, user} = User.ensure_keys_present(user)
result = UserView.render("user.json", %{user: user})
refute result["icon"]
@ -67,8 +65,6 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
banner: %{"url" => [%{"href" => "https://somebanner"}]}
)
{:ok, user} = User.ensure_keys_present(user)
result = UserView.render("user.json", %{user: user})
assert result["icon"]["url"] == "https://someurl"
assert result["image"]["url"] == "https://somebanner"
@ -80,6 +76,15 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
assert %{"invisible" => true} = UserView.render("service.json", %{user: user})
end
test "service has a few essential fields" do
user = insert(:user)
result = UserView.render("service.json", %{user: user})
assert result["id"]
assert result["type"] == "Application"
assert result["inbox"]
assert result["outbox"]
end
test "renders AKAs" do
akas = ["https://i.tusooa.xyz/users/test-pleroma"]
user = insert(:user, also_known_as: akas)
@ -89,7 +94,6 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
describe "endpoints" do
test "local users have a usable endpoints structure" do
user = insert(:user)
{:ok, user} = User.ensure_keys_present(user)
result = UserView.render("user.json", %{user: user})
@ -105,7 +109,6 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
test "remote users have an empty endpoints structure" do
user = insert(:user, local: false)
{:ok, user} = User.ensure_keys_present(user)
result = UserView.render("user.json", %{user: user})
@ -115,7 +118,6 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
test "instance users do not expose oAuth endpoints" do
user = insert(:user, nickname: nil, local: true)
{:ok, user} = User.ensure_keys_present(user)
result = UserView.render("user.json", %{user: user})

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
use Pleroma.Web.ConnCase
use Pleroma.Web.ConnCase, async: false
use Oban.Testing, repo: Pleroma.Repo
import ExUnit.CaptureLog
@ -15,6 +15,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
alias Pleroma.ModerationLog
alias Pleroma.Repo
alias Pleroma.Tests.ObanHelpers
alias Pleroma.UnstubbedConfigMock, as: ConfigMock
alias Pleroma.User
alias Pleroma.Web.CommonAPI
@ -92,18 +93,12 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
describe "PUT /api/pleroma/admin/users/tag" do
setup %{conn: conn} do
clear_config([:instance, :admin_privileges], [:users_manage_tags])
user1 = insert(:user, %{tags: ["x"]})
user2 = insert(:user, %{tags: ["y"]})
user3 = insert(:user, %{tags: ["unchanged"]})
conn =
conn
|> put_req_header("accept", "application/json")
|> put(
"/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <>
"#{user2.nickname}&tags[]=foo&tags[]=bar"
)
%{conn: conn, user1: user1, user2: user2, user3: user3}
end
@ -113,6 +108,14 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
user1: user1,
user2: user2
} do
conn =
conn
|> put_req_header("accept", "application/json")
|> put(
"/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <>
"#{user2.nickname}&tags[]=foo&tags[]=bar"
)
assert empty_json_response(conn)
assert User.get_cached_by_id(user1.id).tags == ["x", "foo", "bar"]
assert User.get_cached_by_id(user2.id).tags == ["y", "foo", "bar"]
@ -130,26 +133,43 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
"@#{admin.nickname} added tags: #{tags} to users: #{users}"
end
test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do
test "it does not modify tags of not specified users", %{
conn: conn,
user1: user1,
user2: user2,
user3: user3
} do
conn =
conn
|> put_req_header("accept", "application/json")
|> put(
"/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <>
"#{user2.nickname}&tags[]=foo&tags[]=bar"
)
assert empty_json_response(conn)
assert User.get_cached_by_id(user3.id).tags == ["unchanged"]
end
test "it requires privileged role :users_manage_tags", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
response =
conn
|> put_req_header("accept", "application/json")
|> put("/api/pleroma/admin/users/tag?nicknames[]=nickname&tags[]=foo&tags[]=bar")
assert json_response(response, :forbidden)
end
end
describe "DELETE /api/pleroma/admin/users/tag" do
setup %{conn: conn} do
clear_config([:instance, :admin_privileges], [:users_manage_tags])
user1 = insert(:user, %{tags: ["x"]})
user2 = insert(:user, %{tags: ["y", "z"]})
user3 = insert(:user, %{tags: ["unchanged"]})
conn =
conn
|> put_req_header("accept", "application/json")
|> delete(
"/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <>
"#{user2.nickname}&tags[]=x&tags[]=z"
)
%{conn: conn, user1: user1, user2: user2, user3: user3}
end
@ -159,6 +179,14 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
user1: user1,
user2: user2
} do
conn =
conn
|> put_req_header("accept", "application/json")
|> delete(
"/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <>
"#{user2.nickname}&tags[]=x&tags[]=z"
)
assert empty_json_response(conn)
assert User.get_cached_by_id(user1.id).tags == []
assert User.get_cached_by_id(user2.id).tags == ["y"]
@ -176,10 +204,34 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
"@#{admin.nickname} removed tags: #{tags} from users: #{users}"
end
test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do
test "it does not modify tags of not specified users", %{
conn: conn,
user1: user1,
user2: user2,
user3: user3
} do
conn =
conn
|> put_req_header("accept", "application/json")
|> delete(
"/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <>
"#{user2.nickname}&tags[]=x&tags[]=z"
)
assert empty_json_response(conn)
assert User.get_cached_by_id(user3.id).tags == ["unchanged"]
end
test "it requires privileged role :users_manage_tags", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
response =
conn
|> put_req_header("accept", "application/json")
|> delete("/api/pleroma/admin/users/tag?nicknames[]=nickname&tags[]=foo&tags[]=bar")
assert json_response(response, :forbidden)
end
end
describe "/api/pleroma/admin/users/:nickname/permission_group" do
@ -271,21 +323,38 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
end
end
test "/api/pleroma/admin/users/:nickname/password_reset", %{conn: conn} do
user = insert(:user)
describe "/api/pleroma/admin/users/:nickname/password_reset" do
test "it returns a password reset link", %{conn: conn} do
clear_config([:instance, :admin_privileges], [:users_manage_credentials])
conn =
conn
|> put_req_header("accept", "application/json")
|> get("/api/pleroma/admin/users/#{user.nickname}/password_reset")
user = insert(:user)
resp = json_response(conn, 200)
conn =
conn
|> put_req_header("accept", "application/json")
|> get("/api/pleroma/admin/users/#{user.nickname}/password_reset")
assert Regex.match?(~r/(http:\/\/|https:\/\/)/, resp["link"])
resp = json_response(conn, 200)
assert Regex.match?(~r/(http:\/\/|https:\/\/)/, resp["link"])
end
test "it requires privileged role :users_manage_credentials", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
response =
conn
|> put_req_header("accept", "application/json")
|> get("/api/pleroma/admin/users/nickname/password_reset")
assert json_response(response, :forbidden)
end
end
describe "PUT disable_mfa" do
test "returns 200 and disable 2fa", %{conn: conn} do
clear_config([:instance, :admin_privileges], [:users_manage_credentials])
user =
insert(:user,
multi_factor_authentication_settings: %MFA.Settings{
@ -307,6 +376,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
end
test "returns 404 if user not found", %{conn: conn} do
clear_config([:instance, :admin_privileges], [:users_manage_credentials])
response =
conn
|> put("/api/pleroma/admin/users/disable_mfa", %{nickname: "nickname"})
@ -314,6 +385,16 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
assert response == %{"error" => "Not found"}
end
test "it requires privileged role :users_manage_credentials", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
response =
conn
|> put("/api/pleroma/admin/users/disable_mfa", %{nickname: "nickname"})
assert json_response(response, :forbidden)
end
end
describe "GET /api/pleroma/admin/restart" do
@ -344,6 +425,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
describe "GET /api/pleroma/admin/users/:nickname/statuses" do
setup do
clear_config([:instance, :admin_privileges], [:messages_read])
user = insert(:user)
insert(:note_activity, user: user)
@ -360,6 +443,14 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
assert length(activities) == 3
end
test "it requires privileged role :messages_read", %{conn: conn, user: user} do
clear_config([:instance, :admin_privileges], [])
conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses")
assert json_response(conn, :forbidden)
end
test "renders user's statuses with pagination", %{conn: conn, user: user} do
%{"total" => 3, "activities" => [activity1]} =
conn
@ -421,21 +512,32 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
describe "GET /api/pleroma/admin/users/:nickname/chats" do
setup do
clear_config([:instance, :admin_privileges], [:messages_read])
user = insert(:user)
%{user: user}
end
test "renders user's chats", %{conn: conn, user: user} do
recipients = insert_list(3, :user)
Enum.each(recipients, fn recipient ->
CommonAPI.post_chat_message(user, recipient, "yo")
end)
%{user: user}
end
test "renders user's chats", %{conn: conn, user: user} do
conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/chats")
assert json_response(conn, 200) |> length() == 3
end
test "it requires privileged role :messages_read", %{conn: conn, user: user} do
clear_config([:instance, :admin_privileges], [])
conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/chats")
assert json_response(conn, :forbidden)
end
end
describe "GET /api/pleroma/admin/users/:nickname/chats unauthorized" do
@ -471,6 +573,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
describe "GET /api/pleroma/admin/moderation_log" do
setup do
clear_config([:instance, :admin_privileges], [:moderation_log_read])
moderator = insert(:user, is_moderator: true)
%{moderator: moderator}
@ -675,6 +778,15 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
assert get_in(first_entry, ["data", "message"]) ==
"@#{moderator.nickname} unfollowed relay: https://example.org/relay"
end
test "it requires privileged role :moderation_log_read", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
assert conn
|> put_req_header("content-type", "multipart/form-data")
|> get("/api/pleroma/admin/moderation_log")
|> json_response(:forbidden)
end
end
test "gets a remote users when [:instance, :limit_to_local_content] is set to :unauthenticated",
@ -688,6 +800,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
describe "GET /users/:nickname/credentials" do
test "gets the user credentials", %{conn: conn} do
clear_config([:instance, :admin_privileges], [:users_manage_credentials])
user = insert(:user)
conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials")
@ -696,6 +809,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
end
test "returns 403 if requested by a non-admin" do
clear_config([:instance, :admin_privileges], [:users_manage_credentials])
user = insert(:user)
conn =
@ -705,6 +819,16 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
assert json_response(conn, :forbidden)
end
test "it requires privileged role :users_manage_credentials", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
response =
conn
|> get("/api/pleroma/admin/users/nickname/credentials")
assert json_response(response, :forbidden)
end
end
describe "PATCH /users/:nickname/credentials" do
@ -714,6 +838,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
end
test "changes password and email", %{conn: conn, admin: admin, user: user} do
clear_config([:instance, :admin_privileges], [:users_manage_credentials])
assert user.password_reset_pending == false
conn =
@ -756,6 +882,19 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
assert json_response(conn, :forbidden)
end
test "returns 403 if not privileged with :users_manage_credentials", %{conn: conn, user: user} do
clear_config([:instance, :admin_privileges], [])
conn =
patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
"password" => "new_password",
"email" => "new_email@example.com",
"name" => "new_name"
})
assert json_response(conn, :forbidden)
end
test "changes actor type from permitted list", %{conn: conn, user: user} do
assert user.actor_type == "Person"
@ -784,6 +923,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
describe "PATCH /users/:nickname/force_password_reset" do
test "sets password_reset_pending to true", %{conn: conn} do
clear_config([:instance, :admin_privileges], [:users_manage_credentials])
user = insert(:user)
assert user.password_reset_pending == false
@ -796,10 +936,21 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
assert User.get_by_id(user.id).password_reset_pending == true
end
test "it requires privileged role :users_manage_credentials", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
response =
conn
|> patch("/api/pleroma/admin/users/force_password_reset", %{nickname: "nickname"})
assert json_response(response, :forbidden)
end
end
describe "PATCH /confirm_email" do
test "it confirms emails of two users", %{conn: conn, admin: admin} do
clear_config([:instance, :admin_privileges], [:users_manage_credentials])
[first_user, second_user] = insert_pair(:user, is_confirmed: false)
refute first_user.is_confirmed
@ -826,10 +977,21 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
assert ModerationLog.get_log_entry_message(log_entry) ==
"@#{admin.nickname} confirmed email for users: @#{first_user.nickname}, @#{second_user.nickname}"
end
test "it requires privileged role :users_manage_credentials", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
response =
conn
|> patch("/api/pleroma/admin/users/confirm_email", %{nicknames: ["nickname"]})
assert json_response(response, :forbidden)
end
end
describe "PATCH /resend_confirmation_email" do
test "it resend emails for two users", %{conn: conn, admin: admin} do
clear_config([:instance, :admin_privileges], [:users_manage_credentials])
[first_user, second_user] = insert_pair(:user, is_confirmed: false)
ret_conn =
@ -855,9 +1017,23 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|> Swoosh.Email.put_private(:hackney_options, ssl_options: [versions: [:"tlsv1.2"]])
|> assert_email_sent()
end
test "it requires privileged role :users_manage_credentials", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
response =
conn
|> patch("/api/pleroma/admin/users/resend_confirmation_email", %{nicknames: ["nickname"]})
assert json_response(response, :forbidden)
end
end
describe "/api/pleroma/admin/stats" do
setup do
clear_config([:instance, :admin_privileges], [:statistics_read])
end
test "status visibility count", %{conn: conn} do
user = insert(:user)
CommonAPI.post(user, %{visibility: "public", status: "hey"})
@ -890,10 +1066,21 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
assert %{"direct" => 0, "private" => 1, "public" => 0, "unlisted" => 1} =
response["status_visibility"]
end
test "it requires privileged role :statistics_read", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
assert conn
|> get("/api/pleroma/admin/stats", instance: "lain.wired")
|> json_response(:forbidden)
end
end
describe "/api/pleroma/backups" do
test "it creates a backup", %{conn: conn} do
ConfigMock
|> Mox.stub_with(Pleroma.Config)
admin = %{id: admin_id, nickname: admin_nickname} = insert(:user, is_admin: true)
token = insert(:oauth_admin_token, user: admin)
user = %{id: user_id, nickname: user_nickname} = insert(:user)
@ -958,6 +1145,34 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
assert Repo.aggregate(Pleroma.User.Backup, :count) == 2
end
end
describe "POST /api/v1/pleroma/admin/reload_emoji" do
setup do
clear_config([:instance, :admin_privileges], [:emoji_manage_emoji])
admin = insert(:user, is_admin: true)
token = insert(:oauth_admin_token, user: admin)
conn =
build_conn()
|> assign(:user, admin)
|> assign(:token, token)
{:ok, %{conn: conn, admin: admin}}
end
test "it requires privileged role :emoji_manage_emoji", %{conn: conn} do
assert conn
|> post("/api/v1/pleroma/admin/reload_emoji")
|> json_response(200)
clear_config([:instance, :admin_privileges], [])
assert conn
|> post("/api/v1/pleroma/admin/reload_emoji")
|> json_response(:forbidden)
end
end
end
# Needed for testing

View file

@ -0,0 +1,375 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.AnnouncementControllerTest do
use Pleroma.Web.ConnCase, async: false
import Pleroma.Factory
setup do
clear_config([:instance, :admin_privileges], [:announcements_manage_announcements])
admin = insert(:user, is_admin: true)
token = insert(:oauth_admin_token, user: admin)
conn =
build_conn()
|> assign(:user, admin)
|> assign(:token, token)
{:ok, %{admin: admin, token: token, conn: conn}}
end
describe "GET /api/v1/pleroma/admin/announcements" do
test "it lists all announcements", %{conn: conn} do
%{id: id} = insert(:announcement)
response =
conn
|> get("/api/v1/pleroma/admin/announcements")
|> json_response_and_validate_schema(:ok)
assert [%{"id" => ^id}] = response
end
test "it requires privileged role :announcements_manage_announcements", %{conn: conn} do
conn
|> get("/api/v1/pleroma/admin/announcements")
|> json_response_and_validate_schema(:ok)
clear_config([:instance, :admin_privileges], [])
conn
|> get("/api/v1/pleroma/admin/announcements")
|> json_response(:forbidden)
end
test "it paginates announcements", %{conn: conn} do
_announcements = Enum.map(0..20, fn _ -> insert(:announcement) end)
response =
conn
|> get("/api/v1/pleroma/admin/announcements")
|> json_response_and_validate_schema(:ok)
assert length(response) == 20
end
test "it paginates announcements with custom params", %{conn: conn} do
announcements = Enum.map(0..20, fn _ -> insert(:announcement) end)
response =
conn
|> get("/api/v1/pleroma/admin/announcements", limit: 5, offset: 7)
|> json_response_and_validate_schema(:ok)
assert length(response) == 5
assert Enum.at(response, 0)["id"] == Enum.at(announcements, 7).id
end
test "it returns empty list with out-of-bounds offset", %{conn: conn} do
_announcements = Enum.map(0..20, fn _ -> insert(:announcement) end)
response =
conn
|> get("/api/v1/pleroma/admin/announcements", offset: 21)
|> json_response_and_validate_schema(:ok)
assert [] = response
end
test "it rejects invalid pagination params", %{conn: conn} do
conn
|> get("/api/v1/pleroma/admin/announcements", limit: 0)
|> json_response_and_validate_schema(400)
conn
|> get("/api/v1/pleroma/admin/announcements", limit: -1)
|> json_response_and_validate_schema(400)
conn
|> get("/api/v1/pleroma/admin/announcements", offset: -1)
|> json_response_and_validate_schema(400)
end
end
describe "GET /api/v1/pleroma/admin/announcements/:id" do
test "it displays one announcement", %{conn: conn} do
%{id: id} = insert(:announcement)
response =
conn
|> get("/api/v1/pleroma/admin/announcements/#{id}")
|> json_response_and_validate_schema(:ok)
assert %{"id" => ^id} = response
end
test "it requires privileged role :announcements_manage_announcements", %{conn: conn} do
%{id: id} = insert(:announcement)
conn
|> get("/api/v1/pleroma/admin/announcements/#{id}")
|> json_response_and_validate_schema(:ok)
clear_config([:instance, :admin_privileges], [])
conn
|> get("/api/v1/pleroma/admin/announcements/#{id}")
|> json_response(:forbidden)
end
test "it returns not found for non-existent id", %{conn: conn} do
%{id: id} = insert(:announcement)
_response =
conn
|> get("/api/v1/pleroma/admin/announcements/#{id}xxx")
|> json_response_and_validate_schema(:not_found)
end
end
describe "DELETE /api/v1/pleroma/admin/announcements/:id" do
test "it deletes specified announcement", %{conn: conn} do
%{id: id} = insert(:announcement)
_response =
conn
|> delete("/api/v1/pleroma/admin/announcements/#{id}")
|> json_response_and_validate_schema(:ok)
end
test "it requires privileged role :announcements_manage_announcements", %{conn: conn} do
%{id: id} = insert(:announcement)
conn
|> delete("/api/v1/pleroma/admin/announcements/#{id}")
|> json_response_and_validate_schema(:ok)
clear_config([:instance, :admin_privileges], [])
conn
|> delete("/api/v1/pleroma/admin/announcements/#{id}")
|> json_response(:forbidden)
end
test "it returns not found for non-existent id", %{conn: conn} do
%{id: id} = insert(:announcement)
_response =
conn
|> delete("/api/v1/pleroma/admin/announcements/#{id}xxx")
|> json_response_and_validate_schema(:not_found)
assert %{id: ^id} = Pleroma.Announcement.get_by_id(id)
end
end
describe "PATCH /api/v1/pleroma/admin/announcements/:id" do
test "it returns not found for non-existent id", %{conn: conn} do
%{id: id} = insert(:announcement)
_response =
conn
|> put_req_header("content-type", "application/json")
|> patch("/api/v1/pleroma/admin/announcements/#{id}xxx", %{})
|> json_response_and_validate_schema(:not_found)
assert %{id: ^id} = Pleroma.Announcement.get_by_id(id)
end
test "it updates a field", %{conn: conn} do
%{id: id} = insert(:announcement)
now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second)
starts_at = NaiveDateTime.add(now, -10, :second)
_response =
conn
|> put_req_header("content-type", "application/json")
|> patch("/api/v1/pleroma/admin/announcements/#{id}", %{
starts_at: NaiveDateTime.to_iso8601(starts_at)
})
|> json_response_and_validate_schema(:ok)
new = Pleroma.Announcement.get_by_id(id)
assert NaiveDateTime.compare(new.starts_at, starts_at) == :eq
end
test "it requires privileged role :announcements_manage_announcements", %{conn: conn} do
%{id: id} = insert(:announcement)
now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second)
starts_at = NaiveDateTime.add(now, -10, :second)
conn
|> put_req_header("content-type", "application/json")
|> patch("/api/v1/pleroma/admin/announcements/#{id}", %{
starts_at: NaiveDateTime.to_iso8601(starts_at)
})
|> json_response_and_validate_schema(:ok)
clear_config([:instance, :admin_privileges], [])
conn
|> put_req_header("content-type", "application/json")
|> patch("/api/v1/pleroma/admin/announcements/#{id}", %{
starts_at: NaiveDateTime.to_iso8601(starts_at)
})
|> json_response(:forbidden)
end
test "it updates with time with utc timezone", %{conn: conn} do
%{id: id} = insert(:announcement)
now = DateTime.now("Etc/UTC") |> elem(1) |> DateTime.truncate(:second)
starts_at = DateTime.add(now, -10, :second)
_response =
conn
|> put_req_header("content-type", "application/json")
|> patch("/api/v1/pleroma/admin/announcements/#{id}", %{
starts_at: DateTime.to_iso8601(starts_at)
})
|> json_response_and_validate_schema(:ok)
new = Pleroma.Announcement.get_by_id(id)
assert DateTime.compare(new.starts_at, starts_at) == :eq
end
test "it updates a data field", %{conn: conn} do
%{id: id} = announcement = insert(:announcement, data: %{"all_day" => true})
assert announcement.data["all_day"] == true
new_content = "new content"
response =
conn
|> put_req_header("content-type", "application/json")
|> patch("/api/v1/pleroma/admin/announcements/#{id}", %{
content: new_content
})
|> json_response_and_validate_schema(:ok)
assert response["content"] == new_content
assert response["all_day"] == true
new = Pleroma.Announcement.get_by_id(id)
assert new.data["content"] == new_content
assert new.data["all_day"] == true
end
test "it nullifies a nullable field", %{conn: conn} do
now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second)
starts_at = NaiveDateTime.add(now, -10, :second)
%{id: id} = insert(:announcement, starts_at: starts_at)
response =
conn
|> put_req_header("content-type", "application/json")
|> patch("/api/v1/pleroma/admin/announcements/#{id}", %{
starts_at: nil
})
|> json_response_and_validate_schema(:ok)
assert response["starts_at"] == nil
new = Pleroma.Announcement.get_by_id(id)
assert new.starts_at == nil
end
end
describe "POST /api/v1/pleroma/admin/announcements" do
test "it creates an announcement", %{conn: conn} do
content = "test post announcement api"
now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second)
starts_at = NaiveDateTime.add(now, -10, :second)
ends_at = NaiveDateTime.add(now, 10, :second)
response =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/pleroma/admin/announcements", %{
"content" => content,
"starts_at" => NaiveDateTime.to_iso8601(starts_at),
"ends_at" => NaiveDateTime.to_iso8601(ends_at),
"all_day" => true
})
|> json_response_and_validate_schema(:ok)
assert %{"content" => ^content, "all_day" => true} = response
announcement = Pleroma.Announcement.get_by_id(response["id"])
assert not is_nil(announcement)
assert NaiveDateTime.compare(announcement.starts_at, starts_at) == :eq
assert NaiveDateTime.compare(announcement.ends_at, ends_at) == :eq
end
test "it requires privileged role :announcements_manage_announcements", %{conn: conn} do
content = "test post announcement api"
now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second)
starts_at = NaiveDateTime.add(now, -10, :second)
ends_at = NaiveDateTime.add(now, 10, :second)
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/pleroma/admin/announcements", %{
"content" => content,
"starts_at" => NaiveDateTime.to_iso8601(starts_at),
"ends_at" => NaiveDateTime.to_iso8601(ends_at),
"all_day" => true
})
|> json_response_and_validate_schema(:ok)
clear_config([:instance, :admin_privileges], [])
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/pleroma/admin/announcements", %{
"content" => content,
"starts_at" => NaiveDateTime.to_iso8601(starts_at),
"ends_at" => NaiveDateTime.to_iso8601(ends_at),
"all_day" => true
})
|> json_response(:forbidden)
end
test "creating with time with utc timezones", %{conn: conn} do
content = "test post announcement api"
now = DateTime.now("Etc/UTC") |> elem(1) |> DateTime.truncate(:second)
starts_at = DateTime.add(now, -10, :second)
ends_at = DateTime.add(now, 10, :second)
response =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/pleroma/admin/announcements", %{
"content" => content,
"starts_at" => DateTime.to_iso8601(starts_at),
"ends_at" => DateTime.to_iso8601(ends_at),
"all_day" => true
})
|> json_response_and_validate_schema(:ok)
assert %{"content" => ^content, "all_day" => true} = response
announcement = Pleroma.Announcement.get_by_id(response["id"])
assert not is_nil(announcement)
assert DateTime.compare(announcement.starts_at, starts_at) == :eq
assert DateTime.compare(announcement.ends_at, ends_at) == :eq
end
end
end

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.ChatControllerTest do
use Pleroma.Web.ConnCase, async: true
use Pleroma.Web.ConnCase, async: false
import Pleroma.Factory
@ -27,7 +27,10 @@ defmodule Pleroma.Web.AdminAPI.ChatControllerTest do
end
describe "DELETE /api/pleroma/admin/chats/:id/messages/:message_id" do
setup do: admin_setup()
setup do
clear_config([:instance, :admin_privileges], [:messages_delete])
admin_setup()
end
test "it deletes a message from the chat", %{conn: conn, admin: admin} do
user = insert(:user)
@ -53,17 +56,29 @@ defmodule Pleroma.Web.AdminAPI.ChatControllerTest do
log_entry = Repo.one(ModerationLog)
assert ModerationLog.get_log_entry_message(log_entry) ==
"@#{admin.nickname} deleted chat message ##{cm_ref.id}"
"@#{admin.nickname} deleted chat message ##{message.id}"
assert result["id"] == cm_ref.id
refute MessageReference.get_by_id(cm_ref.id)
refute MessageReference.get_by_id(recipient_cm_ref.id)
assert %{data: %{"type" => "Tombstone"}} = Object.get_by_id(object.id)
end
test "it requires privileged role :messages_delete", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
assert conn
|> put_req_header("content-type", "application/json")
|> delete("/api/pleroma/admin/chats/some_id/messages/some_ref_id")
|> json_response(:forbidden)
end
end
describe "GET /api/pleroma/admin/chats/:id/messages" do
setup do: admin_setup()
setup do
clear_config([:instance, :admin_privileges], [:messages_read])
admin_setup()
end
test "it paginates", %{conn: conn} do
user = insert(:user)
@ -114,10 +129,21 @@ defmodule Pleroma.Web.AdminAPI.ChatControllerTest do
assert length(result) == 3
end
test "it requires privileged role :messages_read", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
conn = get(conn, "/api/pleroma/admin/chats/some_id/messages")
assert json_response(conn, :forbidden)
end
end
describe "GET /api/pleroma/admin/chats/:id" do
setup do: admin_setup()
setup do
clear_config([:instance, :admin_privileges], [:messages_read])
admin_setup()
end
test "it returns a chat", %{conn: conn} do
user = insert(:user)
@ -135,6 +161,14 @@ defmodule Pleroma.Web.AdminAPI.ChatControllerTest do
assert %{} = result["receiver"]
refute result["account"]
end
test "it requires privileged role :messages_read", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
conn = get(conn, "/api/pleroma/admin/chats/some_id")
assert json_response(conn, :forbidden)
end
end
describe "unauthorized chat moderation" do

View file

@ -316,15 +316,16 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do
assert Application.get_env(:idna, :key5) == {"string", Pleroma.Captcha.NotReal, []}
end
@tag capture_log: true
test "save configs setting without explicit key", %{conn: conn} do
level = Application.get_env(:quack, :level)
meta = Application.get_env(:quack, :meta)
webhook_url = Application.get_env(:quack, :webhook_url)
adapter = Application.get_env(:http, :adapter)
send_user_agent = Application.get_env(:http, :send_user_agent)
user_agent = Application.get_env(:http, :user_agent)
on_exit(fn ->
Application.put_env(:quack, :level, level)
Application.put_env(:quack, :meta, meta)
Application.put_env(:quack, :webhook_url, webhook_url)
Application.put_env(:http, :adapter, adapter)
Application.put_env(:http, :send_user_agent, send_user_agent)
Application.put_env(:http, :user_agent, user_agent)
end)
conn =
@ -333,19 +334,19 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do
|> post("/api/pleroma/admin/config", %{
configs: [
%{
group: ":quack",
key: ":level",
value: ":info"
group: ":http",
key: ":adapter",
value: [":someval"]
},
%{
group: ":quack",
key: ":meta",
value: [":none"]
group: ":http",
key: ":send_user_agent",
value: true
},
%{
group: ":quack",
key: ":webhook_url",
value: "https://hooks.slack.com/services/KEY"
group: ":http",
key: ":user_agent",
value: [":default"]
}
]
})
@ -353,30 +354,30 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do
assert json_response_and_validate_schema(conn, 200) == %{
"configs" => [
%{
"group" => ":quack",
"key" => ":level",
"value" => ":info",
"db" => [":level"]
"group" => ":http",
"key" => ":adapter",
"value" => [":someval"],
"db" => [":adapter"]
},
%{
"group" => ":quack",
"key" => ":meta",
"value" => [":none"],
"db" => [":meta"]
"group" => ":http",
"key" => ":send_user_agent",
"value" => true,
"db" => [":send_user_agent"]
},
%{
"group" => ":quack",
"key" => ":webhook_url",
"value" => "https://hooks.slack.com/services/KEY",
"db" => [":webhook_url"]
"group" => ":http",
"key" => ":user_agent",
"value" => [":default"],
"db" => [":user_agent"]
}
],
"need_reboot" => false
}
assert Application.get_env(:quack, :level) == :info
assert Application.get_env(:quack, :meta) == [:none]
assert Application.get_env(:quack, :webhook_url) == "https://hooks.slack.com/services/KEY"
assert Application.get_env(:http, :adapter) == [:someval]
assert Application.get_env(:http, :send_user_agent) == true
assert Application.get_env(:http, :user_agent) == [:default]
end
test "saving config with partial update", %{conn: conn} do
@ -1501,15 +1502,14 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do
clear_config(:database_config_whitelist, [
{:pleroma, :instance},
{:pleroma, :activitypub},
{:pleroma, Pleroma.Upload},
{:esshd}
{:pleroma, Pleroma.Upload}
])
conn = get(conn, "/api/pleroma/admin/config/descriptions")
children = json_response_and_validate_schema(conn, 200)
assert length(children) == 4
assert length(children) == 3
assert Enum.count(children, fn c -> c["group"] == ":pleroma" end) == 3
@ -1521,9 +1521,6 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do
web_endpoint = Enum.find(children, fn c -> c["key"] == "Pleroma.Upload" end)
assert web_endpoint["children"]
esshd = Enum.find(children, fn c -> c["group"] == ":esshd" end)
assert esshd["children"]
end
end
end

View file

@ -89,6 +89,7 @@ defmodule Pleroma.Web.AdminAPI.FrontendControllerTest do
"build_url" => "http://gensokyo.2hu/builds/${ref}",
"git" => nil,
"installed" => true,
"installed_refs" => ["fantasy"],
"name" => "pleroma",
"ref" => "fantasy"
}

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.InstanceControllerTest do
use Pleroma.Web.ConnCase
use Pleroma.Web.ConnCase, async: false
use Oban.Testing, repo: Pleroma.Repo
import Pleroma.Factory
@ -31,6 +31,7 @@ defmodule Pleroma.Web.AdminAPI.InstanceControllerTest do
end
test "GET /instances/:instance/statuses", %{conn: conn} do
clear_config([:instance, :admin_privileges], [:messages_read])
user = insert(:user, local: false, ap_id: "https://archae.me/users/archaeme")
user2 = insert(:user, local: false, ap_id: "https://test.com/users/test")
insert_pair(:note_activity, user: user)
@ -60,9 +61,14 @@ defmodule Pleroma.Web.AdminAPI.InstanceControllerTest do
|> json_response(200)
assert length(activities) == 3
clear_config([:instance, :admin_privileges], [])
conn |> get("/api/pleroma/admin/instances/archae.me/statuses") |> json_response(:forbidden)
end
test "DELETE /instances/:instance", %{conn: conn} do
clear_config([:instance, :admin_privileges], [:instances_delete])
user = insert(:user, nickname: "lain@lain.com")
post = insert(:note_activity, user: user)
@ -76,5 +82,11 @@ defmodule Pleroma.Web.AdminAPI.InstanceControllerTest do
assert response == "lain.com"
refute Repo.reload(user).is_active
refute Repo.reload(post)
clear_config([:instance, :admin_privileges], [])
conn
|> delete("/api/pleroma/admin/instances/lain.com")
|> json_response(:forbidden)
end
end

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.InstanceDocumentControllerTest do
use Pleroma.Web.ConnCase, async: true
use Pleroma.Web.ConnCase
import Pleroma.Factory
@dir "test/tmp/instance_static"

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.InviteControllerTest do
use Pleroma.Web.ConnCase, async: true
use Pleroma.Web.ConnCase, async: false
import Pleroma.Factory
@ -23,8 +23,25 @@ defmodule Pleroma.Web.AdminAPI.InviteControllerTest do
end
describe "POST /api/pleroma/admin/users/email_invite, with valid config" do
setup do: clear_config([:instance, :registrations_open], false)
setup do: clear_config([:instance, :invites_enabled], true)
setup do
clear_config([:instance, :registrations_open], false)
clear_config([:instance, :invites_enabled], true)
clear_config([:instance, :admin_privileges], [:users_manage_invites])
end
test "returns 403 if not privileged with :users_manage_invites", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
conn =
conn
|> put_req_header("content-type", "application/json;charset=utf-8")
|> post("/api/pleroma/admin/users/email_invite", %{
email: "foo@bar.com",
name: "J. D."
})
assert json_response(conn, :forbidden)
end
test "sends invitation and returns 204", %{admin: admin, conn: conn} do
recipient_email = "foo@bar.com"
@ -114,8 +131,11 @@ defmodule Pleroma.Web.AdminAPI.InviteControllerTest do
end
describe "POST /api/pleroma/admin/users/email_invite, with invalid config" do
setup do: clear_config([:instance, :registrations_open])
setup do: clear_config([:instance, :invites_enabled])
setup do
clear_config([:instance, :registrations_open])
clear_config([:instance, :invites_enabled])
clear_config([:instance, :admin_privileges], [:users_manage_invites])
end
test "it returns 500 if `invites_enabled` is not enabled", %{conn: conn} do
clear_config([:instance, :registrations_open], false)
@ -157,6 +177,21 @@ defmodule Pleroma.Web.AdminAPI.InviteControllerTest do
end
describe "POST /api/pleroma/admin/users/invite_token" do
setup do
clear_config([:instance, :admin_privileges], [:users_manage_invites])
end
test "returns 403 if not privileged with :users_manage_invites", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/admin/users/invite_token")
assert json_response(conn, :forbidden)
end
test "without options", %{conn: conn} do
conn =
conn
@ -221,6 +256,18 @@ defmodule Pleroma.Web.AdminAPI.InviteControllerTest do
end
describe "GET /api/pleroma/admin/users/invites" do
setup do
clear_config([:instance, :admin_privileges], [:users_manage_invites])
end
test "returns 403 if not privileged with :users_manage_invites", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
conn = get(conn, "/api/pleroma/admin/users/invites")
assert json_response(conn, :forbidden)
end
test "no invites", %{conn: conn} do
conn = get(conn, "/api/pleroma/admin/users/invites")
@ -249,6 +296,21 @@ defmodule Pleroma.Web.AdminAPI.InviteControllerTest do
end
describe "POST /api/pleroma/admin/users/revoke_invite" do
setup do
clear_config([:instance, :admin_privileges], [:users_manage_invites])
end
test "returns 403 if not privileged with :users_manage_invites", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/admin/users/revoke_invite", %{"token" => "foo"})
assert json_response(conn, :forbidden)
end
test "with token", %{conn: conn} do
{:ok, invite} = UserInviteToken.create_invite()

View file

@ -5,9 +5,11 @@
defmodule Pleroma.Web.AdminAPI.MediaProxyCacheControllerTest do
use Pleroma.Web.ConnCase
import Pleroma.Factory
import Mock
import Mox
import Pleroma.Factory
alias Pleroma.UnstubbedConfigMock, as: ConfigMock
alias Pleroma.Web.MediaProxy
setup do: clear_config([:media_proxy])
@ -128,6 +130,9 @@ defmodule Pleroma.Web.AdminAPI.MediaProxyCacheControllerTest do
"http://example.com/media/fb1f4d.jpg"
]
ConfigMock
|> stub_with(Pleroma.Test.StaticConfig)
with_mocks [
{MediaProxy.Invalidation.Script, [],
[
@ -150,6 +155,9 @@ defmodule Pleroma.Web.AdminAPI.MediaProxyCacheControllerTest do
"http://example.com/media/fb1f4d.jpg"
]
ConfigMock
|> stub_with(Pleroma.Test.StaticConfig)
with_mocks [{MediaProxy.Invalidation.Script, [], [purge: fn _, _ -> {"ok", 0} end]}] do
conn
|> put_req_header("content-type", "application/json")

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.ReportControllerTest do
use Pleroma.Web.ConnCase, async: true
use Pleroma.Web.ConnCase, async: false
import Pleroma.Factory
@ -27,6 +27,20 @@ defmodule Pleroma.Web.AdminAPI.ReportControllerTest do
end
describe "GET /api/pleroma/admin/reports/:id" do
setup do
clear_config([:instance, :admin_privileges], [:reports_manage_reports])
end
test "returns 403 if not privileged with :reports_manage_reports", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
conn =
conn
|> get("/api/pleroma/admin/reports/report_id")
assert json_response(conn, :forbidden)
end
test "returns report by its id", %{conn: conn} do
[reporter, target_user] = insert_pair(:user)
activity = insert(:note_activity, user: target_user)
@ -55,6 +69,32 @@ defmodule Pleroma.Web.AdminAPI.ReportControllerTest do
assert notes["content"] == "this is an admin note"
end
test "renders reported content even if the status is deleted", %{conn: conn} do
[reporter, target_user] = insert_pair(:user)
activity = insert(:note_activity, user: target_user)
activity = Activity.normalize(activity)
{:ok, %{id: report_id}} =
CommonAPI.report(reporter, %{
account_id: target_user.id,
comment: "I feel offended",
status_ids: [activity.id]
})
CommonAPI.delete(activity.id, target_user)
response =
conn
|> get("/api/pleroma/admin/reports/#{report_id}")
|> json_response_and_validate_schema(:ok)
assert response["id"] == report_id
assert [status] = response["statuses"]
assert activity.object.data["id"] == status["uri"]
assert activity.object.data["content"] == status["content"]
end
test "returns 404 when report id is invalid", %{conn: conn} do
conn = get(conn, "/api/pleroma/admin/reports/test")
@ -64,6 +104,8 @@ defmodule Pleroma.Web.AdminAPI.ReportControllerTest do
describe "PATCH /api/pleroma/admin/reports" do
setup do
clear_config([:instance, :admin_privileges], [:reports_manage_reports])
[reporter, target_user] = insert_pair(:user)
activity = insert(:note_activity, user: target_user)
@ -82,11 +124,30 @@ defmodule Pleroma.Web.AdminAPI.ReportControllerTest do
})
%{
reporter: reporter,
id: report_id,
second_report_id: second_report_id
}
end
test "returns 403 if not privileged with :reports_manage_reports", %{
conn: conn,
id: id,
admin: admin
} do
clear_config([:instance, :admin_privileges], [])
conn =
conn
|> assign(:token, insert(:oauth_token, user: admin, scopes: ["admin:write:reports"]))
|> put_req_header("content-type", "application/json")
|> patch("/api/pleroma/admin/reports", %{
"reports" => [%{"state" => "resolved", "id" => id}]
})
assert json_response(conn, :forbidden)
end
test "requires admin:write:reports scope", %{conn: conn, id: id, admin: admin} do
read_token = insert(:oauth_token, user: admin, scopes: ["admin:read"])
write_token = insert(:oauth_token, user: admin, scopes: ["admin:write:reports"])
@ -207,9 +268,43 @@ defmodule Pleroma.Web.AdminAPI.ReportControllerTest do
assert ModerationLog.get_log_entry_message(second_log_entry) ==
"@#{admin.nickname} updated report ##{second_report_id} (on user @#{second_activity.user_actor.nickname}) with 'closed' state"
end
test "works if reporter is deactivated", %{
conn: conn,
id: id,
reporter: reporter
} do
Pleroma.User.set_activation(reporter, false)
conn
|> put_req_header("content-type", "application/json")
|> patch("/api/pleroma/admin/reports", %{
"reports" => [
%{"state" => "resolved", "id" => id}
]
})
|> json_response_and_validate_schema(:no_content)
activity = Activity.get_by_id_with_user_actor(id)
assert activity.data["state"] == "resolved"
end
end
describe "GET /api/pleroma/admin/reports" do
setup do
clear_config([:instance, :admin_privileges], [:reports_manage_reports])
end
test "returns 403 if not privileged with :reports_manage_reports", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
conn =
conn
|> get(report_path(conn, :index))
assert json_response(conn, :forbidden)
end
test "returns empty response when no reports created", %{conn: conn} do
response =
conn
@ -293,6 +388,34 @@ defmodule Pleroma.Web.AdminAPI.ReportControllerTest do
|> json_response_and_validate_schema(:ok)
end
test "renders content correctly", %{conn: conn} do
[reporter, target_user] = insert_pair(:user)
note = insert(:note, user: target_user, data: %{"content" => "mew 1"})
note2 = insert(:note, user: target_user, data: %{"content" => "mew 2"})
activity = insert(:note_activity, user: target_user, note: note)
activity2 = insert(:note_activity, user: target_user, note: note2)
{:ok, _report} =
CommonAPI.report(reporter, %{
account_id: target_user.id,
comment: "I feel offended",
status_ids: [activity.id, activity2.id]
})
CommonAPI.delete(activity.id, target_user)
CommonAPI.delete(activity2.id, target_user)
response =
conn
|> get(report_path(conn, :index))
|> json_response_and_validate_schema(:ok)
assert [open_report] = response["reports"]
assert %{"statuses" => [s1, s2]} = open_report
assert "mew 1" in [s1["content"], s2["content"]]
assert "mew 2" in [s1["content"], s2["content"]]
end
test "returns 403 when requested by a non-admin" do
user = insert(:user)
token = insert(:oauth_token, user: user)
@ -346,6 +469,8 @@ defmodule Pleroma.Web.AdminAPI.ReportControllerTest do
describe "POST /api/pleroma/admin/reports/:id/notes" do
setup %{conn: conn, admin: admin} do
clear_config([:instance, :admin_privileges], [:reports_manage_reports])
[reporter, target_user] = insert_pair(:user)
activity = insert(:note_activity, user: target_user)
@ -374,6 +499,25 @@ defmodule Pleroma.Web.AdminAPI.ReportControllerTest do
}
end
test "returns 403 if not privileged with :reports_manage_reports", %{
conn: conn,
report_id: report_id
} do
clear_config([:instance, :admin_privileges], [])
post_conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/admin/reports/#{report_id}/notes", %{
content: "this is disgusting2!"
})
delete_conn = delete(conn, "/api/pleroma/admin/reports/#{report_id}/notes/note.id")
assert json_response(post_conn, :forbidden)
assert json_response(delete_conn, :forbidden)
end
test "it creates report note", %{admin_id: admin_id, report_id: report_id} do
assert [note, _] = Repo.all(ReportNote)

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.StatusControllerTest do
use Pleroma.Web.ConnCase, async: true
use Pleroma.Web.ConnCase, async: false
import Pleroma.Factory
@ -26,6 +26,10 @@ defmodule Pleroma.Web.AdminAPI.StatusControllerTest do
end
describe "GET /api/pleroma/admin/statuses/:id" do
setup do
clear_config([:instance, :admin_privileges], [:messages_read])
end
test "not found", %{conn: conn} do
assert conn
|> get("/api/pleroma/admin/statuses/not_found")
@ -50,10 +54,17 @@ defmodule Pleroma.Web.AdminAPI.StatusControllerTest do
assert account["is_active"] == actor.is_active
assert account["is_confirmed"] == actor.is_confirmed
end
test "denies reading activity when not privileged", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
assert conn |> get("/api/pleroma/admin/statuses/some_id") |> json_response(:forbidden)
end
end
describe "PUT /api/pleroma/admin/statuses/:id" do
setup do
clear_config([:instance, :admin_privileges], [:messages_delete])
activity = insert(:note_activity)
%{id: activity.id}
@ -122,10 +133,20 @@ defmodule Pleroma.Web.AdminAPI.StatusControllerTest do
assert %{"error" => "test - Invalid value for enum."} =
json_response_and_validate_schema(conn, :bad_request)
end
test "it requires privileged role :messages_delete", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
assert conn
|> put_req_header("content-type", "application/json")
|> put("/api/pleroma/admin/statuses/some_id", %{})
|> json_response(:forbidden)
end
end
describe "DELETE /api/pleroma/admin/statuses/:id" do
setup do
clear_config([:instance, :admin_privileges], [:messages_delete])
activity = insert(:note_activity)
%{id: activity.id}
@ -149,9 +170,22 @@ defmodule Pleroma.Web.AdminAPI.StatusControllerTest do
assert json_response_and_validate_schema(conn, :not_found) == %{"error" => "Not found"}
end
test "it requires privileged role :messages_delete", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
assert conn
|> put_req_header("content-type", "application/json")
|> delete("/api/pleroma/admin/statuses/some_id")
|> json_response(:forbidden)
end
end
describe "GET /api/pleroma/admin/statuses" do
setup do
clear_config([:instance, :admin_privileges], [:messages_read])
end
test "returns all public and unlisted statuses", %{conn: conn, admin: admin} do
blocked = insert(:user)
user = insert(:user)
@ -197,5 +231,13 @@ defmodule Pleroma.Web.AdminAPI.StatusControllerTest do
conn = get(conn, "/api/pleroma/admin/statuses?godmode=true")
assert json_response_and_validate_schema(conn, 200) |> length() == 3
end
test "it requires privileged role :messages_read", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
conn = get(conn, "/api/pleroma/admin/statuses")
assert json_response(conn, :forbidden)
end
end
end

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.UserControllerTest do
use Pleroma.Web.ConnCase
use Pleroma.Web.ConnCase, async: false
use Oban.Testing, repo: Pleroma.Repo
import Mock
@ -19,6 +19,11 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do
alias Pleroma.Web.Endpoint
alias Pleroma.Web.MediaProxy
setup do
Mox.stub_with(Pleroma.UnstubbedConfigMock, Pleroma.Config)
:ok
end
setup_all do
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
@ -38,6 +43,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do
end
test "with valid `admin_token` query parameter, skips OAuth scopes check" do
clear_config([:instance, :admin_privileges], [:users_read])
clear_config([:admin_token], "password123")
user = insert(:user)
@ -47,53 +53,10 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do
assert json_response_and_validate_schema(conn, 200)
end
test "GET /api/pleroma/admin/users/:nickname requires admin:read:accounts or broader scope",
%{admin: admin} do
user = insert(:user)
url = "/api/pleroma/admin/users/#{user.nickname}"
good_token1 = insert(:oauth_token, user: admin, scopes: ["admin"])
good_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read"])
good_token3 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts"])
bad_token1 = insert(:oauth_token, user: admin, scopes: ["read:accounts"])
bad_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts:partial"])
bad_token3 = nil
for good_token <- [good_token1, good_token2, good_token3] do
conn =
build_conn()
|> assign(:user, admin)
|> assign(:token, good_token)
|> get(url)
assert json_response_and_validate_schema(conn, 200)
end
for good_token <- [good_token1, good_token2, good_token3] do
conn =
build_conn()
|> assign(:user, nil)
|> assign(:token, good_token)
|> get(url)
assert json_response(conn, :forbidden)
end
for bad_token <- [bad_token1, bad_token2, bad_token3] do
conn =
build_conn()
|> assign(:user, admin)
|> assign(:token, bad_token)
|> get(url)
assert json_response_and_validate_schema(conn, :forbidden)
end
end
describe "DELETE /api/pleroma/admin/users" do
test "single user", %{admin: admin, conn: conn} do
clear_config([:instance, :federating], true)
clear_config([:instance, :admin_privileges], [:users_delete])
user =
insert(:user,
@ -149,6 +112,8 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do
end
test "multiple users", %{admin: admin, conn: conn} do
clear_config([:instance, :admin_privileges], [:users_delete])
user_one = insert(:user)
user_two = insert(:user)
@ -168,6 +133,17 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do
assert response -- [user_one.nickname, user_two.nickname] == []
end
test "Needs privileged role", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
response =
conn
|> put_req_header("accept", "application/json")
|> delete("/api/pleroma/admin/users?nickname=nickname")
assert json_response(response, :forbidden)
end
end
describe "/api/pleroma/admin/users" do
@ -307,7 +283,19 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do
end
end
describe "/api/pleroma/admin/users/:nickname" do
describe "GET /api/pleroma/admin/users/:nickname" do
setup do
clear_config([:instance, :admin_privileges], [:users_read])
end
test "returns 403 if not privileged with :users_read", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
conn = get(conn, "/api/pleroma/admin/users/user.nickname")
assert json_response(conn, :forbidden)
end
test "Show", %{conn: conn} do
user = insert(:user)
@ -323,6 +311,50 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do
assert %{"error" => "Not found"} == json_response_and_validate_schema(conn, 404)
end
test "requires admin:read:accounts or broader scope",
%{admin: admin} do
user = insert(:user)
url = "/api/pleroma/admin/users/#{user.nickname}"
good_token1 = insert(:oauth_token, user: admin, scopes: ["admin"])
good_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read"])
good_token3 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts"])
bad_token1 = insert(:oauth_token, user: admin, scopes: ["read:accounts"])
bad_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts:partial"])
bad_token3 = nil
for good_token <- [good_token1, good_token2, good_token3] do
conn =
build_conn()
|> assign(:user, admin)
|> assign(:token, good_token)
|> get(url)
assert json_response_and_validate_schema(conn, 200)
end
for good_token <- [good_token1, good_token2, good_token3] do
conn =
build_conn()
|> assign(:user, nil)
|> assign(:token, good_token)
|> get(url)
assert json_response(conn, :forbidden)
end
for bad_token <- [bad_token1, bad_token2, bad_token3] do
conn =
build_conn()
|> assign(:user, admin)
|> assign(:token, bad_token)
|> get(url)
assert json_response_and_validate_schema(conn, :forbidden)
end
end
end
describe "/api/pleroma/admin/users/follow" do
@ -378,6 +410,18 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do
end
describe "GET /api/pleroma/admin/users" do
setup do
clear_config([:instance, :admin_privileges], [:users_read])
end
test "returns 403 if not privileged with :users_read", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
conn = get(conn, "/api/pleroma/admin/users?page=1")
assert json_response(conn, :forbidden)
end
test "renders users array for the first page", %{conn: conn, admin: admin} do
user = insert(:user, local: false, tags: ["foo", "bar"])
user2 = insert(:user, is_approved: false, registration_reason: "I'm a chill dude")
@ -810,49 +854,9 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do
end
end
test "PATCH /api/pleroma/admin/users/activate", %{admin: admin, conn: conn} do
user_one = insert(:user, is_active: false)
user_two = insert(:user, is_active: false)
conn =
conn
|> put_req_header("content-type", "application/json")
|> patch(
"/api/pleroma/admin/users/activate",
%{nicknames: [user_one.nickname, user_two.nickname]}
)
response = json_response_and_validate_schema(conn, 200)
assert Enum.map(response["users"], & &1["is_active"]) == [true, true]
log_entry = Repo.one(ModerationLog)
assert ModerationLog.get_log_entry_message(log_entry) ==
"@#{admin.nickname} activated users: @#{user_one.nickname}, @#{user_two.nickname}"
end
test "PATCH /api/pleroma/admin/users/deactivate", %{admin: admin, conn: conn} do
user_one = insert(:user, is_active: true)
user_two = insert(:user, is_active: true)
conn =
conn
|> put_req_header("content-type", "application/json")
|> patch(
"/api/pleroma/admin/users/deactivate",
%{nicknames: [user_one.nickname, user_two.nickname]}
)
response = json_response_and_validate_schema(conn, 200)
assert Enum.map(response["users"], & &1["is_active"]) == [false, false]
log_entry = Repo.one(ModerationLog)
assert ModerationLog.get_log_entry_message(log_entry) ==
"@#{admin.nickname} deactivated users: @#{user_one.nickname}, @#{user_two.nickname}"
end
test "PATCH /api/pleroma/admin/users/approve", %{admin: admin, conn: conn} do
clear_config([:instance, :admin_privileges], [:users_manage_invites])
user_one = insert(:user, is_approved: false)
user_two = insert(:user, is_approved: false)
@ -873,6 +877,21 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do
"@#{admin.nickname} approved users: @#{user_one.nickname}, @#{user_two.nickname}"
end
test "PATCH /api/pleroma/admin/users/approve returns 403 if not privileged with :users_manage_invites",
%{conn: conn} do
clear_config([:instance, :admin_privileges], [])
conn =
conn
|> put_req_header("content-type", "application/json")
|> patch(
"/api/pleroma/admin/users/approve",
%{nicknames: ["user_one.nickname", "user_two.nickname"]}
)
assert json_response(conn, :forbidden)
end
test "PATCH /api/pleroma/admin/users/suggest", %{admin: admin, conn: conn} do
user1 = insert(:user, is_suggested: false)
user2 = insert(:user, is_suggested: false)
@ -923,24 +942,113 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do
"@#{admin.nickname} removed suggested users: @#{user1.nickname}, @#{user2.nickname}"
end
test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation", %{admin: admin, conn: conn} do
user = insert(:user)
describe "user activation" do
test "PATCH /api/pleroma/admin/users/activate", %{admin: admin, conn: conn} do
clear_config([:instance, :admin_privileges], [:users_manage_activation_state])
conn =
conn
|> put_req_header("content-type", "application/json")
|> patch("/api/pleroma/admin/users/#{user.nickname}/toggle_activation")
user_one = insert(:user, is_active: false)
user_two = insert(:user, is_active: false)
assert json_response_and_validate_schema(conn, 200) ==
user_response(
user,
%{"is_active" => !user.is_active}
)
conn =
conn
|> put_req_header("content-type", "application/json")
|> patch(
"/api/pleroma/admin/users/activate",
%{nicknames: [user_one.nickname, user_two.nickname]}
)
log_entry = Repo.one(ModerationLog)
response = json_response_and_validate_schema(conn, 200)
assert Enum.map(response["users"], & &1["is_active"]) == [true, true]
assert ModerationLog.get_log_entry_message(log_entry) ==
"@#{admin.nickname} deactivated users: @#{user.nickname}"
log_entry = Repo.one(ModerationLog)
assert ModerationLog.get_log_entry_message(log_entry) ==
"@#{admin.nickname} activated users: @#{user_one.nickname}, @#{user_two.nickname}"
end
test "PATCH /api/pleroma/admin/users/deactivate", %{admin: admin, conn: conn} do
clear_config([:instance, :admin_privileges], [:users_manage_activation_state])
user_one = insert(:user, is_active: true)
user_two = insert(:user, is_active: true)
conn =
conn
|> put_req_header("content-type", "application/json")
|> patch(
"/api/pleroma/admin/users/deactivate",
%{nicknames: [user_one.nickname, user_two.nickname]}
)
response = json_response_and_validate_schema(conn, 200)
assert Enum.map(response["users"], & &1["is_active"]) == [false, false]
log_entry = Repo.one(ModerationLog)
assert ModerationLog.get_log_entry_message(log_entry) ==
"@#{admin.nickname} deactivated users: @#{user_one.nickname}, @#{user_two.nickname}"
end
test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation", %{admin: admin, conn: conn} do
clear_config([:instance, :admin_privileges], [:users_manage_activation_state])
user = insert(:user)
conn =
conn
|> put_req_header("content-type", "application/json")
|> patch("/api/pleroma/admin/users/#{user.nickname}/toggle_activation")
assert json_response_and_validate_schema(conn, 200) ==
user_response(
user,
%{"is_active" => !user.is_active}
)
log_entry = Repo.one(ModerationLog)
assert ModerationLog.get_log_entry_message(log_entry) ==
"@#{admin.nickname} deactivated users: @#{user.nickname}"
end
test "it requires privileged role :statuses_activation to activate", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
conn =
conn
|> put_req_header("content-type", "application/json")
|> patch(
"/api/pleroma/admin/users/activate",
%{nicknames: ["user_one.nickname", "user_two.nickname"]}
)
assert json_response(conn, :forbidden)
end
test "it requires privileged role :statuses_activation to deactivate", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
conn =
conn
|> put_req_header("content-type", "application/json")
|> patch(
"/api/pleroma/admin/users/deactivate",
%{nicknames: ["user_one.nickname", "user_two.nickname"]}
)
assert json_response(conn, :forbidden)
end
test "it requires privileged role :statuses_activation to toggle activation", %{conn: conn} do
clear_config([:instance, :admin_privileges], [])
conn =
conn
|> put_req_header("content-type", "application/json")
|> patch("/api/pleroma/admin/users/user.nickname/toggle_activation")
assert json_response(conn, :forbidden)
end
end
defp user_response(user, attrs \\ %{}) do

View file

@ -0,0 +1,56 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2023 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.Scopes.CompilerTest do
use ExUnit.Case, async: true
alias Pleroma.Web.ApiSpec.Scopes.Compiler
@dummy_response %{}
@data %{
paths: %{
"/mew" => %OpenApiSpex.PathItem{
post: %OpenApiSpex.Operation{
security: [%{"oAuth" => ["a:b:c"]}],
responses: @dummy_response
},
get: %OpenApiSpex.Operation{security: nil, responses: @dummy_response}
},
"/mew2" => %OpenApiSpex.PathItem{
post: %OpenApiSpex.Operation{
security: [%{"oAuth" => ["d:e", "f:g"]}],
responses: @dummy_response
},
get: %OpenApiSpex.Operation{security: nil, responses: @dummy_response}
}
}
}
describe "process_scope/1" do
test "gives all higher-level scopes" do
scopes = Compiler.process_scope("admin:read:accounts")
assert [_, _, _] = scopes
assert "admin" in scopes
assert "admin:read" in scopes
assert "admin:read:accounts" in scopes
end
end
describe "extract_all_scopes_from/1" do
test "extracts scopes" do
scopes = Compiler.extract_all_scopes_from(@data)
assert [_, _, _, _, _, _, _] = scopes
assert "a" in scopes
assert "a:b" in scopes
assert "a:b:c" in scopes
assert "d" in scopes
assert "d:e" in scopes
assert "f" in scopes
assert "f:g" in scopes
end
end
end

View file

@ -0,0 +1,33 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.CommonAPI.ActivityDraftTest do
use Pleroma.DataCase
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.CommonAPI.ActivityDraft
import Pleroma.Factory
test "create/2 with a quote post" do
user = insert(:user)
another_user = insert(:user)
{:ok, direct} = CommonAPI.post(user, %{status: ".", visibility: "direct"})
{:ok, private} = CommonAPI.post(user, %{status: ".", visibility: "private"})
{:ok, unlisted} = CommonAPI.post(user, %{status: ".", visibility: "unlisted"})
{:ok, local} = CommonAPI.post(user, %{status: ".", visibility: "local"})
{:ok, public} = CommonAPI.post(user, %{status: ".", visibility: "public"})
{:error, _} = ActivityDraft.create(user, %{status: "nice", quote_id: direct.id})
{:ok, _} = ActivityDraft.create(user, %{status: "nice", quote_id: private.id})
{:error, _} = ActivityDraft.create(another_user, %{status: "nice", quote_id: private.id})
{:ok, _} = ActivityDraft.create(user, %{status: "nice", quote_id: unlisted.id})
{:ok, _} = ActivityDraft.create(another_user, %{status: "nice", quote_id: unlisted.id})
{:ok, _} = ActivityDraft.create(user, %{status: "nice", quote_id: local.id})
{:ok, _} = ActivityDraft.create(another_user, %{status: "nice", quote_id: local.id})
{:ok, _} = ActivityDraft.create(user, %{status: "nice", quote_id: public.id})
{:ok, _} = ActivityDraft.create(another_user, %{status: "nice", quote_id: public.id})
end
end

View file

@ -4,7 +4,6 @@
defmodule Pleroma.Web.CommonAPI.UtilsTest do
alias Pleroma.Builders.UserBuilder
alias Pleroma.Object
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.CommonAPI.ActivityDraft
alias Pleroma.Web.CommonAPI.Utils
@ -179,6 +178,10 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do
code = "https://github.com/pragdave/earmark/"
{result, [], []} = Utils.format_input(code, "text/markdown")
assert result == ~s[<p><a href="#{code}">#{code}</a></p>]
code = "https://github.com/~foo/bar"
{result, [], []} = Utils.format_input(code, "text/markdown")
assert result == ~s[<p><a href="#{code}">#{code}</a></p>]
end
test "link with local mention" do
@ -197,7 +200,7 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do
{result, _, []} = Utils.format_input(code, "text/markdown")
assert result ==
~s[<p><span class="h-card"><a class="u-url mention" data-user="#{mario.id}" href="#{mario.ap_id}" rel="ugc">@<span>mario</span></a></span> <span class="h-card"><a class="u-url mention" data-user="#{luigi.id}" href="#{luigi.ap_id}" rel="ugc">@<span>luigi</span></a></span> yo whats up?</p>]
~s[<p><span class="h-card"><a class="u-url mention" data-user="#{mario.id}" href="#{mario.ap_id}" rel="ugc">@<span>mario</span></a></span> <span class="h-card"><a class="u-url mention" data-user="#{luigi.id}" href="#{luigi.ap_id}" rel="ugc">@<span>luigi</span></a></span> yo what&#39;s up?</p>]
end
test "remote mentions" do
@ -208,7 +211,7 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do
{result, _, []} = Utils.format_input(code, "text/markdown")
assert result ==
~s[<p><span class="h-card"><a class="u-url mention" data-user="#{mario.id}" href="#{mario.ap_id}" rel="ugc">@<span>mario</span></a></span> <span class="h-card"><a class="u-url mention" data-user="#{luigi.id}" href="#{luigi.ap_id}" rel="ugc">@<span>luigi</span></a></span> yo whats up?</p>]
~s[<p><span class="h-card"><a class="u-url mention" data-user="#{mario.id}" href="#{mario.ap_id}" rel="ugc">@<span>mario</span></a></span> <span class="h-card"><a class="u-url mention" data-user="#{luigi.id}" href="#{luigi.ap_id}" rel="ugc">@<span>luigi</span></a></span> yo what&#39;s up?</p>]
end
test "raw HTML" do
@ -226,7 +229,7 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do
test "blockquote" do
code = ~s[> whoms't are you quoting?]
{result, [], []} = Utils.format_input(code, "text/markdown")
assert result == "<blockquote><p>whomst are you quoting?</p></blockquote>"
assert result == "<blockquote><p>whoms&#39;t are you quoting?</p></blockquote>"
end
test "code" do
@ -273,22 +276,6 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do
end
end
describe "context_to_conversation_id" do
test "creates a mapping object" do
conversation_id = Utils.context_to_conversation_id("random context")
object = Object.get_by_ap_id("random context")
assert conversation_id == object.id
end
test "returns an existing mapping for an existing object" do
{:ok, object} = Object.context_mapping("random context") |> Repo.insert()
conversation_id = Utils.context_to_conversation_id("random context")
assert conversation_id == object.id
end
end
describe "formats date to asctime" do
test "when date is in ISO 8601 format" do
date = DateTime.utc_now() |> DateTime.to_iso8601()
@ -517,17 +504,6 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do
end
end
describe "conversation_id_to_context/1" do
test "returns id" do
object = insert(:note)
assert Utils.conversation_id_to_context(object.id) == object.data["id"]
end
test "returns error if object not found" do
assert Utils.conversation_id_to_context("123") == {:error, "No such conversation"}
end
end
describe "maybe_notify_mentioned_recipients/2" do
test "returns recipients when activity is not `Create`" do
activity = insert(:like_activity)
@ -610,41 +586,61 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do
end
end
describe "attachments_from_ids_descs/2" do
describe "attachments_from_ids_descs/3" do
test "returns [] when attachment ids is empty" do
assert Utils.attachments_from_ids_descs([], "{}") == []
assert Utils.attachments_from_ids_descs([], "{}", nil) == []
end
test "returns list attachments with desc" do
object = insert(:note)
user = insert(:user)
object = insert(:attachment, %{user: user})
desc = Jason.encode!(%{object.id => "test-desc"})
assert Utils.attachments_from_ids_descs(["#{object.id}", "34"], desc) == [
assert Utils.attachments_from_ids_descs(["#{object.id}", "34"], desc, user) == [
Map.merge(object.data, %{"name" => "test-desc"})
]
end
end
describe "attachments_from_ids/1" do
describe "attachments_from_ids/2" do
test "returns attachments with descs" do
object = insert(:note)
user = insert(:user)
object = insert(:attachment, %{user: user})
desc = Jason.encode!(%{object.id => "test-desc"})
assert Utils.attachments_from_ids(%{
media_ids: ["#{object.id}"],
descriptions: desc
}) == [
assert Utils.attachments_from_ids(
%{
media_ids: ["#{object.id}"],
descriptions: desc
},
user
) == [
Map.merge(object.data, %{"name" => "test-desc"})
]
end
test "returns attachments without descs" do
object = insert(:note)
assert Utils.attachments_from_ids(%{media_ids: ["#{object.id}"]}) == [object.data]
user = insert(:user)
object = insert(:attachment, %{user: user})
assert Utils.attachments_from_ids(%{media_ids: ["#{object.id}"]}, user) == [object.data]
end
test "returns [] when not pass media_ids" do
assert Utils.attachments_from_ids(%{}) == []
assert Utils.attachments_from_ids(%{}, nil) == []
end
test "returns [] when media_ids not belong to current user" do
user = insert(:user)
user2 = insert(:user)
object = insert(:attachment, %{user: user})
assert Utils.attachments_from_ids(%{media_ids: ["#{object.id}"]}, user2) == []
end
test "checks that the object is of upload type" do
object = insert(:note)
assert Utils.attachments_from_ids(%{media_ids: ["#{object.id}"]}, nil) == []
end
end

View file

@ -4,7 +4,7 @@
defmodule Pleroma.Web.CommonAPITest do
use Oban.Testing, repo: Pleroma.Repo
use Pleroma.DataCase
use Pleroma.DataCase, async: false
alias Pleroma.Activity
alias Pleroma.Chat
@ -13,6 +13,7 @@ defmodule Pleroma.Web.CommonAPITest do
alias Pleroma.Object
alias Pleroma.Repo
alias Pleroma.Rule
alias Pleroma.UnstubbedConfigMock, as: ConfigMock
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Transmogrifier
@ -21,9 +22,10 @@ defmodule Pleroma.Web.CommonAPITest do
alias Pleroma.Web.CommonAPI
alias Pleroma.Workers.PollWorker
import Pleroma.Factory
import Mock
import Ecto.Query, only: [from: 2]
import Mock
import Mox
import Pleroma.Factory
require Pleroma.Constants
@ -32,6 +34,13 @@ defmodule Pleroma.Web.CommonAPITest do
:ok
end
setup do
ConfigMock
|> stub_with(Pleroma.Test.StaticConfig)
:ok
end
setup do: clear_config([:instance, :safe_dm_mentions])
setup do: clear_config([:instance, :limit])
setup do: clear_config([:instance, :max_pinned_statuses])
@ -62,9 +71,11 @@ defmodule Pleroma.Web.CommonAPITest do
describe "blocking" do
setup do
blocker = insert(:user)
blocked = insert(:user)
User.follow(blocker, blocked)
User.follow(blocked, blocker)
blocked = insert(:user, local: false)
CommonAPI.follow(blocker, blocked)
CommonAPI.follow(blocked, blocker)
CommonAPI.accept_follow_request(blocker, blocked)
CommonAPI.accept_follow_request(blocked, blocked)
%{blocker: blocker, blocked: blocked}
end
@ -73,6 +84,9 @@ defmodule Pleroma.Web.CommonAPITest do
with_mock Pleroma.Web.Federator,
publish: fn _ -> nil end do
assert User.get_follow_state(blocker, blocked) == :follow_accept
refute is_nil(Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(blocker, blocked))
assert {:ok, block} = CommonAPI.block(blocker, blocked)
assert block.local
@ -80,6 +94,11 @@ defmodule Pleroma.Web.CommonAPITest do
refute User.following?(blocker, blocked)
refute User.following?(blocked, blocker)
refute User.get_follow_state(blocker, blocked)
assert %{data: %{"state" => "reject"}} =
Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(blocker, blocked)
assert called(Pleroma.Web.Federator.publish(block))
end
end
@ -270,6 +289,24 @@ defmodule Pleroma.Web.CommonAPITest do
assert {:reject, "[KeywordPolicy] Matches with rejected keyword"} ==
CommonAPI.post_chat_message(author, recipient, "GNO/Linux")
end
test "it reject messages with attachments not belonging to user" do
author = insert(:user)
not_author = insert(:user)
recipient = author
attachment = insert(:attachment, %{user: not_author})
{:error, message} =
CommonAPI.post_chat_message(
author,
recipient,
"123",
media_id: attachment.id
)
assert message == :forbidden
end
end
describe "unblocking" do
@ -322,7 +359,7 @@ defmodule Pleroma.Web.CommonAPITest do
refute Activity.get_by_id(post.id)
end
test "it does not allow a user to delete their posts" do
test "it does not allow a user to delete posts from another user" do
user = insert(:user)
other_user = insert(:user)
@ -332,7 +369,8 @@ defmodule Pleroma.Web.CommonAPITest do
assert Activity.get_by_id(post.id)
end
test "it allows moderators to delete other user's posts" do
test "it allows privileged users to delete other user's posts" do
clear_config([:instance, :moderator_privileges], [:messages_delete])
user = insert(:user)
moderator = insert(:user, is_moderator: true)
@ -344,19 +382,20 @@ defmodule Pleroma.Web.CommonAPITest do
refute Activity.get_by_id(post.id)
end
test "it allows admins to delete other user's posts" do
test "it doesn't allow unprivileged mods or admins to delete other user's posts" do
clear_config([:instance, :admin_privileges], [])
clear_config([:instance, :moderator_privileges], [])
user = insert(:user)
moderator = insert(:user, is_admin: true)
moderator = insert(:user, is_moderator: true, is_admin: true)
{:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
assert {:ok, delete} = CommonAPI.delete(post.id, moderator)
assert delete.local
refute Activity.get_by_id(post.id)
assert {:error, "Could not delete"} = CommonAPI.delete(post.id, moderator)
assert Activity.get_by_id(post.id)
end
test "superusers deleting non-local posts won't federate the delete" do
test "privileged users deleting non-local posts won't federate the delete" do
clear_config([:instance, :admin_privileges], [:messages_delete])
# This is the user of the ingested activity
_user =
insert(:user,
@ -365,7 +404,7 @@ defmodule Pleroma.Web.CommonAPITest do
last_refreshed_at: NaiveDateTime.utc_now()
)
moderator = insert(:user, is_admin: true)
admin = insert(:user, is_admin: true)
data =
File.read!("test/fixtures/mastodon-post-activity.json")
@ -375,13 +414,27 @@ defmodule Pleroma.Web.CommonAPITest do
with_mock Pleroma.Web.Federator,
publish: fn _ -> nil end do
assert {:ok, delete} = CommonAPI.delete(post.id, moderator)
assert {:ok, delete} = CommonAPI.delete(post.id, admin)
assert delete.local
refute called(Pleroma.Web.Federator.publish(:_))
end
refute Activity.get_by_id(post.id)
end
test "it allows privileged users to delete banned user's posts" do
clear_config([:instance, :moderator_privileges], [:messages_delete])
user = insert(:user)
moderator = insert(:user, is_moderator: true)
{:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
User.set_activation(user, false)
assert {:ok, delete} = CommonAPI.delete(post.id, moderator)
assert delete.local
refute Activity.get_by_id(post.id)
end
end
test "favoriting race condition" do
@ -507,6 +560,36 @@ defmodule Pleroma.Web.CommonAPITest do
assert Object.tags(object) == ["2hu"]
end
test "zwnj is treated as word character" do
user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{status: "#ساٴين‌س"})
object = Object.normalize(activity, fetch: false)
assert Object.tags(object) == ["ساٴين‌س"]
end
test "allows lang attribute" do
user = insert(:user)
text = ~s{<span lang="en">something</span><p lang="diaetuitech_rpyhpgc">random</p>}
{:ok, activity} = CommonAPI.post(user, %{status: text, content_type: "text/html"})
object = Object.normalize(activity, fetch: false)
assert object.data["content"] == text
end
test "double dot in link is allowed" do
user = insert(:user)
text = "https://example.to/something..mp3"
{:ok, activity} = CommonAPI.post(user, %{status: text})
object = Object.normalize(activity, fetch: false)
assert object.data["content"] == "<a href=\"#{text}\" rel=\"ugc\">#{text}</a>"
end
test "it adds emoji in the object" do
user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{status: ":firefox:"})
@ -587,7 +670,7 @@ defmodule Pleroma.Web.CommonAPITest do
object = Object.normalize(activity, fetch: false)
assert object.data["content"] == "<p><b>2hu</b></p>alert(&#39;xss&#39;)"
assert object.data["source"] == post
assert object.data["source"]["content"] == post
end
test "it filters out obviously bad tags when accepting a post as Markdown" do
@ -604,7 +687,7 @@ defmodule Pleroma.Web.CommonAPITest do
object = Object.normalize(activity, fetch: false)
assert object.data["content"] == "<p><b>2hu</b></p>"
assert object.data["source"] == post
assert object.data["source"]["content"] == post
end
test "it does not allow replies to direct messages that are not direct messages themselves" do
@ -723,6 +806,65 @@ defmodule Pleroma.Web.CommonAPITest do
scheduled_at: expires_at
)
end
test "it allows quote posting" do
user = insert(:user)
{:ok, quoted} = CommonAPI.post(user, %{status: "Hello world"})
{:ok, quote_post} = CommonAPI.post(user, %{status: "nice post", quote_id: quoted.id})
quoted = Object.normalize(quoted)
quote_post = Object.normalize(quote_post)
assert quote_post.data["quoteUrl"] == quoted.data["id"]
# The OP is not mentioned
refute quoted.data["actor"] in quote_post.data["to"]
end
test "quote posting with explicit addressing doesn't mention the OP" do
user = insert(:user)
{:ok, quoted} = CommonAPI.post(user, %{status: "Hello world"})
{:ok, quote_post} =
CommonAPI.post(user, %{status: "nice post", quote_id: quoted.id, to: []})
assert Object.normalize(quote_post).data["to"] == [Pleroma.Constants.as_public()]
end
test "quote posting visibility" do
user = insert(:user)
another_user = insert(:user)
{:ok, direct} = CommonAPI.post(user, %{status: ".", visibility: "direct"})
{:ok, private} = CommonAPI.post(user, %{status: ".", visibility: "private"})
{:ok, unlisted} = CommonAPI.post(user, %{status: ".", visibility: "unlisted"})
{:ok, local} = CommonAPI.post(user, %{status: ".", visibility: "local"})
{:ok, public} = CommonAPI.post(user, %{status: ".", visibility: "public"})
{:error, _} = CommonAPI.post(user, %{status: "nice", quote_id: direct.id})
{:ok, _} = CommonAPI.post(user, %{status: "nice", quote_id: private.id})
{:error, _} = CommonAPI.post(another_user, %{status: "nice", quote_id: private.id})
{:ok, _} = CommonAPI.post(user, %{status: "nice", quote_id: unlisted.id})
{:ok, _} = CommonAPI.post(another_user, %{status: "nice", quote_id: unlisted.id})
{:ok, _} = CommonAPI.post(user, %{status: "nice", quote_id: local.id})
{:ok, _} = CommonAPI.post(another_user, %{status: "nice", quote_id: local.id})
{:ok, _} = CommonAPI.post(user, %{status: "nice", quote_id: public.id})
{:ok, _} = CommonAPI.post(another_user, %{status: "nice", quote_id: public.id})
end
test "it properly mentions punycode domain" do
user = insert(:user)
_mentioned_user =
insert(:user, ap_id: "https://xn--i2raa.com/users/yyy", nickname: "yyy@xn--i2raa.com")
{:ok, activity} =
CommonAPI.post(user, %{status: "hey @yyy@xn--i2raa.com", content_type: "text/markdown"})
assert "https://xn--i2raa.com/users/yyy" in Object.normalize(activity).data["to"]
end
end
describe "reactions" do
@ -1091,10 +1233,11 @@ defmodule Pleroma.Web.CommonAPITest do
target_user = insert(:user)
{:ok, activity} = CommonAPI.post(target_user, %{status: "foobar"})
activity = Activity.normalize(activity)
reporter_ap_id = reporter.ap_id
target_ap_id = target_user.ap_id
activity_ap_id = activity.data["id"]
reported_object_ap_id = activity.object.data["id"]
comment = "foobar"
report_data = %{
@ -1105,7 +1248,7 @@ defmodule Pleroma.Web.CommonAPITest do
note_obj = %{
"type" => "Note",
"id" => activity_ap_id,
"id" => reported_object_ap_id,
"content" => "foobar",
"published" => activity.object.data["published"],
"actor" => AccountView.render("show.json", %{user: target_user})
@ -1127,6 +1270,7 @@ defmodule Pleroma.Web.CommonAPITest do
test "updates report state" do
[reporter, target_user] = insert_pair(:user)
activity = insert(:note_activity, user: target_user)
object = Object.normalize(activity)
{:ok, %Activity{id: report_id}} =
CommonAPI.report(reporter, %{
@ -1139,10 +1283,36 @@ defmodule Pleroma.Web.CommonAPITest do
assert report.data["state"] == "resolved"
[reported_user, activity_id] = report.data["object"]
[reported_user, object_id] = report.data["object"]
assert reported_user == target_user.ap_id
assert activity_id == activity.data["id"]
assert object_id == object.data["id"]
end
test "updates report state, don't strip when report_strip_status is false" do
clear_config([:instance, :report_strip_status], false)
[reporter, target_user] = insert_pair(:user)
activity = insert(:note_activity, user: target_user)
{:ok, %Activity{id: report_id, data: report_data}} =
CommonAPI.report(reporter, %{
account_id: target_user.id,
comment: "I feel offended",
status_ids: [activity.id]
})
{:ok, report} = CommonAPI.update_report_state(report_id, "resolved")
assert report.data["state"] == "resolved"
[reported_user, reported_activity] = report.data["object"]
assert reported_user == target_user.ap_id
assert is_map(reported_activity)
assert reported_activity["content"] ==
report_data["object"] |> Enum.at(1) |> Map.get("content")
end
test "does not update report state when state is unsupported" do
@ -1297,7 +1467,7 @@ defmodule Pleroma.Web.CommonAPITest do
test "cancels a pending follow for a remote user" do
follower = insert(:user)
followed = insert(:user, is_locked: true, local: false, ap_enabled: true)
followed = insert(:user, is_locked: true, local: false)
assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} =
CommonAPI.follow(follower, followed)
@ -1569,4 +1739,128 @@ defmodule Pleroma.Web.CommonAPITest do
end
end
end
describe "update/3" do
test "updates a post" do
user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{status: "foo1", spoiler_text: "title 1"})
{:ok, updated} = CommonAPI.update(user, activity, %{status: "updated 2"})
updated_object = Object.normalize(updated)
assert updated_object.data["content"] == "updated 2"
assert Map.get(updated_object.data, "summary", "") == ""
assert Map.has_key?(updated_object.data, "updated")
end
test "does not change visibility" do
user = insert(:user)
{:ok, activity} =
CommonAPI.post(user, %{status: "foo1", spoiler_text: "title 1", visibility: "private"})
{:ok, updated} = CommonAPI.update(user, activity, %{status: "updated 2"})
updated_object = Object.normalize(updated)
assert updated_object.data["content"] == "updated 2"
assert Map.get(updated_object.data, "summary", "") == ""
assert Visibility.get_visibility(updated_object) == "private"
assert Visibility.get_visibility(updated) == "private"
end
test "updates a post with emoji" do
[{emoji1, _}, {emoji2, _} | _] = Pleroma.Emoji.get_all()
user = insert(:user)
{:ok, activity} =
CommonAPI.post(user, %{status: "foo1", spoiler_text: "title 1 :#{emoji1}:"})
{:ok, updated} = CommonAPI.update(user, activity, %{status: "updated 2 :#{emoji2}:"})
updated_object = Object.normalize(updated)
assert updated_object.data["content"] == "updated 2 :#{emoji2}:"
assert %{^emoji2 => _} = updated_object.data["emoji"]
end
test "updates a post with emoji and federate properly" do
[{emoji1, _}, {emoji2, _} | _] = Pleroma.Emoji.get_all()
user = insert(:user)
{:ok, activity} =
CommonAPI.post(user, %{status: "foo1", spoiler_text: "title 1 :#{emoji1}:"})
clear_config([:instance, :federating], true)
with_mock Pleroma.Web.Federator,
publish: fn _p -> nil end do
{:ok, updated} = CommonAPI.update(user, activity, %{status: "updated 2 :#{emoji2}:"})
assert updated.data["object"]["content"] == "updated 2 :#{emoji2}:"
assert %{^emoji2 => _} = updated.data["object"]["emoji"]
assert called(Pleroma.Web.Federator.publish(updated))
end
end
test "editing a post that copied a remote title with remote emoji should keep that emoji" do
remote_emoji_uri = "https://remote.org/emoji.png"
note =
insert(
:note,
data: %{
"summary" => ":remoteemoji:",
"emoji" => %{
"remoteemoji" => remote_emoji_uri
},
"tag" => [
%{
"type" => "Emoji",
"name" => "remoteemoji",
"icon" => %{"url" => remote_emoji_uri}
}
]
}
)
note_activity = insert(:note_activity, note: note)
user = insert(:user)
{:ok, reply} =
CommonAPI.post(user, %{
status: "reply",
spoiler_text: ":remoteemoji:",
in_reply_to_id: note_activity.id
})
assert reply.object.data["emoji"]["remoteemoji"] == remote_emoji_uri
{:ok, edit} =
CommonAPI.update(user, reply, %{status: "reply mew mew", spoiler_text: ":remoteemoji:"})
edited_note = Pleroma.Object.normalize(edit)
assert edited_note.data["emoji"]["remoteemoji"] == remote_emoji_uri
end
test "respects MRF" do
user = insert(:user)
clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.KeywordPolicy])
clear_config([:mrf_keyword, :replace], [{"updated", "mewmew"}])
{:ok, activity} = CommonAPI.post(user, %{status: "foo1", spoiler_text: "updated 1"})
assert Object.normalize(activity).data["summary"] == "mewmew 1"
{:ok, updated} = CommonAPI.update(user, activity, %{status: "updated 2"})
updated_object = Object.normalize(updated)
assert updated_object.data["content"] == "mewmew 2"
assert Map.get(updated_object.data, "summary", "") == ""
assert Map.has_key?(updated_object.data, "updated")
end
end
end

View file

@ -1,69 +0,0 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Endpoint.MetricsExporterTest do
# Modifies AppEnv, has to stay synchronous
use Pleroma.Web.ConnCase
alias Pleroma.Web.Endpoint.MetricsExporter
defp config do
Application.get_env(:prometheus, MetricsExporter)
end
describe "with default config" do
test "does NOT expose app metrics", %{conn: conn} do
conn
|> get(config()[:path])
|> json_response(404)
end
end
describe "when enabled" do
setup do
initial_config = config()
on_exit(fn -> Application.put_env(:prometheus, MetricsExporter, initial_config) end)
Application.put_env(
:prometheus,
MetricsExporter,
Keyword.put(initial_config, :enabled, true)
)
end
test "serves app metrics", %{conn: conn} do
conn = get(conn, config()[:path])
assert response = response(conn, 200)
for metric <- [
"http_requests_total",
"http_request_duration_microseconds",
"phoenix_controller_call_duration",
"telemetry_scrape_duration",
"erlang_vm_memory_atom_bytes_total"
] do
assert response =~ ~r/#{metric}/
end
end
test "when IP whitelist configured, " <>
"serves app metrics only if client IP is whitelisted",
%{conn: conn} do
Application.put_env(
:prometheus,
MetricsExporter,
Keyword.put(config(), :ip_whitelist, ["127.127.127.127", {1, 1, 1, 1}, '255.255.255.255'])
)
conn
|> get(config()[:path])
|> json_response(404)
conn
|> Map.put(:remote_ip, {127, 127, 127, 127})
|> get(config()[:path])
|> response(200)
end
end
end

View file

@ -6,20 +6,6 @@ defmodule Pleroma.Web.FallbackTest do
use Pleroma.Web.ConnCase
import Pleroma.Factory
describe "neither preloaded data nor metadata attached to" do
test "GET /registration/:token", %{conn: conn} do
response = get(conn, "/registration/foo")
assert html_response(response, 200) =~ "<!--server-generated-meta-->"
end
test "GET /*path", %{conn: conn} do
assert conn
|> get("/foo")
|> html_response(200) =~ "<!--server-generated-meta-->"
end
end
test "GET /*path adds a title", %{conn: conn} do
clear_config([:instance, :name], "a cool title")
@ -29,21 +15,28 @@ defmodule Pleroma.Web.FallbackTest do
end
describe "preloaded data and metadata attached to" do
test "GET /:maybe_nickname_or_id", %{conn: conn} do
test "GET /:maybe_nickname_or_id with existing user", %{conn: conn} do
clear_config([:instance, :name], "a cool title")
user = insert(:user)
resp = get(conn, "/#{user.nickname}")
assert html_response(resp, 200) =~ "<title>a cool title</title>"
refute html_response(resp, 200) =~ "<!--server-generated-meta-->"
assert html_response(resp, 200) =~ "initial-results"
end
test "GET /:maybe_nickname_or_id with missing user", %{conn: conn} do
clear_config([:instance, :name], "a cool title")
user = insert(:user)
user_missing = get(conn, "/foo")
user_present = get(conn, "/#{user.nickname}")
resp = get(conn, "/foo")
assert html_response(user_missing, 200) =~ "<!--server-generated-meta-->"
refute html_response(user_present, 200) =~ "<!--server-generated-meta-->"
assert html_response(user_present, 200) =~ "initial-results"
assert html_response(user_present, 200) =~ "<title>a cool title</title>"
assert html_response(resp, 200) =~ "<title>a cool title</title>"
refute html_response(resp, 200) =~ "initial-results"
end
test "GET /*path", %{conn: conn} do
assert conn
refute conn
|> get("/foo")
|> html_response(200) =~ "<!--server-generated-meta-->"
@ -65,10 +58,12 @@ defmodule Pleroma.Web.FallbackTest do
end
test "GET /main/all", %{conn: conn} do
clear_config([:instance, :name], "a cool title")
public_page = get(conn, "/main/all")
refute html_response(public_page, 200) =~ "<!--server-generated-meta-->"
assert html_response(public_page, 200) =~ "initial-results"
assert html_response(public_page, 200) =~ "<title>a cool title</title>"
end
end

Some files were not shown because too many files have changed in this diff Show more