Merge remote-tracking branch 'origin/develop' into gitlab-mr-iid-4161
This commit is contained in:
commit
7cc9ba6f06
1296 changed files with 27633 additions and 7387 deletions
|
|
@ -249,7 +249,7 @@ defmodule Pleroma.ActivityTest do
|
|||
{:ok, %{id: id, object: %{data: %{"id" => obj_id}}}} =
|
||||
Pleroma.Web.CommonAPI.post(user, %{status: "cofe"})
|
||||
|
||||
Pleroma.Web.CommonAPI.favorite(another, id)
|
||||
Pleroma.Web.CommonAPI.favorite(id, another)
|
||||
|
||||
assert obj_id
|
||||
|> Pleroma.Activity.Queries.by_object_id()
|
||||
|
|
@ -261,23 +261,27 @@ defmodule Pleroma.ActivityTest do
|
|||
test "add_by_params_query/3" do
|
||||
user = insert(:user)
|
||||
|
||||
note = insert(:note_activity, user: user)
|
||||
note_activity = insert(:note_activity, user: user)
|
||||
|
||||
insert(:add_activity, user: user, note: note)
|
||||
insert(:add_activity, user: user, note: note)
|
||||
insert(:add_activity, user: user, note_activity: note_activity)
|
||||
insert(:add_activity, user: user, note_activity: note_activity)
|
||||
insert(:add_activity, user: user)
|
||||
|
||||
assert Repo.aggregate(Activity, :count, :id) == 4
|
||||
assert Repo.aggregate(Activity, :count, :id) == 5
|
||||
|
||||
add_query =
|
||||
Activity.add_by_params_query(note.data["object"], user.ap_id, user.featured_address)
|
||||
Activity.add_by_params_query(
|
||||
note_activity.data["object"],
|
||||
user.ap_id,
|
||||
user.featured_address
|
||||
)
|
||||
|
||||
assert Repo.aggregate(add_query, :count, :id) == 2
|
||||
|
||||
Repo.delete_all(add_query)
|
||||
assert Repo.aggregate(add_query, :count, :id) == 0
|
||||
|
||||
assert Repo.aggregate(Activity, :count, :id) == 2
|
||||
assert Repo.aggregate(Activity, :count, :id) == 3
|
||||
end
|
||||
|
||||
describe "associated_object_id() sql function" do
|
||||
|
|
|
|||
|
|
@ -273,24 +273,28 @@ defmodule Pleroma.ConfigDBTest do
|
|||
end
|
||||
|
||||
test "sigil" do
|
||||
assert ConfigDB.to_elixir_types("~r[comp[lL][aA][iI][nN]er]") == ~r/comp[lL][aA][iI][nN]er/
|
||||
assert ConfigDB.to_elixir_types("~r[comp[lL][aA][iI][nN]er]").source ==
|
||||
~r/comp[lL][aA][iI][nN]er/.source
|
||||
end
|
||||
|
||||
test "link sigil" do
|
||||
assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/") == ~r/https:\/\/example.com/
|
||||
assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/").source ==
|
||||
~r/https:\/\/example.com/.source
|
||||
end
|
||||
|
||||
test "link sigil with um modifiers" do
|
||||
assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/um") ==
|
||||
~r/https:\/\/example.com/um
|
||||
assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/um").source ==
|
||||
~r/https:\/\/example.com/um.source
|
||||
end
|
||||
|
||||
test "link sigil with i modifier" do
|
||||
assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/i") == ~r/https:\/\/example.com/i
|
||||
assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/i").source ==
|
||||
~r/https:\/\/example.com/i.source
|
||||
end
|
||||
|
||||
test "link sigil with s modifier" do
|
||||
assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/s") == ~r/https:\/\/example.com/s
|
||||
assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/s").source ==
|
||||
~r/https:\/\/example.com/s.source
|
||||
end
|
||||
|
||||
test "raise if valid delimiter not found" do
|
||||
|
|
@ -460,11 +464,11 @@ defmodule Pleroma.ConfigDBTest do
|
|||
test "complex keyword with sigil" do
|
||||
assert ConfigDB.to_elixir_types([
|
||||
%{"tuple" => [":federated_timeline_removal", []]},
|
||||
%{"tuple" => [":reject", ["~r/comp[lL][aA][iI][nN]er/"]]},
|
||||
%{"tuple" => [":reject", [~r/comp[lL][aA][iI][nN]er/.source]]},
|
||||
%{"tuple" => [":replace", []]}
|
||||
]) == [
|
||||
federated_timeline_removal: [],
|
||||
reject: [~r/comp[lL][aA][iI][nN]er/],
|
||||
reject: [~r/comp[lL][aA][iI][nN]er/.source],
|
||||
replace: []
|
||||
]
|
||||
end
|
||||
|
|
|
|||
|
|
@ -332,7 +332,7 @@ defmodule Pleroma.Conversation.ParticipationTest do
|
|||
# When it's a reply from the blocked user
|
||||
{:ok, _direct2} =
|
||||
CommonAPI.post(blocked, %{
|
||||
status: "reply",
|
||||
status: "@#{third_user.nickname}, #{blocker.nickname} reply",
|
||||
visibility: "direct",
|
||||
in_reply_to_conversation_id: blocked_participation.id
|
||||
})
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ defmodule Pleroma.ConversationTest do
|
|||
setup_all do: clear_config([:instance, :federating], true)
|
||||
|
||||
setup do
|
||||
Mox.stub_with(Pleroma.UnstubbedConfigMock, Pleroma.Config)
|
||||
Mox.stub_with(Pleroma.UnstubbedConfigMock, Pleroma.Test.StaticConfig)
|
||||
:ok
|
||||
end
|
||||
|
||||
|
|
@ -66,8 +66,10 @@ defmodule Pleroma.ConversationTest do
|
|||
jafnhar = insert(:user, local: false)
|
||||
tridi = insert(:user)
|
||||
|
||||
to = [har.nickname, jafnhar.nickname, tridi.nickname]
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.post(har, %{status: "Hey @#{jafnhar.nickname}", visibility: "direct"})
|
||||
CommonAPI.post(har, %{status: "Hey @#{jafnhar.nickname}", visibility: "direct", to: to})
|
||||
|
||||
object = Pleroma.Object.normalize(activity, fetch: false)
|
||||
context = object.data["context"]
|
||||
|
|
@ -88,7 +90,8 @@ defmodule Pleroma.ConversationTest do
|
|||
CommonAPI.post(jafnhar, %{
|
||||
status: "Hey @#{har.nickname}",
|
||||
visibility: "direct",
|
||||
in_reply_to_status_id: activity.id
|
||||
in_reply_to_status_id: activity.id,
|
||||
to: to
|
||||
})
|
||||
|
||||
object = Pleroma.Object.normalize(activity, fetch: false)
|
||||
|
|
@ -112,7 +115,8 @@ defmodule Pleroma.ConversationTest do
|
|||
CommonAPI.post(tridi, %{
|
||||
status: "Hey @#{har.nickname}",
|
||||
visibility: "direct",
|
||||
in_reply_to_status_id: activity.id
|
||||
in_reply_to_status_id: activity.id,
|
||||
to: to
|
||||
})
|
||||
|
||||
object = Pleroma.Object.normalize(activity, fetch: false)
|
||||
|
|
|
|||
|
|
@ -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.EctoType.ActivityPub.ObjectValidators.ContentLanguageMapTest do
|
||||
use Pleroma.DataCase, async: true
|
||||
|
||||
alias Pleroma.EctoType.ActivityPub.ObjectValidators.ContentLanguageMap
|
||||
|
||||
test "it validates" do
|
||||
data = %{
|
||||
"en-US" => "mew mew",
|
||||
"en-GB" => "meow meow"
|
||||
}
|
||||
|
||||
assert {:ok, ^data} = ContentLanguageMap.cast(data)
|
||||
end
|
||||
|
||||
test "it validates empty strings" do
|
||||
data = %{
|
||||
"en-US" => "mew mew",
|
||||
"en-GB" => ""
|
||||
}
|
||||
|
||||
assert {:ok, ^data} = ContentLanguageMap.cast(data)
|
||||
end
|
||||
|
||||
test "it ignores non-strings within the map" do
|
||||
data = %{
|
||||
"en-US" => "mew mew",
|
||||
"en-GB" => 123
|
||||
}
|
||||
|
||||
assert {:ok, validated_data} = ContentLanguageMap.cast(data)
|
||||
|
||||
assert validated_data == %{"en-US" => "mew mew"}
|
||||
end
|
||||
|
||||
test "it ignores bad locale codes" do
|
||||
data = %{
|
||||
"en-US" => "mew mew",
|
||||
"en_GB" => "meow meow",
|
||||
"en<<#@!$#!@%!GB" => "meow meow"
|
||||
}
|
||||
|
||||
assert {:ok, validated_data} = ContentLanguageMap.cast(data)
|
||||
|
||||
assert validated_data == %{"en-US" => "mew mew"}
|
||||
end
|
||||
|
||||
test "it complains with non-map data" do
|
||||
assert :error = ContentLanguageMap.cast("mew")
|
||||
assert :error = ContentLanguageMap.cast(["mew"])
|
||||
assert :error = ContentLanguageMap.cast([%{"en-US" => "mew"}])
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
# 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.LanguageCodeTest do
|
||||
use Pleroma.DataCase, async: true
|
||||
|
||||
alias Pleroma.EctoType.ActivityPub.ObjectValidators.LanguageCode
|
||||
|
||||
test "it accepts language code" do
|
||||
text = "pl"
|
||||
assert {:ok, ^text} = LanguageCode.cast(text)
|
||||
end
|
||||
|
||||
test "it accepts language code with region" do
|
||||
text = "pl-PL"
|
||||
assert {:ok, ^text} = LanguageCode.cast(text)
|
||||
end
|
||||
|
||||
test "errors for invalid language code" do
|
||||
assert {:error, :invalid_language} = LanguageCode.cast("ru_RU")
|
||||
assert {:error, :invalid_language} = LanguageCode.cast(" ")
|
||||
assert {:error, :invalid_language} = LanguageCode.cast("en-US\n")
|
||||
end
|
||||
|
||||
test "errors for non-text" do
|
||||
assert :error == LanguageCode.cast(42)
|
||||
end
|
||||
end
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
defmodule Pleroma.Emoji.PackTest do
|
||||
use Pleroma.DataCase
|
||||
alias Pleroma.Emoji
|
||||
alias Pleroma.Emoji.Pack
|
||||
|
||||
@emoji_path Path.join(
|
||||
|
|
@ -12,6 +13,9 @@ defmodule Pleroma.Emoji.PackTest do
|
|||
)
|
||||
|
||||
setup do
|
||||
# Reload emoji to ensure a clean state
|
||||
Emoji.reload()
|
||||
|
||||
pack_path = Path.join(@emoji_path, "dump_pack")
|
||||
File.mkdir(pack_path)
|
||||
|
||||
|
|
@ -53,6 +57,63 @@ defmodule Pleroma.Emoji.PackTest do
|
|||
|
||||
assert updated_pack.files_count == 5
|
||||
end
|
||||
|
||||
test "skips existing emojis when adding from zip file", %{pack: pack} do
|
||||
# First, let's create a test pack with a "bear" emoji
|
||||
test_pack_path = Path.join(@emoji_path, "test_bear_pack")
|
||||
Pleroma.Backports.mkdir_p(test_pack_path)
|
||||
|
||||
# Create a pack.json file
|
||||
File.write!(Path.join(test_pack_path, "pack.json"), """
|
||||
{
|
||||
"files": { "bear": "bear.png" },
|
||||
"pack": {
|
||||
"description": "Bear Pack", "homepage": "https://pleroma.social",
|
||||
"license": "Test license", "share-files": true
|
||||
}}
|
||||
""")
|
||||
|
||||
# Copy a test image to use as the bear emoji
|
||||
File.cp!(
|
||||
Path.absname("test/instance_static/emoji/test_pack/blank.png"),
|
||||
Path.join(test_pack_path, "bear.png")
|
||||
)
|
||||
|
||||
# Load the pack to register the "bear" emoji in the global registry
|
||||
{:ok, _bear_pack} = Pleroma.Emoji.Pack.load_pack("test_bear_pack")
|
||||
|
||||
# Reload emoji to make sure the bear emoji is in the global registry
|
||||
Emoji.reload()
|
||||
|
||||
# Verify that the bear emoji exists in the global registry
|
||||
assert Emoji.exist?("bear")
|
||||
|
||||
# Now try to add a zip file that contains an emoji with the same shortcode
|
||||
file = %Plug.Upload{
|
||||
content_type: "application/zip",
|
||||
filename: "emojis.zip",
|
||||
path: Path.absname("test/fixtures/emojis.zip")
|
||||
}
|
||||
|
||||
{:ok, updated_pack} = Pack.add_file(pack, nil, nil, file)
|
||||
|
||||
# Verify that the "bear" emoji was skipped
|
||||
refute Map.has_key?(updated_pack.files, "bear")
|
||||
|
||||
# Other emojis should be added
|
||||
assert Map.has_key?(updated_pack.files, "a_trusted_friend-128")
|
||||
assert Map.has_key?(updated_pack.files, "auroraborealis")
|
||||
assert Map.has_key?(updated_pack.files, "baby_in_a_box")
|
||||
assert Map.has_key?(updated_pack.files, "bear-128")
|
||||
|
||||
# Total count should be 4 (all emojis except "bear")
|
||||
assert updated_pack.files_count == 4
|
||||
|
||||
# Clean up the test pack
|
||||
on_exit(fn ->
|
||||
File.rm_rf!(test_pack_path)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
test "returns error when zip file is bad", %{pack: pack} do
|
||||
|
|
@ -62,7 +123,7 @@ defmodule Pleroma.Emoji.PackTest do
|
|||
path: Path.absname("test/instance_static/emoji/test_pack/blank.png")
|
||||
}
|
||||
|
||||
assert Pack.add_file(pack, nil, nil, file) == {:error, :einval}
|
||||
assert {:error, _} = Pack.add_file(pack, nil, nil, file)
|
||||
end
|
||||
|
||||
test "returns pack when zip file is empty", %{pack: pack} do
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ defmodule Pleroma.FrontendTest do
|
|||
@dir "test/frontend_static_test"
|
||||
|
||||
setup do
|
||||
File.mkdir_p!(@dir)
|
||||
Pleroma.Backports.mkdir_p!(@dir)
|
||||
clear_config([:instance, :static_dir], @dir)
|
||||
|
||||
on_exit(fn ->
|
||||
|
|
@ -46,7 +46,7 @@ defmodule Pleroma.FrontendTest do
|
|||
|
||||
folder = Path.join([@dir, "frontends", "pleroma", "fantasy"])
|
||||
previously_existing = Path.join([folder, "temp"])
|
||||
File.mkdir_p!(folder)
|
||||
Pleroma.Backports.mkdir_p!(folder)
|
||||
File.write!(previously_existing, "yey")
|
||||
assert File.exists?(previously_existing)
|
||||
|
||||
|
|
|
|||
|
|
@ -14,4 +14,133 @@ defmodule Pleroma.HashtagTest do
|
|||
assert {:name, {"can't be blank", [validation: :required]}} in changeset.errors
|
||||
end
|
||||
end
|
||||
|
||||
describe "search_hashtags" do
|
||||
test "searches hashtags by partial match" do
|
||||
{:ok, _} = Hashtag.get_or_create_by_name("car")
|
||||
{:ok, _} = Hashtag.get_or_create_by_name("racecar")
|
||||
{:ok, _} = Hashtag.get_or_create_by_name("nascar")
|
||||
{:ok, _} = Hashtag.get_or_create_by_name("bicycle")
|
||||
|
||||
results = Hashtag.search("car")
|
||||
assert "car" in results
|
||||
assert "racecar" in results
|
||||
assert "nascar" in results
|
||||
refute "bicycle" in results
|
||||
|
||||
results = Hashtag.search("race")
|
||||
assert "racecar" in results
|
||||
refute "car" in results
|
||||
refute "nascar" in results
|
||||
refute "bicycle" in results
|
||||
|
||||
results = Hashtag.search("nonexistent")
|
||||
assert results == []
|
||||
end
|
||||
|
||||
test "searches hashtags by multiple words in query" do
|
||||
{:ok, _} = Hashtag.get_or_create_by_name("computer")
|
||||
{:ok, _} = Hashtag.get_or_create_by_name("laptop")
|
||||
{:ok, _} = Hashtag.get_or_create_by_name("desktop")
|
||||
{:ok, _} = Hashtag.get_or_create_by_name("phone")
|
||||
|
||||
# Search for "new computer" - should return "computer"
|
||||
results = Hashtag.search("new computer")
|
||||
assert "computer" in results
|
||||
refute "laptop" in results
|
||||
refute "desktop" in results
|
||||
refute "phone" in results
|
||||
|
||||
# Search for "computer laptop" - should return both
|
||||
results = Hashtag.search("computer laptop")
|
||||
assert "computer" in results
|
||||
assert "laptop" in results
|
||||
refute "desktop" in results
|
||||
refute "phone" in results
|
||||
|
||||
# Search for "new phone" - should return "phone"
|
||||
results = Hashtag.search("new phone")
|
||||
assert "phone" in results
|
||||
refute "computer" in results
|
||||
refute "laptop" in results
|
||||
refute "desktop" in results
|
||||
end
|
||||
|
||||
test "supports pagination" do
|
||||
{:ok, _} = Hashtag.get_or_create_by_name("alpha")
|
||||
{:ok, _} = Hashtag.get_or_create_by_name("beta")
|
||||
{:ok, _} = Hashtag.get_or_create_by_name("gamma")
|
||||
{:ok, _} = Hashtag.get_or_create_by_name("delta")
|
||||
|
||||
results = Hashtag.search("a", limit: 2)
|
||||
assert length(results) == 2
|
||||
|
||||
results = Hashtag.search("a", limit: 2, offset: 1)
|
||||
assert length(results) == 2
|
||||
end
|
||||
|
||||
test "handles matching many search terms" do
|
||||
{:ok, _} = Hashtag.get_or_create_by_name("computer")
|
||||
{:ok, _} = Hashtag.get_or_create_by_name("laptop")
|
||||
{:ok, _} = Hashtag.get_or_create_by_name("phone")
|
||||
{:ok, _} = Hashtag.get_or_create_by_name("tablet")
|
||||
|
||||
results = Hashtag.search("new fast computer laptop phone tablet device")
|
||||
assert "computer" in results
|
||||
assert "laptop" in results
|
||||
assert "phone" in results
|
||||
assert "tablet" in results
|
||||
end
|
||||
|
||||
test "ranks results by match quality" do
|
||||
{:ok, _} = Hashtag.get_or_create_by_name("my_computer")
|
||||
{:ok, _} = Hashtag.get_or_create_by_name("computer_science")
|
||||
{:ok, _} = Hashtag.get_or_create_by_name("computer")
|
||||
|
||||
results = Hashtag.search("computer")
|
||||
|
||||
# Exact match first
|
||||
assert Enum.at(results, 0) == "computer"
|
||||
|
||||
# Prefix match would be next
|
||||
assert Enum.at(results, 1) == "computer_science"
|
||||
|
||||
# worst match is last
|
||||
assert Enum.at(results, 2) == "my_computer"
|
||||
end
|
||||
|
||||
test "prioritizes shorter names when ranking is equal" do
|
||||
# Create hashtags with same ranking but different lengths
|
||||
{:ok, _} = Hashtag.get_or_create_by_name("car")
|
||||
{:ok, _} = Hashtag.get_or_create_by_name("racecar")
|
||||
{:ok, _} = Hashtag.get_or_create_by_name("nascar")
|
||||
|
||||
# Search for "car" - shorter names should come first
|
||||
results = Hashtag.search("car")
|
||||
# Shortest exact match first
|
||||
assert Enum.at(results, 0) == "car"
|
||||
assert "racecar" in results
|
||||
assert "nascar" in results
|
||||
end
|
||||
|
||||
test "handles hashtag symbols in search query" do
|
||||
{:ok, _} = Hashtag.get_or_create_by_name("computer")
|
||||
{:ok, _} = Hashtag.get_or_create_by_name("laptop")
|
||||
{:ok, _} = Hashtag.get_or_create_by_name("phone")
|
||||
|
||||
results_with_hash = Hashtag.search("#computer #laptop")
|
||||
results_without_hash = Hashtag.search("computer laptop")
|
||||
|
||||
assert results_with_hash == results_without_hash
|
||||
|
||||
results_mixed = Hashtag.search("#computer laptop #phone")
|
||||
assert "computer" in results_mixed
|
||||
assert "laptop" in results_mixed
|
||||
assert "phone" in results_mixed
|
||||
|
||||
results_only_hash = Hashtag.search("#computer")
|
||||
results_no_hash = Hashtag.search("computer")
|
||||
assert results_only_hash == results_no_hash
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -41,6 +41,10 @@ defmodule Pleroma.HTMLTest do
|
|||
<span class="h-card"><a class="u-url mention animate-spin">@<span>foo</span></a></span>
|
||||
"""
|
||||
|
||||
@mention_hashtags_sample """
|
||||
<a href="https://mastodon.example/tags/linux" class="mention hashtag" rel="tag">#<span>linux</span></a>
|
||||
"""
|
||||
|
||||
describe "StripTags scrubber" do
|
||||
test "works as expected" do
|
||||
expected = """
|
||||
|
|
@ -126,6 +130,15 @@ defmodule Pleroma.HTMLTest do
|
|||
Pleroma.HTML.Scrubber.TwitterText
|
||||
)
|
||||
end
|
||||
|
||||
test "does allow mention hashtags" do
|
||||
expected = """
|
||||
<a href="https://mastodon.example/tags/linux" class="mention hashtag" rel="tag">#<span>linux</span></a>
|
||||
"""
|
||||
|
||||
assert expected ==
|
||||
HTML.filter_tags(@mention_hashtags_sample, Pleroma.HTML.Scrubber.Default)
|
||||
end
|
||||
end
|
||||
|
||||
describe "default scrubber" do
|
||||
|
|
@ -189,6 +202,15 @@ defmodule Pleroma.HTMLTest do
|
|||
Pleroma.HTML.Scrubber.Default
|
||||
)
|
||||
end
|
||||
|
||||
test "does allow mention hashtags" do
|
||||
expected = """
|
||||
<a href="https://mastodon.example/tags/linux" class="mention hashtag" rel="tag">#<span>linux</span></a>
|
||||
"""
|
||||
|
||||
assert expected ==
|
||||
HTML.filter_tags(@mention_hashtags_sample, Pleroma.HTML.Scrubber.Default)
|
||||
end
|
||||
end
|
||||
|
||||
describe "extract_first_external_url_from_object" do
|
||||
|
|
|
|||
|
|
@ -16,6 +16,14 @@ defmodule Pleroma.HTTP.AdapterHelper.HackneyTest do
|
|||
describe "options/2" do
|
||||
setup do: clear_config([:http, :adapter], a: 1, b: 2)
|
||||
|
||||
test "uses redirect-safe defaults", %{uri: uri} do
|
||||
opts = Hackney.options([], uri)
|
||||
|
||||
assert opts[:follow_redirect] == false
|
||||
assert opts[:force_redirect] == false
|
||||
assert opts[:with_body] == true
|
||||
end
|
||||
|
||||
test "add proxy and opts from config", %{uri: uri} do
|
||||
opts = Hackney.options([proxy: "localhost:8123"], uri)
|
||||
|
||||
|
|
|
|||
355
test/pleroma/http/hackney_follow_redirect_regression_test.exs
Normal file
355
test/pleroma/http/hackney_follow_redirect_regression_test.exs
Normal file
|
|
@ -0,0 +1,355 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.HTTP.HackneyFollowRedirectRegressionTest do
|
||||
use ExUnit.Case, async: false
|
||||
|
||||
setup do
|
||||
{:ok, _} = Application.ensure_all_started(:hackney)
|
||||
|
||||
{:ok, tls_server} = start_tls_redirect_server()
|
||||
{:ok, proxy} = start_connect_proxy()
|
||||
|
||||
on_exit(fn ->
|
||||
stop_connect_proxy(proxy)
|
||||
stop_tls_redirect_server(tls_server)
|
||||
end)
|
||||
|
||||
{:ok, tls_server: tls_server, proxy: proxy}
|
||||
end
|
||||
|
||||
test "hackney follow_redirect crashes behind CONNECT proxy on relative redirects", %{
|
||||
tls_server: tls_server,
|
||||
proxy: proxy
|
||||
} do
|
||||
url = "#{tls_server.base_url}/redirect"
|
||||
|
||||
opts = [
|
||||
pool: :media,
|
||||
proxy: proxy.proxy_url,
|
||||
insecure: true,
|
||||
connect_timeout: 1_000,
|
||||
recv_timeout: 1_000,
|
||||
follow_redirect: true,
|
||||
force_redirect: true
|
||||
]
|
||||
|
||||
{pid, ref} = spawn_monitor(fn -> :hackney.request(:get, url, [], <<>>, opts) end)
|
||||
|
||||
assert_receive {:DOWN, ^ref, :process, ^pid, reason}, 5_000
|
||||
|
||||
assert match?({%FunctionClauseError{}, _}, reason) or match?(%FunctionClauseError{}, reason) or
|
||||
match?({:function_clause, _}, reason)
|
||||
end
|
||||
|
||||
test "redirects work via proxy when hackney follow_redirect is disabled", %{
|
||||
tls_server: tls_server,
|
||||
proxy: proxy
|
||||
} do
|
||||
url = "#{tls_server.base_url}/redirect"
|
||||
|
||||
adapter_opts = [
|
||||
pool: :media,
|
||||
proxy: proxy.proxy_url,
|
||||
insecure: true,
|
||||
connect_timeout: 1_000,
|
||||
recv_timeout: 1_000,
|
||||
follow_redirect: false,
|
||||
force_redirect: false,
|
||||
with_body: true
|
||||
]
|
||||
|
||||
client = Tesla.client([Tesla.Middleware.FollowRedirects], Tesla.Adapter.Hackney)
|
||||
|
||||
assert {:ok, %Tesla.Env{status: 200, body: "ok"}} =
|
||||
Tesla.request(client, method: :get, url: url, opts: [adapter: adapter_opts])
|
||||
end
|
||||
|
||||
test "reverse proxy hackney client follows redirects via proxy without crashing", %{
|
||||
tls_server: tls_server,
|
||||
proxy: proxy
|
||||
} do
|
||||
url = "#{tls_server.base_url}/redirect"
|
||||
|
||||
opts = [
|
||||
pool: :media,
|
||||
proxy: proxy.proxy_url,
|
||||
insecure: true,
|
||||
connect_timeout: 1_000,
|
||||
recv_timeout: 1_000,
|
||||
follow_redirect: true
|
||||
]
|
||||
|
||||
assert {:ok, 200, _headers, ref} =
|
||||
Pleroma.ReverseProxy.Client.Hackney.request(:get, url, [], "", opts)
|
||||
|
||||
assert collect_body(ref) == "ok"
|
||||
Pleroma.ReverseProxy.Client.Hackney.close(ref)
|
||||
end
|
||||
|
||||
defp collect_body(ref, acc \\ "") do
|
||||
case Pleroma.ReverseProxy.Client.Hackney.stream_body(ref) do
|
||||
:done -> acc
|
||||
{:ok, data, _ref} -> collect_body(ref, acc <> data)
|
||||
{:error, error} -> flunk("stream_body failed: #{inspect(error)}")
|
||||
end
|
||||
end
|
||||
|
||||
defp start_tls_redirect_server do
|
||||
certfile = Path.expand("../../fixtures/server.pem", __DIR__)
|
||||
keyfile = Path.expand("../../fixtures/private_key.pem", __DIR__)
|
||||
|
||||
{:ok, listener} =
|
||||
:ssl.listen(0, [
|
||||
:binary,
|
||||
certfile: certfile,
|
||||
keyfile: keyfile,
|
||||
reuseaddr: true,
|
||||
active: false,
|
||||
packet: :raw,
|
||||
ip: {127, 0, 0, 1}
|
||||
])
|
||||
|
||||
{:ok, {{127, 0, 0, 1}, port}} = :ssl.sockname(listener)
|
||||
|
||||
{:ok, acceptor} =
|
||||
Task.start_link(fn ->
|
||||
accept_tls_loop(listener)
|
||||
end)
|
||||
|
||||
{:ok, %{listener: listener, acceptor: acceptor, base_url: "https://127.0.0.1:#{port}"}}
|
||||
end
|
||||
|
||||
defp stop_tls_redirect_server(%{listener: listener, acceptor: acceptor}) do
|
||||
:ok = :ssl.close(listener)
|
||||
|
||||
if Process.alive?(acceptor) do
|
||||
Process.exit(acceptor, :normal)
|
||||
end
|
||||
end
|
||||
|
||||
defp accept_tls_loop(listener) do
|
||||
case :ssl.transport_accept(listener) do
|
||||
{:ok, socket} ->
|
||||
_ = Task.start(fn -> serve_tls(socket) end)
|
||||
accept_tls_loop(listener)
|
||||
|
||||
{:error, :closed} ->
|
||||
:ok
|
||||
|
||||
{:error, _reason} ->
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
defp serve_tls(tcp_socket) do
|
||||
with {:ok, ssl_socket} <- :ssl.handshake(tcp_socket, 2_000),
|
||||
{:ok, data} <- recv_ssl_headers(ssl_socket),
|
||||
{:ok, path} <- parse_path(data) do
|
||||
case path do
|
||||
"/redirect" ->
|
||||
send_ssl_response(ssl_socket, 302, "Found", [{"Location", "/final"}], "")
|
||||
|
||||
"/final" ->
|
||||
send_ssl_response(ssl_socket, 200, "OK", [], "ok")
|
||||
|
||||
_ ->
|
||||
send_ssl_response(ssl_socket, 404, "Not Found", [], "not found")
|
||||
end
|
||||
|
||||
:ssl.close(ssl_socket)
|
||||
else
|
||||
_ ->
|
||||
_ = :gen_tcp.close(tcp_socket)
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
defp recv_ssl_headers(socket, acc \\ <<>>) do
|
||||
case :ssl.recv(socket, 0, 1_000) do
|
||||
{:ok, data} ->
|
||||
acc = acc <> data
|
||||
|
||||
if :binary.match(acc, "\r\n\r\n") != :nomatch do
|
||||
{:ok, acc}
|
||||
else
|
||||
if byte_size(acc) > 8_192 do
|
||||
{:error, :too_large}
|
||||
else
|
||||
recv_ssl_headers(socket, acc)
|
||||
end
|
||||
end
|
||||
|
||||
{:error, _} = error ->
|
||||
error
|
||||
end
|
||||
end
|
||||
|
||||
defp send_ssl_response(socket, status, reason, headers, body) do
|
||||
base_headers =
|
||||
[
|
||||
{"Content-Length", Integer.to_string(byte_size(body))},
|
||||
{"Connection", "close"}
|
||||
] ++ headers
|
||||
|
||||
iodata =
|
||||
[
|
||||
"HTTP/1.1 ",
|
||||
Integer.to_string(status),
|
||||
" ",
|
||||
reason,
|
||||
"\r\n",
|
||||
Enum.map(base_headers, fn {k, v} -> [k, ": ", v, "\r\n"] end),
|
||||
"\r\n",
|
||||
body
|
||||
]
|
||||
|
||||
:ssl.send(socket, iodata)
|
||||
end
|
||||
|
||||
defp start_connect_proxy do
|
||||
{:ok, listener} =
|
||||
:gen_tcp.listen(0, [
|
||||
:binary,
|
||||
active: false,
|
||||
packet: :raw,
|
||||
reuseaddr: true,
|
||||
ip: {127, 0, 0, 1}
|
||||
])
|
||||
|
||||
{:ok, {{127, 0, 0, 1}, port}} = :inet.sockname(listener)
|
||||
|
||||
{:ok, acceptor} =
|
||||
Task.start_link(fn ->
|
||||
accept_proxy_loop(listener)
|
||||
end)
|
||||
|
||||
{:ok, %{listener: listener, acceptor: acceptor, proxy_url: "127.0.0.1:#{port}"}}
|
||||
end
|
||||
|
||||
defp stop_connect_proxy(%{listener: listener, acceptor: acceptor}) do
|
||||
:ok = :gen_tcp.close(listener)
|
||||
|
||||
if Process.alive?(acceptor) do
|
||||
Process.exit(acceptor, :normal)
|
||||
end
|
||||
end
|
||||
|
||||
defp accept_proxy_loop(listener) do
|
||||
case :gen_tcp.accept(listener) do
|
||||
{:ok, socket} ->
|
||||
_ = Task.start(fn -> serve_proxy(socket) end)
|
||||
accept_proxy_loop(listener)
|
||||
|
||||
{:error, :closed} ->
|
||||
:ok
|
||||
|
||||
{:error, _reason} ->
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
defp serve_proxy(client_socket) do
|
||||
with {:ok, {headers, rest}} <- recv_tcp_headers(client_socket),
|
||||
{:ok, {host, port}} <- parse_connect(headers),
|
||||
{:ok, upstream_socket} <- connect_upstream(host, port) do
|
||||
:gen_tcp.send(client_socket, "HTTP/1.1 200 Connection established\r\n\r\n")
|
||||
|
||||
if rest != <<>> do
|
||||
:gen_tcp.send(upstream_socket, rest)
|
||||
end
|
||||
|
||||
tunnel(client_socket, upstream_socket)
|
||||
else
|
||||
_ ->
|
||||
:gen_tcp.close(client_socket)
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
defp tunnel(client_socket, upstream_socket) do
|
||||
parent = self()
|
||||
_ = spawn_link(fn -> forward(client_socket, upstream_socket, parent) end)
|
||||
_ = spawn_link(fn -> forward(upstream_socket, client_socket, parent) end)
|
||||
|
||||
receive do
|
||||
:tunnel_closed -> :ok
|
||||
after
|
||||
10_000 -> :ok
|
||||
end
|
||||
|
||||
:gen_tcp.close(client_socket)
|
||||
:gen_tcp.close(upstream_socket)
|
||||
end
|
||||
|
||||
defp forward(from_socket, to_socket, parent) do
|
||||
case :gen_tcp.recv(from_socket, 0, 10_000) do
|
||||
{:ok, data} ->
|
||||
_ = :gen_tcp.send(to_socket, data)
|
||||
forward(from_socket, to_socket, parent)
|
||||
|
||||
{:error, _reason} ->
|
||||
send(parent, :tunnel_closed)
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
defp recv_tcp_headers(socket, acc \\ <<>>) do
|
||||
case :gen_tcp.recv(socket, 0, 1_000) do
|
||||
{:ok, data} ->
|
||||
acc = acc <> data
|
||||
|
||||
case :binary.match(acc, "\r\n\r\n") do
|
||||
:nomatch ->
|
||||
if byte_size(acc) > 8_192 do
|
||||
{:error, :too_large}
|
||||
else
|
||||
recv_tcp_headers(socket, acc)
|
||||
end
|
||||
|
||||
{idx, _len} ->
|
||||
split_at = idx + 4
|
||||
<<headers::binary-size(split_at), rest::binary>> = acc
|
||||
{:ok, {headers, rest}}
|
||||
end
|
||||
|
||||
{:error, _} = error ->
|
||||
error
|
||||
end
|
||||
end
|
||||
|
||||
defp parse_connect(data) do
|
||||
with [request_line | _] <- String.split(data, "\r\n", trim: true),
|
||||
["CONNECT", hostport | _] <- String.split(request_line, " ", parts: 3),
|
||||
[host, port_str] <- String.split(hostport, ":", parts: 2),
|
||||
{port, ""} <- Integer.parse(port_str) do
|
||||
{:ok, {host, port}}
|
||||
else
|
||||
_ -> {:error, :invalid_connect}
|
||||
end
|
||||
end
|
||||
|
||||
defp connect_upstream(host, port) do
|
||||
address =
|
||||
case :inet.parse_address(String.to_charlist(host)) do
|
||||
{:ok, ip} -> ip
|
||||
{:error, _} -> String.to_charlist(host)
|
||||
end
|
||||
|
||||
:gen_tcp.connect(address, port, [:binary, active: false, packet: :raw], 1_000)
|
||||
end
|
||||
|
||||
defp parse_path(data) do
|
||||
case String.split(data, "\r\n", parts: 2) do
|
||||
[request_line | _] ->
|
||||
case String.split(request_line, " ") do
|
||||
[_method, path, _protocol] -> {:ok, path}
|
||||
_ -> {:error, :invalid_request}
|
||||
end
|
||||
|
||||
_ ->
|
||||
{:error, :invalid_request}
|
||||
end
|
||||
end
|
||||
end
|
||||
151
test/pleroma/http/hackney_redirect_regression_test.exs
Normal file
151
test/pleroma/http/hackney_redirect_regression_test.exs
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.HTTP.HackneyRedirectRegressionTest do
|
||||
use ExUnit.Case, async: false
|
||||
|
||||
alias Pleroma.HTTP.AdapterHelper.Hackney, as: HackneyAdapterHelper
|
||||
|
||||
setup do
|
||||
{:ok, _} = Application.ensure_all_started(:hackney)
|
||||
|
||||
{:ok, server} = start_server()
|
||||
on_exit(fn -> stop_server(server) end)
|
||||
|
||||
{:ok, server: server}
|
||||
end
|
||||
|
||||
test "pooled redirects work with follow_redirect disabled", %{server: server} do
|
||||
url = "#{server.base_url}/redirect"
|
||||
uri = URI.parse(url)
|
||||
|
||||
adapter_opts =
|
||||
HackneyAdapterHelper.options(
|
||||
[pool: :media, follow_redirect: false, no_proxy_env: true],
|
||||
uri
|
||||
)
|
||||
|
||||
client = Tesla.client([Tesla.Middleware.FollowRedirects], Tesla.Adapter.Hackney)
|
||||
|
||||
assert {:ok, %Tesla.Env{status: 200, body: "ok"}} =
|
||||
Tesla.request(client, method: :get, url: url, opts: [adapter: adapter_opts])
|
||||
end
|
||||
|
||||
defp start_server do
|
||||
{:ok, listener} =
|
||||
:gen_tcp.listen(0, [
|
||||
:binary,
|
||||
active: false,
|
||||
packet: :raw,
|
||||
reuseaddr: true,
|
||||
ip: {127, 0, 0, 1}
|
||||
])
|
||||
|
||||
{:ok, {{127, 0, 0, 1}, port}} = :inet.sockname(listener)
|
||||
|
||||
{:ok, acceptor} =
|
||||
Task.start_link(fn ->
|
||||
accept_loop(listener)
|
||||
end)
|
||||
|
||||
{:ok, %{listener: listener, acceptor: acceptor, base_url: "http://127.0.0.1:#{port}"}}
|
||||
end
|
||||
|
||||
defp stop_server(%{listener: listener, acceptor: acceptor}) do
|
||||
:ok = :gen_tcp.close(listener)
|
||||
|
||||
if Process.alive?(acceptor) do
|
||||
Process.exit(acceptor, :normal)
|
||||
end
|
||||
end
|
||||
|
||||
defp accept_loop(listener) do
|
||||
case :gen_tcp.accept(listener) do
|
||||
{:ok, socket} ->
|
||||
serve(socket)
|
||||
accept_loop(listener)
|
||||
|
||||
{:error, :closed} ->
|
||||
:ok
|
||||
|
||||
{:error, _reason} ->
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
defp serve(socket) do
|
||||
with {:ok, data} <- recv_headers(socket),
|
||||
{:ok, path} <- parse_path(data) do
|
||||
case path do
|
||||
"/redirect" ->
|
||||
send_response(socket, 302, "Found", [{"Location", "/final"}], "")
|
||||
|
||||
"/final" ->
|
||||
send_response(socket, 200, "OK", [], "ok")
|
||||
|
||||
_ ->
|
||||
send_response(socket, 404, "Not Found", [], "not found")
|
||||
end
|
||||
else
|
||||
_ -> :ok
|
||||
end
|
||||
|
||||
:gen_tcp.close(socket)
|
||||
end
|
||||
|
||||
defp recv_headers(socket, acc \\ <<>>) do
|
||||
case :gen_tcp.recv(socket, 0, 1_000) do
|
||||
{:ok, data} ->
|
||||
acc = acc <> data
|
||||
|
||||
if :binary.match(acc, "\r\n\r\n") != :nomatch do
|
||||
{:ok, acc}
|
||||
else
|
||||
if byte_size(acc) > 8_192 do
|
||||
{:error, :too_large}
|
||||
else
|
||||
recv_headers(socket, acc)
|
||||
end
|
||||
end
|
||||
|
||||
{:error, _} = error ->
|
||||
error
|
||||
end
|
||||
end
|
||||
|
||||
defp parse_path(data) do
|
||||
case String.split(data, "\r\n", parts: 2) do
|
||||
[request_line | _] ->
|
||||
case String.split(request_line, " ") do
|
||||
[_method, path, _protocol] -> {:ok, path}
|
||||
_ -> {:error, :invalid_request}
|
||||
end
|
||||
|
||||
_ ->
|
||||
{:error, :invalid_request}
|
||||
end
|
||||
end
|
||||
|
||||
defp send_response(socket, status, reason, headers, body) do
|
||||
base_headers =
|
||||
[
|
||||
{"Content-Length", Integer.to_string(byte_size(body))},
|
||||
{"Connection", "close"}
|
||||
] ++ headers
|
||||
|
||||
iodata =
|
||||
[
|
||||
"HTTP/1.1 ",
|
||||
Integer.to_string(status),
|
||||
" ",
|
||||
reason,
|
||||
"\r\n",
|
||||
Enum.map(base_headers, fn {k, v} -> [k, ": ", v, "\r\n"] end),
|
||||
"\r\n",
|
||||
body
|
||||
]
|
||||
|
||||
:gen_tcp.send(socket, iodata)
|
||||
end
|
||||
end
|
||||
|
|
@ -3,10 +3,13 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.HTTPTest do
|
||||
use ExUnit.Case, async: true
|
||||
use ExUnit.Case, async: false
|
||||
use Pleroma.Tests.Helpers
|
||||
|
||||
import Tesla.Mock
|
||||
|
||||
alias Pleroma.HTTP
|
||||
alias Pleroma.Utils.URIEncoding
|
||||
|
||||
setup do
|
||||
mock(fn
|
||||
|
|
@ -25,6 +28,39 @@ defmodule Pleroma.HTTPTest do
|
|||
|
||||
%{method: :post, url: "http://example.com/world"} ->
|
||||
%Tesla.Env{status: 200, body: "world"}
|
||||
|
||||
%{method: :get, url: "https://example.com/emoji/Pack%201/koronebless.png?foo=bar+baz"} ->
|
||||
%Tesla.Env{status: 200, body: "emoji data"}
|
||||
|
||||
%{
|
||||
method: :get,
|
||||
url: "https://example.com/media/foo/bar%20!$&'()*+,;=/:%20@a%20%5Bbaz%5D.mp4"
|
||||
} ->
|
||||
%Tesla.Env{status: 200, body: "video data"}
|
||||
|
||||
%{method: :get, url: "https://example.com/media/unicode%20%F0%9F%99%82%20.gif"} ->
|
||||
%Tesla.Env{status: 200, body: "unicode data"}
|
||||
|
||||
%{
|
||||
method: :get,
|
||||
url:
|
||||
"https://i.guim.co.uk/img/media/1069ef13c447908272c4de94174cec2b6352cb2f/0_91_2000_1201/master/2000.jpg?width=1200&height=630&quality=85&auto=format&fit=crop&precrop=40:21,offset-x50,offset-y0&overlay-align=bottom%2Cleft&overlay-width=100p&overlay-base64=L2ltZy9zdGF0aWMvb3ZlcmxheXMvdGctb3BpbmlvbnMtYWdlLTIwMTkucG5n&enable=upscale&s=cba21427a73512fdc9863c486c03fdd8"
|
||||
} ->
|
||||
%Tesla.Env{status: 200, body: "Guardian image quirk"}
|
||||
|
||||
%{
|
||||
method: :get,
|
||||
url:
|
||||
"https://i.guim.co.uk/emoji/Pack%201/koronebless.png?precrop=40:21,overlay-x0,overlay-y0&foo=bar+baz"
|
||||
} ->
|
||||
%Tesla.Env{status: 200, body: "Space in query with Guardian quirk"}
|
||||
|
||||
%{
|
||||
method: :get,
|
||||
url:
|
||||
"https://examplebucket.s3.amazonaws.com/test.txt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=accessKEY%2F20130721%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20130721T201207Z&X-Amz-Expires=86400&X-Amz-Signature=SIGNATURE&X-Amz-SignedHeaders=host"
|
||||
} ->
|
||||
%Tesla.Env{status: 200, body: "AWS S3 data"}
|
||||
end)
|
||||
|
||||
:ok
|
||||
|
|
@ -67,4 +103,115 @@ defmodule Pleroma.HTTPTest do
|
|||
}
|
||||
end
|
||||
end
|
||||
|
||||
test "URL encoding properly encodes URLs with spaces" do
|
||||
clear_config(:test_url_encoding, true)
|
||||
|
||||
url_with_space = "https://example.com/emoji/Pack 1/koronebless.png?foo=bar baz"
|
||||
|
||||
{:ok, result} = HTTP.get(url_with_space)
|
||||
|
||||
assert result.status == 200
|
||||
|
||||
properly_encoded_url = "https://example.com/emoji/Pack%201/koronebless.png?foo=bar+baz"
|
||||
|
||||
{:ok, result} = HTTP.get(properly_encoded_url)
|
||||
|
||||
assert result.status == 200
|
||||
|
||||
url_with_reserved_chars = "https://example.com/media/foo/bar !$&'()*+,;=/: @a [baz].mp4"
|
||||
|
||||
{:ok, result} = HTTP.get(url_with_reserved_chars)
|
||||
|
||||
assert result.status == 200
|
||||
|
||||
url_with_unicode = "https://example.com/media/unicode 🙂 .gif"
|
||||
|
||||
{:ok, result} = HTTP.get(url_with_unicode)
|
||||
|
||||
assert result.status == 200
|
||||
end
|
||||
|
||||
test "decodes URL first by default" do
|
||||
clear_config(:test_url_encoding, true)
|
||||
|
||||
normal_url = "https://example.com/media/file%20with%20space.jpg?name=a+space.jpg"
|
||||
|
||||
result = URIEncoding.encode_url(normal_url)
|
||||
|
||||
assert result == "https://example.com/media/file%20with%20space.jpg?name=a+space.jpg"
|
||||
end
|
||||
|
||||
test "doesn't decode URL first when specified" do
|
||||
clear_config(:test_url_encoding, true)
|
||||
|
||||
normal_url = "https://example.com/media/file%20with%20space.jpg"
|
||||
|
||||
result = URIEncoding.encode_url(normal_url, bypass_decode: true)
|
||||
|
||||
assert result == "https://example.com/media/file%2520with%2520space.jpg"
|
||||
end
|
||||
|
||||
test "properly applies Guardian image query quirk" do
|
||||
clear_config(:test_url_encoding, true)
|
||||
|
||||
url =
|
||||
"https://i.guim.co.uk/img/media/1069ef13c447908272c4de94174cec2b6352cb2f/0_91_2000_1201/master/2000.jpg?width=1200&height=630&quality=85&auto=format&fit=crop&precrop=40:21,offset-x50,offset-y0&overlay-align=bottom%2Cleft&overlay-width=100p&overlay-base64=L2ltZy9zdGF0aWMvb3ZlcmxheXMvdGctb3BpbmlvbnMtYWdlLTIwMTkucG5n&enable=upscale&s=cba21427a73512fdc9863c486c03fdd8"
|
||||
|
||||
result = URIEncoding.encode_url(url)
|
||||
|
||||
assert result == url
|
||||
|
||||
{:ok, result_get} = HTTP.get(result)
|
||||
|
||||
assert result_get.status == 200
|
||||
end
|
||||
|
||||
test "properly encodes spaces as \"pluses\" in query when using quirks" do
|
||||
clear_config(:test_url_encoding, true)
|
||||
|
||||
url =
|
||||
"https://i.guim.co.uk/emoji/Pack 1/koronebless.png?precrop=40:21,overlay-x0,overlay-y0&foo=bar baz"
|
||||
|
||||
properly_encoded_url =
|
||||
"https://i.guim.co.uk/emoji/Pack%201/koronebless.png?precrop=40:21,overlay-x0,overlay-y0&foo=bar+baz"
|
||||
|
||||
result = URIEncoding.encode_url(url)
|
||||
|
||||
assert result == properly_encoded_url
|
||||
|
||||
{:ok, result_get} = HTTP.get(result)
|
||||
|
||||
assert result_get.status == 200
|
||||
end
|
||||
|
||||
test "properly encode AWS S3 queries" do
|
||||
clear_config(:test_url_encoding, true)
|
||||
|
||||
url =
|
||||
"https://examplebucket.s3.amazonaws.com/test.txt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=accessKEY%2F20130721%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20130721T201207Z&X-Amz-Expires=86400&X-Amz-Signature=SIGNATURE&X-Amz-SignedHeaders=host"
|
||||
|
||||
unencoded_url =
|
||||
"https://examplebucket.s3.amazonaws.com/test.txt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=accessKEY/20130721/us-east-1/s3/aws4_request&X-Amz-Date=20130721T201207Z&X-Amz-Expires=86400&X-Amz-Signature=SIGNATURE&X-Amz-SignedHeaders=host"
|
||||
|
||||
result = URIEncoding.encode_url(url)
|
||||
result_unencoded = URIEncoding.encode_url(unencoded_url)
|
||||
|
||||
assert result == url
|
||||
assert result == result_unencoded
|
||||
|
||||
{:ok, result_get} = HTTP.get(result)
|
||||
|
||||
assert result_get.status == 200
|
||||
end
|
||||
|
||||
test "preserves query key order" do
|
||||
clear_config(:test_url_encoding, true)
|
||||
|
||||
url = "https://example.com/foo?hjkl=qwertz&xyz=abc&bar=baz"
|
||||
|
||||
result = URIEncoding.encode_url(url)
|
||||
|
||||
assert result == url
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3,19 +3,15 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Instances.InstanceTest do
|
||||
alias Pleroma.Instances
|
||||
alias Pleroma.Instances.Instance
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.Tests.ObanHelpers
|
||||
alias Pleroma.Web.CommonAPI
|
||||
|
||||
use Oban.Testing, repo: Pleroma.Repo
|
||||
use Pleroma.DataCase
|
||||
|
||||
import ExUnit.CaptureLog
|
||||
import Pleroma.Factory
|
||||
|
||||
setup_all do: clear_config([:instance, :federation_reachability_timeout_days], 1)
|
||||
|
||||
describe "set_reachable/1" do
|
||||
test "clears `unreachable_since` of existing matching Instance record having non-nil `unreachable_since`" do
|
||||
unreachable_since = NaiveDateTime.to_iso8601(NaiveDateTime.utc_now())
|
||||
|
|
@ -31,6 +27,32 @@ defmodule Pleroma.Instances.InstanceTest do
|
|||
assert {:ok, instance} = Instance.set_reachable(instance.host)
|
||||
refute instance.unreachable_since
|
||||
end
|
||||
|
||||
test "cancels all ReachabilityWorker jobs for the domain" do
|
||||
domain = "cancelme.example.org"
|
||||
insert(:instance, host: domain, unreachable_since: NaiveDateTime.utc_now())
|
||||
|
||||
# Insert a ReachabilityWorker job for this domain, scheduled 5 minutes in the future
|
||||
scheduled_at = DateTime.add(DateTime.utc_now(), 300, :second)
|
||||
|
||||
{:ok, job} =
|
||||
Pleroma.Workers.ReachabilityWorker.new(
|
||||
%{"domain" => domain, "phase" => "phase_1min", "attempt" => 1},
|
||||
scheduled_at: scheduled_at
|
||||
)
|
||||
|> Oban.insert()
|
||||
|
||||
# Ensure the job is present
|
||||
job = Pleroma.Repo.get(Oban.Job, job.id)
|
||||
assert job
|
||||
|
||||
# Call set_reachable, which should delete the job
|
||||
assert {:ok, _} = Instance.set_reachable(domain)
|
||||
|
||||
# Reload the job and assert it is deleted
|
||||
job = Pleroma.Repo.get(Oban.Job, job.id)
|
||||
refute job
|
||||
end
|
||||
end
|
||||
|
||||
describe "set_unreachable/1" do
|
||||
|
|
@ -145,7 +167,11 @@ defmodule Pleroma.Instances.InstanceTest do
|
|||
end
|
||||
|
||||
test "Doesn't scrapes unreachable instances" do
|
||||
instance = insert(:instance, unreachable_since: Instances.reachability_datetime_threshold())
|
||||
instance =
|
||||
insert(:instance,
|
||||
unreachable_since: NaiveDateTime.utc_now() |> NaiveDateTime.add(-:timer.hours(24))
|
||||
)
|
||||
|
||||
url = "https://" <> instance.host
|
||||
|
||||
assert capture_log(fn -> assert nil == Instance.get_or_update_favicon(URI.parse(url)) end) =~
|
||||
|
|
@ -213,32 +239,44 @@ defmodule Pleroma.Instances.InstanceTest do
|
|||
end
|
||||
end
|
||||
|
||||
test "delete_users_and_activities/1 deletes remote instance users and activities" do
|
||||
[mario, luigi, _peach, wario] =
|
||||
users = [
|
||||
insert(:user, nickname: "mario@mushroom.kingdom", name: "Mario"),
|
||||
insert(:user, nickname: "luigi@mushroom.kingdom", name: "Luigi"),
|
||||
insert(:user, nickname: "peach@mushroom.kingdom", name: "Peach"),
|
||||
insert(:user, nickname: "wario@greedville.biz", name: "Wario")
|
||||
]
|
||||
test "delete/1 schedules a job to delete the instance and users" do
|
||||
insert(:user, nickname: "mario@mushroom.kingdom", name: "Mario")
|
||||
|
||||
{:ok, post1} = CommonAPI.post(mario, %{status: "letsa go!"})
|
||||
{:ok, post2} = CommonAPI.post(luigi, %{status: "itsa me... luigi"})
|
||||
{:ok, post3} = CommonAPI.post(wario, %{status: "WHA-HA-HA!"})
|
||||
{:ok, _job} = Instance.delete("mushroom.kingdom")
|
||||
|
||||
{:ok, job} = Instance.delete_users_and_activities("mushroom.kingdom")
|
||||
:ok = ObanHelpers.perform(job)
|
||||
assert_enqueued(
|
||||
worker: Pleroma.Workers.DeleteWorker,
|
||||
args: %{"op" => "delete_instance", "host" => "mushroom.kingdom"}
|
||||
)
|
||||
end
|
||||
|
||||
[mario, luigi, peach, wario] = Repo.reload(users)
|
||||
describe "check_unreachable/1" do
|
||||
test "schedules a ReachabilityWorker job for the given domain" do
|
||||
domain = "test.example.com"
|
||||
|
||||
refute mario.is_active
|
||||
refute luigi.is_active
|
||||
refute peach.is_active
|
||||
refute peach.name == "Peach"
|
||||
# Call check_unreachable
|
||||
assert {:ok, _job} = Instance.check_unreachable(domain)
|
||||
|
||||
assert wario.is_active
|
||||
assert wario.name == "Wario"
|
||||
# Verify that a ReachabilityWorker job was scheduled
|
||||
jobs = all_enqueued(worker: Pleroma.Workers.ReachabilityWorker)
|
||||
assert length(jobs) == 1
|
||||
[job] = jobs
|
||||
assert job.args["domain"] == domain
|
||||
end
|
||||
|
||||
assert [nil, nil, %{}] = Repo.reload([post1, post2, post3])
|
||||
test "handles multiple calls for the same domain (uniqueness enforced)" do
|
||||
domain = "duplicate.example.com"
|
||||
|
||||
assert {:ok, _job1} = Instance.check_unreachable(domain)
|
||||
|
||||
# Second call for the same domain
|
||||
assert {:ok, %Oban.Job{conflict?: true}} = Instance.check_unreachable(domain)
|
||||
|
||||
# Should only have one job due to uniqueness
|
||||
jobs = all_enqueued(worker: Pleroma.Workers.ReachabilityWorker)
|
||||
assert length(jobs) == 1
|
||||
[job] = jobs
|
||||
assert job.args["domain"] == domain
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -6,74 +6,42 @@ defmodule Pleroma.InstancesTest do
|
|||
alias Pleroma.Instances
|
||||
|
||||
use Pleroma.DataCase
|
||||
|
||||
setup_all do: clear_config([:instance, :federation_reachability_timeout_days], 1)
|
||||
use Oban.Testing, repo: Pleroma.Repo
|
||||
|
||||
describe "reachable?/1" do
|
||||
test "returns `true` for host / url with unknown reachability status" do
|
||||
assert Instances.reachable?("unknown.site")
|
||||
assert Instances.reachable?("http://unknown.site")
|
||||
end
|
||||
|
||||
test "returns `false` for host / url marked unreachable for at least `reachability_datetime_threshold()`" do
|
||||
host = "consistently-unreachable.name"
|
||||
Instances.set_consistently_unreachable(host)
|
||||
|
||||
refute Instances.reachable?(host)
|
||||
refute Instances.reachable?("http://#{host}/path")
|
||||
end
|
||||
|
||||
test "returns `true` for host / url marked unreachable for less than `reachability_datetime_threshold()`" do
|
||||
url = "http://eventually-unreachable.name/path"
|
||||
|
||||
Instances.set_unreachable(url)
|
||||
|
||||
assert Instances.reachable?(url)
|
||||
assert Instances.reachable?(URI.parse(url).host)
|
||||
end
|
||||
|
||||
test "raises FunctionClauseError exception on non-binary input" do
|
||||
assert_raise FunctionClauseError, fn -> Instances.reachable?(nil) end
|
||||
assert_raise FunctionClauseError, fn -> Instances.reachable?(1) end
|
||||
end
|
||||
end
|
||||
|
||||
describe "filter_reachable/1" do
|
||||
setup do
|
||||
host = "consistently-unreachable.name"
|
||||
url1 = "http://eventually-unreachable.com/path"
|
||||
url2 = "http://domain.com/path"
|
||||
unreachable_host = "consistently-unreachable.name"
|
||||
reachable_host = "http://domain.com/path"
|
||||
|
||||
Instances.set_consistently_unreachable(host)
|
||||
Instances.set_unreachable(url1)
|
||||
Instances.set_unreachable(unreachable_host)
|
||||
|
||||
result = Instances.filter_reachable([host, url1, url2, nil])
|
||||
%{result: result, url1: url1, url2: url2}
|
||||
result = Instances.filter_reachable([unreachable_host, reachable_host, nil])
|
||||
%{result: result, reachable_host: reachable_host, unreachable_host: unreachable_host}
|
||||
end
|
||||
|
||||
test "returns a map with keys containing 'not marked consistently unreachable' elements of supplied list",
|
||||
%{result: result, url1: url1, url2: url2} do
|
||||
assert is_map(result)
|
||||
assert Enum.sort([url1, url2]) == result |> Map.keys() |> Enum.sort()
|
||||
test "returns a list of only reachable elements",
|
||||
%{result: result, reachable_host: reachable_host} do
|
||||
assert is_list(result)
|
||||
assert [reachable_host] == result
|
||||
end
|
||||
|
||||
test "returns a map with `unreachable_since` values for keys",
|
||||
%{result: result, url1: url1, url2: url2} do
|
||||
assert is_map(result)
|
||||
assert %NaiveDateTime{} = result[url1]
|
||||
assert is_nil(result[url2])
|
||||
end
|
||||
|
||||
test "returns an empty map for empty list or list containing no hosts / url" do
|
||||
assert %{} == Instances.filter_reachable([])
|
||||
assert %{} == Instances.filter_reachable([nil])
|
||||
test "returns an empty list when provided no data" do
|
||||
assert [] == Instances.filter_reachable([])
|
||||
assert [] == Instances.filter_reachable([nil])
|
||||
end
|
||||
end
|
||||
|
||||
describe "set_reachable/1" do
|
||||
test "sets unreachable url or host reachable" do
|
||||
host = "domain.com"
|
||||
Instances.set_consistently_unreachable(host)
|
||||
Instances.set_unreachable(host)
|
||||
refute Instances.reachable?(host)
|
||||
|
||||
Instances.set_reachable(host)
|
||||
|
|
@ -103,22 +71,68 @@ defmodule Pleroma.InstancesTest do
|
|||
end
|
||||
end
|
||||
|
||||
describe "set_consistently_unreachable/1" do
|
||||
test "sets reachable url or host unreachable" do
|
||||
url = "http://domain.com?q="
|
||||
assert Instances.reachable?(url)
|
||||
describe "check_all_unreachable/0" do
|
||||
test "schedules ReachabilityWorker jobs for all unreachable instances" do
|
||||
domain1 = "unreachable1.example.com"
|
||||
domain2 = "unreachable2.example.com"
|
||||
domain3 = "unreachable3.example.com"
|
||||
|
||||
Instances.set_consistently_unreachable(url)
|
||||
refute Instances.reachable?(url)
|
||||
Instances.set_unreachable(domain1)
|
||||
Instances.set_unreachable(domain2)
|
||||
Instances.set_unreachable(domain3)
|
||||
|
||||
Instances.check_all_unreachable()
|
||||
|
||||
# Verify that ReachabilityWorker jobs were scheduled for all unreachable domains
|
||||
jobs = all_enqueued(worker: Pleroma.Workers.ReachabilityWorker)
|
||||
assert length(jobs) == 3
|
||||
|
||||
domains = Enum.map(jobs, & &1.args["domain"])
|
||||
assert domain1 in domains
|
||||
assert domain2 in domains
|
||||
assert domain3 in domains
|
||||
end
|
||||
|
||||
test "keeps unreachable url or host unreachable" do
|
||||
host = "site.name"
|
||||
Instances.set_consistently_unreachable(host)
|
||||
refute Instances.reachable?(host)
|
||||
test "does not schedule jobs for reachable instances" do
|
||||
unreachable_domain = "unreachable.example.com"
|
||||
reachable_domain = "reachable.example.com"
|
||||
|
||||
Instances.set_consistently_unreachable(host)
|
||||
refute Instances.reachable?(host)
|
||||
Instances.set_unreachable(unreachable_domain)
|
||||
Instances.set_reachable(reachable_domain)
|
||||
|
||||
Instances.check_all_unreachable()
|
||||
|
||||
# Verify that only one job was scheduled (for the unreachable domain)
|
||||
jobs = all_enqueued(worker: Pleroma.Workers.ReachabilityWorker)
|
||||
assert length(jobs) == 1
|
||||
[job] = jobs
|
||||
assert job.args["domain"] == unreachable_domain
|
||||
end
|
||||
end
|
||||
|
||||
test "delete_all_unreachable/0 schedules DeleteWorker jobs for all unreachable instances" do
|
||||
domain1 = "unreachable1.example.com"
|
||||
domain2 = "unreachable2.example.com"
|
||||
domain3 = "unreachable3.example.com"
|
||||
|
||||
Instances.set_unreachable(domain1)
|
||||
Instances.set_unreachable(domain2)
|
||||
Instances.set_unreachable(domain3)
|
||||
|
||||
Instances.delete_all_unreachable()
|
||||
|
||||
# Verify that DeleteWorker jobs were scheduled for all unreachable domains
|
||||
jobs = all_enqueued(worker: Pleroma.Workers.DeleteWorker)
|
||||
assert length(jobs) == 3
|
||||
|
||||
domains = Enum.map(jobs, & &1.args["host"])
|
||||
assert domain1 in domains
|
||||
assert domain2 in domains
|
||||
assert domain3 in domains
|
||||
|
||||
# Verify all jobs are delete_instance operations
|
||||
Enum.each(jobs, fn job ->
|
||||
assert job.args["op"] == "delete_instance"
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -268,6 +268,17 @@ defmodule Pleroma.Integration.MastodonWebsocketTest do
|
|||
end)
|
||||
end
|
||||
|
||||
test "accepts valid token on Sec-WebSocket-Protocol header", %{token: token} do
|
||||
assert {:ok, _} = start_socket("?stream=user", [{"Sec-WebSocket-Protocol", token.token}])
|
||||
|
||||
capture_log(fn ->
|
||||
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()
|
||||
|
||||
|
|
@ -352,7 +363,7 @@ defmodule Pleroma.Integration.MastodonWebsocketTest do
|
|||
test "accepts the 'list' stream", %{token: token, user: user} do
|
||||
posting_user = insert(:user)
|
||||
|
||||
{:ok, list} = Pleroma.List.create("test", user)
|
||||
{:ok, list} = Pleroma.List.create(%{title: "test"}, user)
|
||||
Pleroma.List.follow(list, posting_user)
|
||||
|
||||
assert {:ok, _} = start_socket("?stream=list&access_token=#{token.token}&list=#{list.id}")
|
||||
|
|
@ -404,7 +415,7 @@ defmodule Pleroma.Integration.MastodonWebsocketTest do
|
|||
|
||||
test "receives private statuses", %{user: reading_user, token: token} do
|
||||
user = insert(:user)
|
||||
CommonAPI.follow(reading_user, user)
|
||||
CommonAPI.follow(user, reading_user)
|
||||
|
||||
{:ok, _} = start_socket("?stream=user&access_token=#{token.token}")
|
||||
|
||||
|
|
@ -431,7 +442,7 @@ defmodule Pleroma.Integration.MastodonWebsocketTest do
|
|||
|
||||
test "receives edits", %{user: reading_user, token: token} do
|
||||
user = insert(:user)
|
||||
CommonAPI.follow(reading_user, user)
|
||||
CommonAPI.follow(user, reading_user)
|
||||
|
||||
{:ok, _} = start_socket("?stream=user&access_token=#{token.token}")
|
||||
|
||||
|
|
@ -440,7 +451,7 @@ defmodule Pleroma.Integration.MastodonWebsocketTest do
|
|||
|
||||
assert_receive {:text, _raw_json}, 1_000
|
||||
|
||||
{:ok, _} = CommonAPI.update(user, activity, %{status: "mew mew", visibility: "private"})
|
||||
{:ok, _} = CommonAPI.update(activity, user, %{status: "mew mew", visibility: "private"})
|
||||
|
||||
assert_receive {:text, raw_json}, 1_000
|
||||
|
||||
|
|
@ -459,7 +470,7 @@ defmodule Pleroma.Integration.MastodonWebsocketTest do
|
|||
|
||||
test "receives notifications", %{user: reading_user, token: token} do
|
||||
user = insert(:user)
|
||||
CommonAPI.follow(reading_user, user)
|
||||
CommonAPI.follow(user, reading_user)
|
||||
|
||||
{:ok, _} = start_socket("?stream=user:notification&access_token=#{token.token}")
|
||||
|
||||
|
|
|
|||
56
test/pleroma/language/language_detector_test.exs
Normal file
56
test/pleroma/language/language_detector_test.exs
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Language.LanguageDetectorTest do
|
||||
use Pleroma.DataCase, async: true
|
||||
|
||||
alias Pleroma.Language.LanguageDetector
|
||||
alias Pleroma.Language.LanguageDetectorMock
|
||||
alias Pleroma.StaticStubbedConfigMock
|
||||
|
||||
import Mox
|
||||
|
||||
setup do
|
||||
# Stub the StaticStubbedConfigMock to return our mock for the provider
|
||||
StaticStubbedConfigMock
|
||||
|> stub(:get, fn
|
||||
[Pleroma.Language.LanguageDetector, :provider] -> LanguageDetectorMock
|
||||
_other -> nil
|
||||
end)
|
||||
|
||||
# Stub the LanguageDetectorMock with default implementations
|
||||
LanguageDetectorMock
|
||||
|> stub(:missing_dependencies, fn -> [] end)
|
||||
|> stub(:configured?, fn -> true end)
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
test "it detects text language" do
|
||||
LanguageDetectorMock
|
||||
|> expect(:detect, fn _text -> "fr" end)
|
||||
|
||||
detected_language = LanguageDetector.detect("Je viens d'atterrir en Tchéquie.")
|
||||
|
||||
assert detected_language == "fr"
|
||||
end
|
||||
|
||||
test "it returns nil if text is not long enough" do
|
||||
# No need to set expectations as the word count check happens before the provider is called
|
||||
|
||||
detected_language = LanguageDetector.detect("it returns nil")
|
||||
|
||||
assert detected_language == nil
|
||||
end
|
||||
|
||||
test "it returns nil if no provider specified" do
|
||||
# Override the stub to return nil for the provider
|
||||
StaticStubbedConfigMock
|
||||
|> expect(:get, fn [Pleroma.Language.LanguageDetector, :provider] -> nil end)
|
||||
|
||||
detected_language = LanguageDetector.detect("this should also return nil")
|
||||
|
||||
assert detected_language == nil
|
||||
end
|
||||
end
|
||||
37
test/pleroma/language/translation/deepl_test.exs
Normal file
37
test/pleroma/language/translation/deepl_test.exs
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Language.Translation.DeeplTest do
|
||||
use Pleroma.Web.ConnCase
|
||||
|
||||
alias Pleroma.Language.Translation.Deepl
|
||||
|
||||
test "it translates text" do
|
||||
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
||||
clear_config([Pleroma.Language.Translation.Deepl, :base_url], "https://api-free.deepl.com")
|
||||
clear_config([Pleroma.Language.Translation.Deepl, :api_key], "API_KEY")
|
||||
|
||||
{:ok, res} =
|
||||
Deepl.translate(
|
||||
"USUNĄĆ ŚLEDZIKA!Wklej to na swojego śledzika. Jeżeli uzbieramy 70% użytkowników nk...to usuną śledzika!!!",
|
||||
"pl",
|
||||
"en"
|
||||
)
|
||||
|
||||
assert %{
|
||||
detected_source_language: "PL",
|
||||
provider: "DeepL"
|
||||
} = res
|
||||
end
|
||||
|
||||
test "it returns languages list" do
|
||||
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
||||
clear_config([Pleroma.Language.Translation.Deepl, :base_url], "https://api-free.deepl.com")
|
||||
clear_config([Pleroma.Language.Translation.Deepl, :api_key], "API_KEY")
|
||||
|
||||
assert {:ok, [language | _languages]} = Deepl.supported_languages(:target)
|
||||
|
||||
assert is_binary(language)
|
||||
end
|
||||
end
|
||||
59
test/pleroma/language/translation/translate_locally_test.exs
Normal file
59
test/pleroma/language/translation/translate_locally_test.exs
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2024 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Language.Translation.TranslateLocallyTest do
|
||||
use Pleroma.DataCase
|
||||
|
||||
alias Pleroma.Language.Translation.TranslateLocally
|
||||
|
||||
@example_models %{
|
||||
"de" => %{
|
||||
"en" => "de-en-base"
|
||||
},
|
||||
"en" => %{
|
||||
"de" => "en-de-base",
|
||||
"pl" => "en-pl-tiny"
|
||||
},
|
||||
"cs" => %{
|
||||
"en" => "cs-en-base"
|
||||
},
|
||||
"pl" => %{
|
||||
"en" => "pl-en-tiny"
|
||||
}
|
||||
}
|
||||
|
||||
test "it returns languages list" do
|
||||
clear_config([Pleroma.Language.Translation.TranslateLocally, :models], @example_models)
|
||||
|
||||
assert {:ok, languages} = TranslateLocally.supported_languages(:source)
|
||||
assert ["cs", "de", "en", "pl"] = languages |> Enum.sort()
|
||||
end
|
||||
|
||||
describe "it returns languages matrix" do
|
||||
test "without intermediary language" do
|
||||
clear_config([Pleroma.Language.Translation.TranslateLocally, :models], @example_models)
|
||||
|
||||
assert {:ok,
|
||||
%{
|
||||
"cs" => ["en"],
|
||||
"de" => ["en"],
|
||||
"en" => ["de", "pl"],
|
||||
"pl" => ["en"]
|
||||
}} = TranslateLocally.languages_matrix()
|
||||
end
|
||||
|
||||
test "with intermediary language" do
|
||||
clear_config([Pleroma.Language.Translation.TranslateLocally, :models], @example_models)
|
||||
clear_config([Pleroma.Language.Translation.TranslateLocally, :intermediary_language], "en")
|
||||
|
||||
assert {:ok,
|
||||
%{
|
||||
"cs" => ["de", "en", "pl"],
|
||||
"de" => ["en", "pl"],
|
||||
"en" => ["de", "pl"],
|
||||
"pl" => ["de", "en"]
|
||||
}} = TranslateLocally.languages_matrix()
|
||||
end
|
||||
end
|
||||
end
|
||||
28
test/pleroma/language/translation_test.exs
Normal file
28
test/pleroma/language/translation_test.exs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
defmodule Pleroma.Language.TranslationTest do
|
||||
use Pleroma.Web.ConnCase
|
||||
|
||||
alias Pleroma.Language.Translation
|
||||
|
||||
setup do: clear_config([Pleroma.Language.Translation, :provider], TranslationMock)
|
||||
|
||||
test "it translates text" do
|
||||
assert {:ok,
|
||||
%{
|
||||
content: "txet emos",
|
||||
detected_source_language: _,
|
||||
provider: _
|
||||
}} = Translation.translate("some text", "en", "uk")
|
||||
end
|
||||
|
||||
test "it stores translation result in cache" do
|
||||
Translation.translate("some text", "en", "uk")
|
||||
|
||||
assert {:ok, result} =
|
||||
Cachex.get(
|
||||
:translations_cache,
|
||||
"en/uk/#{:crypto.hash(:sha256, "some text") |> Base.encode64()}"
|
||||
)
|
||||
|
||||
assert result.content == "txet emos"
|
||||
end
|
||||
end
|
||||
|
|
@ -10,22 +10,23 @@ defmodule Pleroma.ListTest do
|
|||
|
||||
test "creating a list" do
|
||||
user = insert(:user)
|
||||
{:ok, %Pleroma.List{} = list} = Pleroma.List.create("title", user)
|
||||
%Pleroma.List{title: title} = Pleroma.List.get(list.id, user)
|
||||
{:ok, %Pleroma.List{} = list} = Pleroma.List.create(%{title: "title"}, user)
|
||||
%Pleroma.List{title: title, exclusive: exclusive} = Pleroma.List.get(list.id, user)
|
||||
assert title == "title"
|
||||
assert exclusive == false
|
||||
end
|
||||
|
||||
test "validates title" do
|
||||
user = insert(:user)
|
||||
|
||||
assert {:error, changeset} = Pleroma.List.create("", user)
|
||||
assert {:error, changeset} = Pleroma.List.create(%{title: ""}, user)
|
||||
assert changeset.errors == [title: {"can't be blank", [validation: :required]}]
|
||||
end
|
||||
|
||||
test "getting a list not belonging to the user" do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
{:ok, %Pleroma.List{} = list} = Pleroma.List.create("title", user)
|
||||
{:ok, %Pleroma.List{} = list} = Pleroma.List.create(%{title: "title"}, user)
|
||||
ret = Pleroma.List.get(list.id, other_user)
|
||||
assert is_nil(ret)
|
||||
end
|
||||
|
|
@ -33,7 +34,7 @@ defmodule Pleroma.ListTest do
|
|||
test "adding an user to a list" do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
{:ok, list} = Pleroma.List.create("title", user)
|
||||
{:ok, list} = Pleroma.List.create(%{title: "title"}, user)
|
||||
{:ok, %{following: following}} = Pleroma.List.follow(list, other_user)
|
||||
assert [other_user.follower_address] == following
|
||||
end
|
||||
|
|
@ -41,7 +42,7 @@ defmodule Pleroma.ListTest do
|
|||
test "removing an user from a list" do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
{:ok, list} = Pleroma.List.create("title", user)
|
||||
{:ok, list} = Pleroma.List.create(%{title: "title"}, user)
|
||||
{:ok, %{following: _following}} = Pleroma.List.follow(list, other_user)
|
||||
{:ok, %{following: following}} = Pleroma.List.unfollow(list, other_user)
|
||||
assert [] == following
|
||||
|
|
@ -49,14 +50,27 @@ defmodule Pleroma.ListTest do
|
|||
|
||||
test "renaming a list" do
|
||||
user = insert(:user)
|
||||
{:ok, list} = Pleroma.List.create("title", user)
|
||||
{:ok, %{title: title}} = Pleroma.List.rename(list, "new")
|
||||
{:ok, list} = Pleroma.List.create(%{title: "title"}, user)
|
||||
{:ok, %{title: title}} = Pleroma.List.update(list, %{title: "new"})
|
||||
assert "new" == title
|
||||
end
|
||||
|
||||
test "updating a list exclusivity" do
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, %{exclusive: exclusive} = list} =
|
||||
Pleroma.List.create(%{title: "title", exclusive: true}, user)
|
||||
|
||||
assert exclusive == true
|
||||
{:ok, %{exclusive: exclusive} = list} = Pleroma.List.update(list, %{exclusive: false})
|
||||
assert exclusive == false
|
||||
{:ok, %{exclusive: exclusive}} = Pleroma.List.update(list, %{exclusive: true})
|
||||
assert exclusive == true
|
||||
end
|
||||
|
||||
test "deleting a list" do
|
||||
user = insert(:user)
|
||||
{:ok, list} = Pleroma.List.create("title", user)
|
||||
{:ok, list} = Pleroma.List.create(%{title: "title"}, user)
|
||||
{:ok, list} = Pleroma.List.delete(list)
|
||||
assert is_nil(Repo.get(Pleroma.List, list.id))
|
||||
end
|
||||
|
|
@ -65,7 +79,7 @@ defmodule Pleroma.ListTest do
|
|||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
third_user = insert(:user)
|
||||
{:ok, list} = Pleroma.List.create("title", user)
|
||||
{:ok, list} = Pleroma.List.create(%{title: "title"}, user)
|
||||
{:ok, list} = Pleroma.List.follow(list, other_user)
|
||||
{:ok, list} = Pleroma.List.follow(list, third_user)
|
||||
{:ok, following} = Pleroma.List.get_following(list)
|
||||
|
|
@ -76,9 +90,9 @@ defmodule Pleroma.ListTest do
|
|||
test "getting all lists by an user" do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
{:ok, list_one} = Pleroma.List.create("title", user)
|
||||
{:ok, list_two} = Pleroma.List.create("other title", user)
|
||||
{:ok, list_three} = Pleroma.List.create("third title", other_user)
|
||||
{:ok, list_one} = Pleroma.List.create(%{title: "title"}, user)
|
||||
{:ok, list_two} = Pleroma.List.create(%{title: "other title"}, user)
|
||||
{:ok, list_three} = Pleroma.List.create(%{title: "third title"}, other_user)
|
||||
lists = Pleroma.List.for_user(user, %{})
|
||||
assert list_one in lists
|
||||
assert list_two in lists
|
||||
|
|
@ -88,9 +102,9 @@ defmodule Pleroma.ListTest do
|
|||
test "getting all lists the user is a member of" do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
{:ok, list_one} = Pleroma.List.create("title", user)
|
||||
{:ok, list_two} = Pleroma.List.create("other title", user)
|
||||
{:ok, list_three} = Pleroma.List.create("third title", other_user)
|
||||
{:ok, list_one} = Pleroma.List.create(%{title: "title"}, user)
|
||||
{:ok, list_two} = Pleroma.List.create(%{title: "other title"}, user)
|
||||
{:ok, list_three} = Pleroma.List.create(%{title: "third title"}, other_user)
|
||||
{:ok, list_one} = Pleroma.List.follow(list_one, other_user)
|
||||
{:ok, list_two} = Pleroma.List.follow(list_two, other_user)
|
||||
{:ok, list_three} = Pleroma.List.follow(list_three, user)
|
||||
|
|
@ -106,8 +120,8 @@ defmodule Pleroma.ListTest do
|
|||
not_owner = insert(:user)
|
||||
member_1 = insert(:user)
|
||||
member_2 = insert(:user)
|
||||
{:ok, owned_list} = Pleroma.List.create("owned", owner)
|
||||
{:ok, not_owned_list} = Pleroma.List.create("not owned", not_owner)
|
||||
{:ok, owned_list} = Pleroma.List.create(%{title: "owned"}, owner)
|
||||
{:ok, not_owned_list} = Pleroma.List.create(%{title: "not owned"}, not_owner)
|
||||
{:ok, owned_list} = Pleroma.List.follow(owned_list, member_1)
|
||||
{:ok, owned_list} = Pleroma.List.follow(owned_list, member_2)
|
||||
{:ok, not_owned_list} = Pleroma.List.follow(not_owned_list, member_1)
|
||||
|
|
@ -123,14 +137,14 @@ defmodule Pleroma.ListTest do
|
|||
|
||||
test "get by ap_id" do
|
||||
user = insert(:user)
|
||||
{:ok, list} = Pleroma.List.create("foo", user)
|
||||
{:ok, list} = Pleroma.List.create(%{title: "foo"}, user)
|
||||
assert Pleroma.List.get_by_ap_id(list.ap_id) == list
|
||||
end
|
||||
|
||||
test "memberships" do
|
||||
user = insert(:user)
|
||||
member = insert(:user)
|
||||
{:ok, list} = Pleroma.List.create("foo", user)
|
||||
{:ok, list} = Pleroma.List.create(%{title: "foo"}, user)
|
||||
{:ok, list} = Pleroma.List.follow(list, member)
|
||||
|
||||
assert Pleroma.List.memberships(member) == [list.ap_id]
|
||||
|
|
@ -140,7 +154,7 @@ defmodule Pleroma.ListTest do
|
|||
user = insert(:user)
|
||||
member = insert(:user)
|
||||
|
||||
{:ok, list} = Pleroma.List.create("foo", user)
|
||||
{:ok, list} = Pleroma.List.create(%{title: "foo"}, user)
|
||||
{:ok, list} = Pleroma.List.follow(list, member)
|
||||
|
||||
assert Pleroma.List.member?(list, member)
|
||||
|
|
|
|||
|
|
@ -36,11 +36,12 @@ defmodule Pleroma.MarkerTest do
|
|||
insert(:notification, user: user, activity: insert(:note_activity))
|
||||
insert(:notification, user: user, activity: insert(:note_activity))
|
||||
insert(:marker, timeline: "home", user: user)
|
||||
%Marker{} = refreshed_marker = refresh_record(marker)
|
||||
|
||||
assert Marker.get_markers(
|
||||
user,
|
||||
["notifications"]
|
||||
) == [%Marker{refresh_record(marker) | unread_count: 2}]
|
||||
) == [%{refreshed_marker | unread_count: 2}]
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ defmodule Pleroma.MigrationHelper.NotificationBackfillTest do
|
|||
{:ok, post} = CommonAPI.post(user, %{status: "yeah, @#{other_user.nickname}"})
|
||||
{:ok, chat} = CommonAPI.post_chat_message(user, other_user, "yo")
|
||||
{:ok, react} = CommonAPI.react_with_emoji(post.id, other_user, "☕")
|
||||
{:ok, like} = CommonAPI.favorite(other_user, post.id)
|
||||
{:ok, like} = CommonAPI.favorite(post.id, other_user)
|
||||
{:ok, react_2} = CommonAPI.react_with_emoji(post.id, other_user, "☕")
|
||||
|
||||
data =
|
||||
|
|
|
|||
|
|
@ -308,4 +308,37 @@ defmodule Pleroma.ModerationLogTest do
|
|||
assert log.data["message"] == "@#{moderator.nickname} deleted status ##{note.id}"
|
||||
end
|
||||
end
|
||||
|
||||
describe "get_log_entry_message/1" do
|
||||
setup do
|
||||
moderator = insert(:user, is_moderator: true)
|
||||
[moderator: moderator]
|
||||
end
|
||||
|
||||
test "handles unknown action types gracefully", %{moderator: moderator} do
|
||||
log_entry = %ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => moderator.nickname},
|
||||
"action" => "unknown_action",
|
||||
"some_data" => "test_value"
|
||||
}
|
||||
}
|
||||
|
||||
assert ModerationLog.get_log_entry_message(log_entry) =~ moderator.nickname
|
||||
assert ModerationLog.get_log_entry_message(log_entry) =~ "unknown_action"
|
||||
end
|
||||
|
||||
test "handles malformed log entries gracefully" do
|
||||
log_entry = %ModerationLog{
|
||||
data: %{
|
||||
"action" => "force_password_reset"
|
||||
# Missing "actor" and "subject" fields
|
||||
}
|
||||
}
|
||||
|
||||
message = ModerationLog.get_log_entry_message(log_entry)
|
||||
assert is_binary(message)
|
||||
assert message =~ "force_password_reset"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -17,9 +17,10 @@ defmodule Pleroma.NotificationTest do
|
|||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.MastodonAPI.NotificationView
|
||||
alias Pleroma.Web.Streamer
|
||||
|
||||
setup do
|
||||
Mox.stub_with(Pleroma.UnstubbedConfigMock, Pleroma.Config)
|
||||
Mox.stub_with(Pleroma.UnstubbedConfigMock, Pleroma.Test.StaticConfig)
|
||||
:ok
|
||||
end
|
||||
|
||||
|
|
@ -165,7 +166,7 @@ defmodule Pleroma.NotificationTest do
|
|||
{:ok, _activity_two} = CommonAPI.repeat(activity_one.id, repeated_user)
|
||||
|
||||
{:ok, _edit_activity} =
|
||||
CommonAPI.update(user, activity_one, %{
|
||||
CommonAPI.update(activity_one, user, %{
|
||||
status: "hey @#{other_user.nickname}! mew mew"
|
||||
})
|
||||
|
||||
|
|
@ -180,8 +181,8 @@ defmodule Pleroma.NotificationTest do
|
|||
question = insert(:question, user: user1)
|
||||
activity = insert(:question_activity, question: question)
|
||||
|
||||
{:ok, _, _} = CommonAPI.vote(user2, question, [0])
|
||||
{:ok, _, _} = CommonAPI.vote(user3, question, [1])
|
||||
{:ok, _, _} = CommonAPI.vote(question, user2, [0])
|
||||
{:ok, _, _} = CommonAPI.vote(question, user3, [1])
|
||||
|
||||
{:ok, notifications} = Notification.create_poll_notifications(activity)
|
||||
|
||||
|
|
@ -209,7 +210,7 @@ defmodule Pleroma.NotificationTest do
|
|||
notification_settings: %Pleroma.User.NotificationSetting{block_from_strangers: true}
|
||||
)
|
||||
|
||||
CommonAPI.follow(follower, followed)
|
||||
CommonAPI.follow(followed, follower)
|
||||
{:ok, activity} = CommonAPI.post(follower, %{status: "hey @#{followed.nickname}"})
|
||||
refute Notification.create_notification(activity, followed)
|
||||
end
|
||||
|
|
@ -222,7 +223,7 @@ defmodule Pleroma.NotificationTest do
|
|||
notification_settings: %Pleroma.User.NotificationSetting{block_from_strangers: true}
|
||||
)
|
||||
|
||||
CommonAPI.follow(receiver, poster)
|
||||
CommonAPI.follow(poster, receiver)
|
||||
{:ok, activity} = CommonAPI.post(poster, %{status: "hey @#{receiver.nickname}"})
|
||||
assert Notification.create_notification(activity, receiver)
|
||||
end
|
||||
|
|
@ -238,7 +239,7 @@ defmodule Pleroma.NotificationTest do
|
|||
user = insert(:user)
|
||||
subscriber = insert(:user)
|
||||
|
||||
{:ok, _, _, _} = CommonAPI.follow(subscriber, user)
|
||||
{:ok, _, _, _} = CommonAPI.follow(user, subscriber)
|
||||
User.subscribe(subscriber, user)
|
||||
{:ok, status} = CommonAPI.post(user, %{status: "Akariiiin"})
|
||||
{:ok, [_notif]} = Notification.create_notifications(status)
|
||||
|
|
@ -295,7 +296,7 @@ defmodule Pleroma.NotificationTest do
|
|||
insert(:filter, user: user, phrase: "tesla", hide: true)
|
||||
|
||||
{:ok, activity_one} = CommonAPI.post(user, %{status: "wow tesla"})
|
||||
{:ok, activity_two} = CommonAPI.favorite(other_user, activity_one.id)
|
||||
{:ok, activity_two} = CommonAPI.favorite(activity_one.id, other_user)
|
||||
|
||||
{:ok, [notification]} = Notification.create_notifications(activity_two)
|
||||
|
||||
|
|
@ -309,7 +310,7 @@ defmodule Pleroma.NotificationTest do
|
|||
user = insert(:user)
|
||||
followed_user = insert(:user, is_locked: false)
|
||||
|
||||
{:ok, _, _, _activity} = CommonAPI.follow(user, followed_user)
|
||||
{:ok, _, _, _activity} = CommonAPI.follow(followed_user, user)
|
||||
assert FollowingRelationship.following?(user, followed_user)
|
||||
assert [notification] = Notification.for_user(followed_user)
|
||||
|
||||
|
|
@ -324,7 +325,7 @@ defmodule Pleroma.NotificationTest do
|
|||
user = insert(:user)
|
||||
followed_user = insert(:user, is_locked: true)
|
||||
|
||||
{:ok, _, _, _activity} = CommonAPI.follow(user, followed_user)
|
||||
{:ok, _, _, _activity} = CommonAPI.follow(followed_user, user)
|
||||
refute FollowingRelationship.following?(user, followed_user)
|
||||
assert [notification] = Notification.for_user(followed_user)
|
||||
|
||||
|
|
@ -349,12 +350,12 @@ defmodule Pleroma.NotificationTest do
|
|||
user = insert(:user)
|
||||
followed_user = insert(:user, is_locked: false)
|
||||
|
||||
{:ok, _, _, _activity} = CommonAPI.follow(user, followed_user)
|
||||
{:ok, _, _, _activity} = CommonAPI.follow(followed_user, user)
|
||||
assert FollowingRelationship.following?(user, followed_user)
|
||||
assert [notification] = Notification.for_user(followed_user)
|
||||
|
||||
CommonAPI.unfollow(user, followed_user)
|
||||
{:ok, _, _, _activity_dupe} = CommonAPI.follow(user, followed_user)
|
||||
CommonAPI.unfollow(followed_user, user)
|
||||
{:ok, _, _, _activity_dupe} = CommonAPI.follow(followed_user, user)
|
||||
|
||||
notification_id = notification.id
|
||||
assert [%{id: ^notification_id}] = Notification.for_user(followed_user)
|
||||
|
|
@ -363,7 +364,7 @@ defmodule Pleroma.NotificationTest do
|
|||
test "dismisses the notification on follow request rejection" do
|
||||
user = insert(:user, is_locked: true)
|
||||
follower = insert(:user)
|
||||
{:ok, _, _, _follow_activity} = CommonAPI.follow(follower, user)
|
||||
{:ok, _, _, _follow_activity} = CommonAPI.follow(user, follower)
|
||||
assert [_notification] = Notification.for_user(user)
|
||||
{:ok, _follower} = CommonAPI.reject_follow_request(follower, user)
|
||||
assert [] = Notification.for_user(user)
|
||||
|
|
@ -446,8 +447,7 @@ defmodule Pleroma.NotificationTest do
|
|||
|
||||
describe "set_read_up_to()" do
|
||||
test "it sets all notifications as read up to a specified notification ID" do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
[user, other_user] = insert_pair(:user)
|
||||
|
||||
{:ok, _activity} =
|
||||
CommonAPI.post(user, %{
|
||||
|
|
@ -486,6 +486,37 @@ defmodule Pleroma.NotificationTest do
|
|||
|
||||
assert m.last_read_id == to_string(n2.id)
|
||||
end
|
||||
|
||||
@tag needs_streamer: true
|
||||
test "it sends updated marker to the 'user' and the 'user:notification' stream" do
|
||||
%{user: user, token: oauth_token} = oauth_access(["read"])
|
||||
other_user = insert(:user)
|
||||
|
||||
{:ok, _activity} =
|
||||
CommonAPI.post(other_user, %{
|
||||
status: "hi @#{user.nickname}!"
|
||||
})
|
||||
|
||||
[%{id: notification_id}] = Notification.for_user(user)
|
||||
|
||||
notification_id = to_string(notification_id)
|
||||
|
||||
task =
|
||||
Task.async(fn ->
|
||||
{:ok, _topic} =
|
||||
Streamer.get_topic_and_add_socket("user:notification", user, oauth_token)
|
||||
|
||||
assert_receive {:text, event}, 4_000
|
||||
|
||||
assert %{"event" => "marker", "payload" => payload} = Jason.decode!(event)
|
||||
|
||||
assert %{"notifications" => %{"last_read_id" => ^notification_id}} =
|
||||
Jason.decode!(payload)
|
||||
end)
|
||||
|
||||
Notification.set_read_up_to(user, notification_id)
|
||||
Task.await(task)
|
||||
end
|
||||
end
|
||||
|
||||
describe "for_user_since/2" do
|
||||
|
|
@ -617,7 +648,7 @@ defmodule Pleroma.NotificationTest do
|
|||
status: "hey @#{other_user.nickname}!"
|
||||
})
|
||||
|
||||
{:ok, activity_two} = CommonAPI.favorite(third_user, activity_one.id)
|
||||
{:ok, activity_two} = CommonAPI.favorite(activity_one.id, third_user)
|
||||
|
||||
enabled_receivers = Notification.get_notified_from_activity(activity_two)
|
||||
|
||||
|
|
@ -693,7 +724,7 @@ defmodule Pleroma.NotificationTest do
|
|||
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
|
||||
|
||||
{:ok, _} = CommonAPI.add_mute(other_user, activity)
|
||||
{:ok, _} = CommonAPI.add_mute(activity, other_user)
|
||||
|
||||
{:ok, same_context_activity} =
|
||||
CommonAPI.post(user, %{
|
||||
|
|
@ -748,7 +779,7 @@ defmodule Pleroma.NotificationTest do
|
|||
{:ok, _activity_two} = CommonAPI.repeat(activity_one.id, repeated_user)
|
||||
|
||||
{:ok, edit_activity} =
|
||||
CommonAPI.update(user, activity_one, %{
|
||||
CommonAPI.update(activity_one, user, %{
|
||||
status: "hey @#{other_user.nickname}! mew mew"
|
||||
})
|
||||
|
||||
|
|
@ -768,7 +799,7 @@ defmodule Pleroma.NotificationTest do
|
|||
|
||||
assert Enum.empty?(Notification.for_user(user))
|
||||
|
||||
{:ok, _} = CommonAPI.favorite(other_user, activity.id)
|
||||
{:ok, _} = CommonAPI.favorite(activity.id, other_user)
|
||||
|
||||
assert length(Notification.for_user(user)) == 1
|
||||
|
||||
|
|
@ -785,7 +816,7 @@ defmodule Pleroma.NotificationTest do
|
|||
|
||||
assert Enum.empty?(Notification.for_user(user))
|
||||
|
||||
{:ok, _} = CommonAPI.favorite(other_user, activity.id)
|
||||
{:ok, _} = CommonAPI.favorite(activity.id, other_user)
|
||||
|
||||
assert length(Notification.for_user(user)) == 1
|
||||
|
||||
|
|
@ -840,7 +871,7 @@ defmodule Pleroma.NotificationTest do
|
|||
|
||||
assert Enum.empty?(Notification.for_user(user))
|
||||
|
||||
{:error, :not_found} = CommonAPI.favorite(other_user, activity.id)
|
||||
{:error, :not_found} = CommonAPI.favorite(activity.id, other_user)
|
||||
|
||||
assert Enum.empty?(Notification.for_user(user))
|
||||
end
|
||||
|
|
@ -1090,7 +1121,7 @@ defmodule Pleroma.NotificationTest do
|
|||
another_user = insert(:user)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "Give me my cofe!"})
|
||||
{:ok, _} = CommonAPI.favorite(another_user, activity.id)
|
||||
{:ok, _} = CommonAPI.favorite(activity.id, another_user)
|
||||
|
||||
assert length(Notification.for_user(user)) == 1
|
||||
end
|
||||
|
|
@ -1101,7 +1132,7 @@ defmodule Pleroma.NotificationTest do
|
|||
|
||||
insert(:filter, user: followed_user, phrase: "test", hide: true)
|
||||
|
||||
{:ok, _, _, _activity} = CommonAPI.follow(user, followed_user)
|
||||
{:ok, _, _, _activity} = CommonAPI.follow(followed_user, user)
|
||||
refute FollowingRelationship.following?(user, followed_user)
|
||||
assert [notification] = Notification.for_user(followed_user)
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ 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
|
||||
|
|
@ -100,7 +99,7 @@ defmodule Pleroma.Object.FetcherTest do
|
|||
test "it returns thread depth exceeded error if thread depth is exceeded" do
|
||||
clear_config([:instance, :federation_incoming_replies_max_depth], 0)
|
||||
|
||||
assert {:error, :allowed_depth} = Fetcher.fetch_object_from_id(@ap_id, depth: 1)
|
||||
assert {:allowed_depth, false} = Fetcher.fetch_object_from_id(@ap_id, depth: 1)
|
||||
end
|
||||
|
||||
test "it fetches object if max thread depth is restricted to 0 and depth is not specified" do
|
||||
|
|
@ -118,15 +117,18 @@ defmodule Pleroma.Object.FetcherTest do
|
|||
|
||||
describe "actor origin containment" do
|
||||
test "it rejects objects with a bogus origin" do
|
||||
{:error, _} = Fetcher.fetch_object_from_id("https://info.pleroma.site/activity.json")
|
||||
{:containment, :error} =
|
||||
Fetcher.fetch_object_from_id("https://info.pleroma.site/activity.json")
|
||||
end
|
||||
|
||||
test "it rejects objects when attributedTo is wrong (variant 1)" do
|
||||
{:error, _} = Fetcher.fetch_object_from_id("https://info.pleroma.site/activity2.json")
|
||||
{:containment, :error} =
|
||||
Fetcher.fetch_object_from_id("https://info.pleroma.site/activity2.json")
|
||||
end
|
||||
|
||||
test "it rejects objects when attributedTo is wrong (variant 2)" do
|
||||
{:error, _} = Fetcher.fetch_object_from_id("https://info.pleroma.site/activity3.json")
|
||||
{:containment, :error} =
|
||||
Fetcher.fetch_object_from_id("https://info.pleroma.site/activity3.json")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -150,28 +152,102 @@ defmodule Pleroma.Object.FetcherTest do
|
|||
clear_config([:mrf_keyword, :reject], ["yeah"])
|
||||
clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.KeywordPolicy])
|
||||
|
||||
assert {:reject, "[KeywordPolicy] Matches with rejected keyword"} ==
|
||||
assert {:transmogrifier, {:reject, "[KeywordPolicy] Matches with rejected keyword"}} ==
|
||||
Fetcher.fetch_object_from_id(
|
||||
"http://mastodon.example.org/@admin/99541947525187367"
|
||||
)
|
||||
end
|
||||
|
||||
test "it does not fetch a spoofed object uploaded on an instance as an attachment" do
|
||||
assert {:error, _} =
|
||||
assert {:fetch, {:error, {:content_type, "application/json"}}} =
|
||||
Fetcher.fetch_object_from_id(
|
||||
"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)
|
||||
test "it does not fetch from local instance" do
|
||||
local_url = Pleroma.Web.Endpoint.url() <> "/objects/local_resource"
|
||||
|
||||
{:ok, _object} =
|
||||
Fetcher.fetch_object_from_id("http://mastodon.example.org/@admin/99541947525187367")
|
||||
assert {:fetch, {:error, "Trying to fetch local resource"}} =
|
||||
Fetcher.fetch_object_from_id(local_url)
|
||||
end
|
||||
|
||||
assert Instances.reachable?(id)
|
||||
test "it validates content-type headers according to ActivityPub spec" do
|
||||
# Setup a mock for an object with invalid content-type
|
||||
mock(fn
|
||||
%{method: :get, url: "https://example.com/objects/invalid-content-type"} ->
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
# Not a valid AP content-type
|
||||
headers: [{"content-type", "application/json"}],
|
||||
body:
|
||||
Jason.encode!(%{
|
||||
"id" => "https://example.com/objects/invalid-content-type",
|
||||
"type" => "Note",
|
||||
"content" => "This has an invalid content type",
|
||||
"actor" => "https://example.com/users/actor",
|
||||
"attributedTo" => "https://example.com/users/actor"
|
||||
})
|
||||
}
|
||||
end)
|
||||
|
||||
assert {:fetch, {:error, {:content_type, "application/json"}}} =
|
||||
Fetcher.fetch_object_from_id("https://example.com/objects/invalid-content-type")
|
||||
end
|
||||
|
||||
test "it accepts objects with application/ld+json and ActivityStreams profile" do
|
||||
# Setup a mock for an object with ld+json content-type and AS profile
|
||||
mock(fn
|
||||
%{method: :get, url: "https://example.com/objects/valid-ld-json"} ->
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
headers: [
|
||||
{"content-type",
|
||||
"application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""}
|
||||
],
|
||||
body:
|
||||
Jason.encode!(%{
|
||||
"id" => "https://example.com/objects/valid-ld-json",
|
||||
"type" => "Note",
|
||||
"content" => "This has a valid ld+json content type",
|
||||
"actor" => "https://example.com/users/actor",
|
||||
"attributedTo" => "https://example.com/users/actor"
|
||||
})
|
||||
}
|
||||
end)
|
||||
|
||||
# This should pass if content-type validation works correctly
|
||||
assert {:ok, object} =
|
||||
Fetcher.fetch_and_contain_remote_object_from_id(
|
||||
"https://example.com/objects/valid-ld-json"
|
||||
)
|
||||
|
||||
assert object["content"] == "This has a valid ld+json content type"
|
||||
end
|
||||
|
||||
test "it rejects objects with no content-type header" do
|
||||
# Setup a mock for an object with no content-type header
|
||||
mock(fn
|
||||
%{method: :get, url: "https://example.com/objects/no-content-type"} ->
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
# No content-type header
|
||||
headers: [],
|
||||
body:
|
||||
Jason.encode!(%{
|
||||
"id" => "https://example.com/objects/no-content-type",
|
||||
"type" => "Note",
|
||||
"content" => "This has no content type header",
|
||||
"actor" => "https://example.com/users/actor",
|
||||
"attributedTo" => "https://example.com/users/actor"
|
||||
})
|
||||
}
|
||||
end)
|
||||
|
||||
# We want to test that the request fails with a missing content-type error
|
||||
# but the actual error is {:fetch, {:error, nil}} - we'll check for this format
|
||||
result = Fetcher.fetch_object_from_id("https://example.com/objects/no-content-type")
|
||||
assert {:fetch, {:error, nil}} = result
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -531,6 +607,110 @@ defmodule Pleroma.Object.FetcherTest do
|
|||
end
|
||||
end
|
||||
|
||||
describe "cross-domain redirect handling" do
|
||||
setup do
|
||||
mock(fn
|
||||
# Cross-domain redirect with original domain in id
|
||||
%{method: :get, url: "https://original.test/objects/123"} ->
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
url: "https://media.test/objects/123",
|
||||
headers: [{"content-type", "application/activity+json"}],
|
||||
body:
|
||||
Jason.encode!(%{
|
||||
"id" => "https://original.test/objects/123",
|
||||
"type" => "Note",
|
||||
"content" => "This is redirected content",
|
||||
"actor" => "https://original.test/users/actor",
|
||||
"attributedTo" => "https://original.test/users/actor"
|
||||
})
|
||||
}
|
||||
|
||||
# Cross-domain redirect with final domain in id
|
||||
%{method: :get, url: "https://original.test/objects/final-domain-id"} ->
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
url: "https://media.test/objects/final-domain-id",
|
||||
headers: [{"content-type", "application/activity+json"}],
|
||||
body:
|
||||
Jason.encode!(%{
|
||||
"id" => "https://media.test/objects/final-domain-id",
|
||||
"type" => "Note",
|
||||
"content" => "This has final domain in id",
|
||||
"actor" => "https://original.test/users/actor",
|
||||
"attributedTo" => "https://original.test/users/actor"
|
||||
})
|
||||
}
|
||||
|
||||
# No redirect - same domain
|
||||
%{method: :get, url: "https://original.test/objects/same-domain-redirect"} ->
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
url: "https://original.test/objects/different-path",
|
||||
headers: [{"content-type", "application/activity+json"}],
|
||||
body:
|
||||
Jason.encode!(%{
|
||||
"id" => "https://original.test/objects/same-domain-redirect",
|
||||
"type" => "Note",
|
||||
"content" => "This has a same-domain redirect",
|
||||
"actor" => "https://original.test/users/actor",
|
||||
"attributedTo" => "https://original.test/users/actor"
|
||||
})
|
||||
}
|
||||
|
||||
# Test case with missing url field in response (common in tests)
|
||||
%{method: :get, url: "https://original.test/objects/missing-url"} ->
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
# No url field
|
||||
headers: [{"content-type", "application/activity+json"}],
|
||||
body:
|
||||
Jason.encode!(%{
|
||||
"id" => "https://original.test/objects/missing-url",
|
||||
"type" => "Note",
|
||||
"content" => "This has no URL field in response",
|
||||
"actor" => "https://original.test/users/actor",
|
||||
"attributedTo" => "https://original.test/users/actor"
|
||||
})
|
||||
}
|
||||
end)
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
test "it rejects objects from cross-domain redirects with original domain in id" do
|
||||
assert {:error, {:cross_domain_redirect, true}} =
|
||||
Fetcher.fetch_and_contain_remote_object_from_id(
|
||||
"https://original.test/objects/123"
|
||||
)
|
||||
end
|
||||
|
||||
test "it rejects objects from cross-domain redirects with final domain in id" do
|
||||
assert {:error, {:cross_domain_redirect, true}} =
|
||||
Fetcher.fetch_and_contain_remote_object_from_id(
|
||||
"https://original.test/objects/final-domain-id"
|
||||
)
|
||||
end
|
||||
|
||||
test "it accepts objects with same-domain redirects" do
|
||||
assert {:ok, data} =
|
||||
Fetcher.fetch_and_contain_remote_object_from_id(
|
||||
"https://original.test/objects/same-domain-redirect"
|
||||
)
|
||||
|
||||
assert data["content"] == "This has a same-domain redirect"
|
||||
end
|
||||
|
||||
test "it handles responses without URL field (common in tests)" do
|
||||
assert {:ok, data} =
|
||||
Fetcher.fetch_and_contain_remote_object_from_id(
|
||||
"https://original.test/objects/missing-url"
|
||||
)
|
||||
|
||||
assert data["content"] == "This has no URL field in response"
|
||||
end
|
||||
end
|
||||
|
||||
describe "fetch with history" do
|
||||
setup do
|
||||
object2 = %{
|
||||
|
|
|
|||
|
|
@ -6,12 +6,10 @@ defmodule Pleroma.ObjectTest do
|
|||
use Pleroma.DataCase
|
||||
use Oban.Testing, repo: Pleroma.Repo
|
||||
|
||||
import ExUnit.CaptureLog
|
||||
import Mox
|
||||
import Pleroma.Factory
|
||||
import Tesla.Mock
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Hashtag
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Repo
|
||||
|
|
@ -158,7 +156,7 @@ defmodule Pleroma.ObjectTest do
|
|||
|
||||
uploads_dir = Pleroma.Config.get!([Pleroma.Uploaders.Local, :uploads])
|
||||
|
||||
File.mkdir_p!(uploads_dir)
|
||||
Pleroma.Backports.mkdir_p!(uploads_dir)
|
||||
|
||||
file = %Plug.Upload{
|
||||
content_type: "image/jpeg",
|
||||
|
|
@ -176,8 +174,9 @@ defmodule Pleroma.ObjectTest do
|
|||
|
||||
filename = Path.basename(href)
|
||||
|
||||
assert {:ok, files} = File.ls(uploads_dir)
|
||||
assert filename in files
|
||||
expected_path = Path.join([uploads_dir, Pleroma.Upload.Filter.Dedupe.shard_path(filename)])
|
||||
|
||||
assert File.exists?(expected_path)
|
||||
|
||||
Object.delete(note)
|
||||
|
||||
|
|
@ -185,8 +184,7 @@ defmodule Pleroma.ObjectTest do
|
|||
|
||||
assert Object.get_by_id(note.id).data["deleted"]
|
||||
assert Object.get_by_id(attachment.id) == nil
|
||||
assert {:ok, files} = File.ls(uploads_dir)
|
||||
refute filename in files
|
||||
refute File.exists?(expected_path)
|
||||
end
|
||||
|
||||
test "with objects that have legacy data.url attribute" do
|
||||
|
|
@ -282,148 +280,6 @@ defmodule Pleroma.ObjectTest do
|
|||
end
|
||||
end
|
||||
|
||||
describe "get_by_id_and_maybe_refetch" do
|
||||
setup do
|
||||
mock(fn
|
||||
%{method: :get, url: "https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d"} ->
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/tesla_mock/poll_original.json"),
|
||||
headers: HttpRequestMock.activitypub_object_headers()
|
||||
}
|
||||
|
||||
env ->
|
||||
apply(HttpRequestMock, :request, [env])
|
||||
end)
|
||||
|
||||
mock_modified = fn resp ->
|
||||
mock(fn
|
||||
%{method: :get, url: "https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d"} ->
|
||||
resp
|
||||
|
||||
env ->
|
||||
apply(HttpRequestMock, :request, [env])
|
||||
end)
|
||||
end
|
||||
|
||||
on_exit(fn -> mock(fn env -> apply(HttpRequestMock, :request, [env]) end) end)
|
||||
|
||||
[mock_modified: mock_modified]
|
||||
end
|
||||
|
||||
test "refetches if the time since the last refetch is greater than the interval", %{
|
||||
mock_modified: mock_modified
|
||||
} do
|
||||
%Object{} =
|
||||
object =
|
||||
Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d",
|
||||
fetch: true
|
||||
)
|
||||
|
||||
Object.set_cache(object)
|
||||
|
||||
assert Enum.at(object.data["oneOf"], 0)["replies"]["totalItems"] == 4
|
||||
assert Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 0
|
||||
|
||||
mock_modified.(%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/tesla_mock/poll_modified.json"),
|
||||
headers: HttpRequestMock.activitypub_object_headers()
|
||||
})
|
||||
|
||||
updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: -1)
|
||||
object_in_cache = Object.get_cached_by_ap_id(object.data["id"])
|
||||
assert updated_object == object_in_cache
|
||||
assert Enum.at(updated_object.data["oneOf"], 0)["replies"]["totalItems"] == 8
|
||||
assert Enum.at(updated_object.data["oneOf"], 1)["replies"]["totalItems"] == 3
|
||||
end
|
||||
|
||||
test "returns the old object if refetch fails", %{mock_modified: mock_modified} do
|
||||
%Object{} =
|
||||
object =
|
||||
Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d",
|
||||
fetch: true
|
||||
)
|
||||
|
||||
Object.set_cache(object)
|
||||
|
||||
assert Enum.at(object.data["oneOf"], 0)["replies"]["totalItems"] == 4
|
||||
assert Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 0
|
||||
|
||||
assert capture_log(fn ->
|
||||
mock_modified.(%Tesla.Env{status: 404, body: ""})
|
||||
|
||||
updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: -1)
|
||||
object_in_cache = Object.get_cached_by_ap_id(object.data["id"])
|
||||
assert updated_object == object_in_cache
|
||||
assert Enum.at(updated_object.data["oneOf"], 0)["replies"]["totalItems"] == 4
|
||||
assert Enum.at(updated_object.data["oneOf"], 1)["replies"]["totalItems"] == 0
|
||||
end) =~
|
||||
"[error] Couldn't refresh https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d"
|
||||
end
|
||||
|
||||
test "does not refetch if the time since the last refetch is greater than the interval", %{
|
||||
mock_modified: mock_modified
|
||||
} do
|
||||
%Object{} =
|
||||
object =
|
||||
Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d",
|
||||
fetch: true
|
||||
)
|
||||
|
||||
Object.set_cache(object)
|
||||
|
||||
assert Enum.at(object.data["oneOf"], 0)["replies"]["totalItems"] == 4
|
||||
assert Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 0
|
||||
|
||||
mock_modified.(%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/tesla_mock/poll_modified.json"),
|
||||
headers: HttpRequestMock.activitypub_object_headers()
|
||||
})
|
||||
|
||||
updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: 100)
|
||||
object_in_cache = Object.get_cached_by_ap_id(object.data["id"])
|
||||
assert updated_object == object_in_cache
|
||||
assert Enum.at(updated_object.data["oneOf"], 0)["replies"]["totalItems"] == 4
|
||||
assert Enum.at(updated_object.data["oneOf"], 1)["replies"]["totalItems"] == 0
|
||||
end
|
||||
|
||||
test "preserves internal fields on refetch", %{mock_modified: mock_modified} do
|
||||
%Object{} =
|
||||
object =
|
||||
Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d",
|
||||
fetch: true
|
||||
)
|
||||
|
||||
Object.set_cache(object)
|
||||
|
||||
assert Enum.at(object.data["oneOf"], 0)["replies"]["totalItems"] == 4
|
||||
assert Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 0
|
||||
|
||||
user = insert(:user)
|
||||
activity = Activity.get_create_by_object_ap_id(object.data["id"])
|
||||
{:ok, activity} = CommonAPI.favorite(user, activity.id)
|
||||
object = Object.get_by_ap_id(activity.data["object"])
|
||||
|
||||
assert object.data["like_count"] == 1
|
||||
|
||||
mock_modified.(%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/tesla_mock/poll_modified.json"),
|
||||
headers: HttpRequestMock.activitypub_object_headers()
|
||||
})
|
||||
|
||||
updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: -1)
|
||||
object_in_cache = Object.get_cached_by_ap_id(object.data["id"])
|
||||
assert updated_object == object_in_cache
|
||||
assert Enum.at(updated_object.data["oneOf"], 0)["replies"]["totalItems"] == 8
|
||||
assert Enum.at(updated_object.data["oneOf"], 1)["replies"]["totalItems"] == 3
|
||||
|
||||
assert updated_object.data["like_count"] == 1
|
||||
end
|
||||
end
|
||||
|
||||
describe ":hashtags association" do
|
||||
test "Hashtag records are created with Object record and updated on its change" do
|
||||
user = insert(:user)
|
||||
|
|
|
|||
|
|
@ -1,42 +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.OTPVersionTest do
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
alias Pleroma.OTPVersion
|
||||
|
||||
describe "check/1" do
|
||||
test "22.4" do
|
||||
assert OTPVersion.get_version_from_files(["test/fixtures/warnings/otp_version/22.4"]) ==
|
||||
"22.4"
|
||||
end
|
||||
|
||||
test "22.1" do
|
||||
assert OTPVersion.get_version_from_files(["test/fixtures/warnings/otp_version/22.1"]) ==
|
||||
"22.1"
|
||||
end
|
||||
|
||||
test "21.1" do
|
||||
assert OTPVersion.get_version_from_files(["test/fixtures/warnings/otp_version/21.1"]) ==
|
||||
"21.1"
|
||||
end
|
||||
|
||||
test "23.0" do
|
||||
assert OTPVersion.get_version_from_files(["test/fixtures/warnings/otp_version/23.0"]) ==
|
||||
"23.0"
|
||||
end
|
||||
|
||||
test "with nonexistent file" do
|
||||
assert OTPVersion.get_version_from_files([
|
||||
"test/fixtures/warnings/otp_version/non-exising",
|
||||
"test/fixtures/warnings/otp_version/22.4"
|
||||
]) == "22.4"
|
||||
end
|
||||
|
||||
test "empty paths" do
|
||||
assert OTPVersion.get_version_from_files([]) == nil
|
||||
end
|
||||
end
|
||||
end
|
||||
19
test/pleroma/release_task_test.exs
Normal file
19
test/pleroma/release_task_test.exs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.ReleaseTaskTest do
|
||||
use Pleroma.DataCase, async: true
|
||||
|
||||
alias Pleroma.ReleaseTasks
|
||||
|
||||
test "finding the module" do
|
||||
task = "search.meilisearch"
|
||||
assert Mix.Tasks.Pleroma.Search.Meilisearch == ReleaseTasks.find_module(task)
|
||||
|
||||
task = "user"
|
||||
assert Mix.Tasks.Pleroma.User == ReleaseTasks.find_module(task)
|
||||
|
||||
refute ReleaseTasks.find_module("doesnt.exist")
|
||||
end
|
||||
end
|
||||
|
|
@ -3,12 +3,11 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Repo.Migrations.AutolinkerToLinkifyTest do
|
||||
use Pleroma.DataCase
|
||||
use Pleroma.DataCase, async: false
|
||||
import Pleroma.Factory
|
||||
import Pleroma.Tests.Helpers
|
||||
alias Pleroma.ConfigDB
|
||||
|
||||
setup do: clear_config(Pleroma.Formatter)
|
||||
setup_all do: require_migration("20200716195806_autolinker_to_linkify")
|
||||
|
||||
test "change/0 converts auto_linker opts for Pleroma.Formatter", %{migration: migration} do
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Repo.Migrations.PublisherMigrationChangeTest do
|
||||
use Oban.Testing, repo: Pleroma.Repo
|
||||
use Pleroma.DataCase
|
||||
import Pleroma.Factory
|
||||
import Pleroma.Tests.Helpers
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Workers.PublisherWorker
|
||||
|
||||
setup_all do: require_migration("20240729163838_publisher_job_change")
|
||||
|
||||
describe "up/0" do
|
||||
test "migrates publisher jobs to new format", %{migration: migration} do
|
||||
user = insert(:user)
|
||||
|
||||
%Activity{id: activity_id, data: %{"id" => ap_id}} =
|
||||
insert(:note_activity, user: user)
|
||||
|
||||
{:ok, %{id: job_id}} =
|
||||
PublisherWorker.new(%{
|
||||
"actor_id" => user.id,
|
||||
"json" => "{}",
|
||||
"id" => ap_id,
|
||||
"inbox" => "https://example.com/inbox",
|
||||
"unreachable_since" => nil
|
||||
})
|
||||
|> Oban.insert()
|
||||
|
||||
assert [%{id: ^job_id, args: %{"id" => ^ap_id}}] = all_enqueued(worker: PublisherWorker)
|
||||
|
||||
assert migration.up() == :ok
|
||||
|
||||
assert_enqueued(
|
||||
worker: PublisherWorker,
|
||||
args: %{"id" => ap_id, "activity_id" => activity_id}
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -24,7 +24,8 @@ defmodule Pleroma.RepoTest do
|
|||
describe "get_assoc/2" do
|
||||
test "get assoc from preloaded data" do
|
||||
user = %User{name: "Agent Smith"}
|
||||
token = %Pleroma.Web.OAuth.Token{insert(:oauth_token) | user: user}
|
||||
%Pleroma.Web.OAuth.Token{} = token = insert(:oauth_token)
|
||||
token = %{token | user: user}
|
||||
assert Repo.get_assoc(token, :user) == {:ok, user}
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ defmodule Pleroma.ResilienceTest do
|
|||
other_user = insert(:user)
|
||||
|
||||
{:ok, post_one} = CommonAPI.post(user, %{status: "Here is a post"})
|
||||
{:ok, like} = CommonAPI.favorite(other_user, post_one.id)
|
||||
{:ok, like} = CommonAPI.favorite(post_one.id, other_user)
|
||||
|
||||
%{
|
||||
user: user,
|
||||
|
|
@ -90,7 +90,7 @@ defmodule Pleroma.ResilienceTest do
|
|||
|> json_response(200)
|
||||
|
||||
# Favoriting again doesn't hurt
|
||||
{:ok, _like_two} = CommonAPI.favorite(other_user, post.id)
|
||||
{:ok, _like_two} = CommonAPI.favorite(post.id, other_user)
|
||||
|
||||
post = Repo.get(Activity, post.id)
|
||||
|
||||
|
|
|
|||
|
|
@ -63,7 +63,11 @@ defmodule Pleroma.ReverseProxyTest do
|
|||
|> Plug.Conn.put_req_header("user-agent", "fake/1.0")
|
||||
|> ReverseProxy.call("/user-agent")
|
||||
|
||||
assert json_response(conn, 200) == %{"user-agent" => Pleroma.Application.user_agent()}
|
||||
# Convert the response to a map without relying on json_response
|
||||
body = conn.resp_body
|
||||
assert conn.status == 200
|
||||
response = Jason.decode!(body)
|
||||
assert response == %{"user-agent" => Pleroma.Application.user_agent()}
|
||||
end
|
||||
|
||||
test "closed connection", %{conn: conn} do
|
||||
|
|
@ -138,11 +142,14 @@ defmodule Pleroma.ReverseProxyTest do
|
|||
test "common", %{conn: conn} do
|
||||
ClientMock
|
||||
|> expect(:request, fn :head, "/head", _, _, _ ->
|
||||
{:ok, 200, [{"content-type", "text/html; charset=utf-8"}]}
|
||||
{:ok, 200, [{"content-type", "image/png"}]}
|
||||
end)
|
||||
|
||||
conn = ReverseProxy.call(Map.put(conn, :method, "HEAD"), "/head")
|
||||
assert html_response(conn, 200) == ""
|
||||
|
||||
assert conn.status == 200
|
||||
assert Conn.get_resp_header(conn, "content-type") == ["image/png"]
|
||||
assert conn.resp_body == ""
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -249,7 +256,10 @@ defmodule Pleroma.ReverseProxyTest do
|
|||
)
|
||||
|> ReverseProxy.call("/headers")
|
||||
|
||||
%{"headers" => headers} = json_response(conn, 200)
|
||||
body = conn.resp_body
|
||||
assert conn.status == 200
|
||||
response = Jason.decode!(body)
|
||||
headers = response["headers"]
|
||||
assert headers["Accept"] == "text/html"
|
||||
end
|
||||
|
||||
|
|
@ -262,7 +272,10 @@ defmodule Pleroma.ReverseProxyTest do
|
|||
)
|
||||
|> ReverseProxy.call("/headers")
|
||||
|
||||
%{"headers" => headers} = json_response(conn, 200)
|
||||
body = conn.resp_body
|
||||
assert conn.status == 200
|
||||
response = Jason.decode!(body)
|
||||
headers = response["headers"]
|
||||
refute headers["Accept-Language"]
|
||||
end
|
||||
end
|
||||
|
|
@ -328,4 +341,121 @@ defmodule Pleroma.ReverseProxyTest do
|
|||
assert {"content-disposition", "attachment; filename=\"filename.jpg\""} in conn.resp_headers
|
||||
end
|
||||
end
|
||||
|
||||
describe "content-type sanitisation" do
|
||||
test "preserves allowed image type", %{conn: conn} do
|
||||
ClientMock
|
||||
|> expect(:request, fn :get, "/content", _, _, _ ->
|
||||
{:ok, 200, [{"content-type", "image/png"}], %{url: "/content"}}
|
||||
end)
|
||||
|> expect(:stream_body, fn _ -> :done end)
|
||||
|
||||
conn = ReverseProxy.call(conn, "/content")
|
||||
|
||||
assert conn.status == 200
|
||||
assert Conn.get_resp_header(conn, "content-type") == ["image/png"]
|
||||
end
|
||||
|
||||
test "preserves allowed video type", %{conn: conn} do
|
||||
ClientMock
|
||||
|> expect(:request, fn :get, "/content", _, _, _ ->
|
||||
{:ok, 200, [{"content-type", "video/mp4"}], %{url: "/content"}}
|
||||
end)
|
||||
|> expect(:stream_body, fn _ -> :done end)
|
||||
|
||||
conn = ReverseProxy.call(conn, "/content")
|
||||
|
||||
assert conn.status == 200
|
||||
assert Conn.get_resp_header(conn, "content-type") == ["video/mp4"]
|
||||
end
|
||||
|
||||
test "sanitizes ActivityPub content type", %{conn: conn} do
|
||||
ClientMock
|
||||
|> expect(:request, fn :get, "/content", _, _, _ ->
|
||||
{:ok, 200, [{"content-type", "application/activity+json"}], %{url: "/content"}}
|
||||
end)
|
||||
|> expect(:stream_body, fn _ -> :done end)
|
||||
|
||||
conn = ReverseProxy.call(conn, "/content")
|
||||
|
||||
assert conn.status == 200
|
||||
assert Conn.get_resp_header(conn, "content-type") == ["application/octet-stream"]
|
||||
end
|
||||
|
||||
test "sanitizes LD-JSON content type", %{conn: conn} do
|
||||
ClientMock
|
||||
|> expect(:request, fn :get, "/content", _, _, _ ->
|
||||
{:ok, 200, [{"content-type", "application/ld+json"}], %{url: "/content"}}
|
||||
end)
|
||||
|> expect(:stream_body, fn _ -> :done end)
|
||||
|
||||
conn = ReverseProxy.call(conn, "/content")
|
||||
|
||||
assert conn.status == 200
|
||||
assert Conn.get_resp_header(conn, "content-type") == ["application/octet-stream"]
|
||||
end
|
||||
end
|
||||
|
||||
# Hackney is used for Reverse Proxy when Hackney or Finch is the Tesla Adapter
|
||||
# Gun is able to proxy through Tesla, so it does not need testing as the
|
||||
# test cases in the Pleroma.HTTPTest module are sufficient
|
||||
describe "Hackney URL encoding:" do
|
||||
setup do
|
||||
ClientMock
|
||||
|> expect(:request, fn
|
||||
:get,
|
||||
"https://example.com/emoji/Pack%201/koronebless.png?foo=bar+baz",
|
||||
_headers,
|
||||
_body,
|
||||
_opts ->
|
||||
{:ok, 200, [{"content-type", "image/png"}], "It works!"}
|
||||
|
||||
:get,
|
||||
"https://example.com/media/foo/bar%20!$&'()*+,;=/:%20@a%20%5Bbaz%5D.mp4",
|
||||
_headers,
|
||||
_body,
|
||||
_opts ->
|
||||
{:ok, 200, [{"content-type", "video/mp4"}], "Allowed reserved chars."}
|
||||
|
||||
:get, "https://example.com/media/unicode%20%F0%9F%99%82%20.gif", _headers, _body, _opts ->
|
||||
{:ok, 200, [{"content-type", "image/gif"}], "Unicode emoji in path"}
|
||||
end)
|
||||
|> stub(:stream_body, fn _ -> :done end)
|
||||
|> stub(:close, fn _ -> :ok end)
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
test "properly encodes URLs with spaces", %{conn: conn} do
|
||||
url_with_space = "https://example.com/emoji/Pack 1/koronebless.png?foo=bar baz"
|
||||
|
||||
result = ReverseProxy.call(conn, url_with_space)
|
||||
|
||||
assert result.status == 200
|
||||
end
|
||||
|
||||
test "properly encoded URL should not be altered", %{conn: conn} do
|
||||
properly_encoded_url = "https://example.com/emoji/Pack%201/koronebless.png?foo=bar+baz"
|
||||
|
||||
result = ReverseProxy.call(conn, properly_encoded_url)
|
||||
|
||||
assert result.status == 200
|
||||
end
|
||||
|
||||
test "properly encodes URLs with allowed reserved characters", %{conn: conn} do
|
||||
url_with_reserved_chars = "https://example.com/media/foo/bar !$&'()*+,;=/: @a [baz].mp4"
|
||||
|
||||
result = ReverseProxy.call(conn, url_with_reserved_chars)
|
||||
|
||||
assert result.status == 200
|
||||
end
|
||||
|
||||
test "properly encodes URLs with unicode in path", %{conn: conn} do
|
||||
url_with_unicode = "https://example.com/media/unicode 🙂 .gif"
|
||||
|
||||
result = ReverseProxy.call(conn, url_with_unicode)
|
||||
|
||||
assert result.status == 200
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
496
test/pleroma/safe_zip_test.exs
Normal file
496
test/pleroma/safe_zip_test.exs
Normal file
|
|
@ -0,0 +1,496 @@
|
|||
defmodule Pleroma.SafeZipTest do
|
||||
# Not making this async because it creates and deletes files
|
||||
use ExUnit.Case
|
||||
|
||||
alias Pleroma.SafeZip
|
||||
|
||||
@fixtures_dir "test/fixtures"
|
||||
@tmp_dir "test/zip_tmp"
|
||||
|
||||
setup do
|
||||
# Ensure tmp directory exists
|
||||
Pleroma.Backports.mkdir_p!(@tmp_dir)
|
||||
|
||||
on_exit(fn ->
|
||||
# Clean up any files created during tests
|
||||
File.rm_rf!(@tmp_dir)
|
||||
Pleroma.Backports.mkdir_p!(@tmp_dir)
|
||||
end)
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
describe "list_dir_file/1" do
|
||||
test "lists files in a valid zip" do
|
||||
{:ok, files} = SafeZip.list_dir_file(Path.join(@fixtures_dir, "emojis.zip"))
|
||||
assert is_list(files)
|
||||
assert length(files) > 0
|
||||
end
|
||||
|
||||
test "returns an empty list for empty zip" do
|
||||
{:ok, files} = SafeZip.list_dir_file(Path.join(@fixtures_dir, "empty.zip"))
|
||||
assert files == []
|
||||
end
|
||||
|
||||
test "returns error for non-existent file" do
|
||||
assert {:error, _} = SafeZip.list_dir_file(Path.join(@fixtures_dir, "nonexistent.zip"))
|
||||
end
|
||||
|
||||
test "only lists regular files, not directories" do
|
||||
# Create a zip with both files and directories
|
||||
zip_path = create_zip_with_directory()
|
||||
|
||||
# List files with SafeZip
|
||||
{:ok, files} = SafeZip.list_dir_file(zip_path)
|
||||
|
||||
# Verify only regular files are listed, not directories
|
||||
assert "file_in_dir/test_file.txt" in files
|
||||
assert "root_file.txt" in files
|
||||
|
||||
# Directory entries should not be included in the list
|
||||
refute "file_in_dir/" in files
|
||||
end
|
||||
end
|
||||
|
||||
describe "contains_all_data?/2" do
|
||||
test "returns true when all files are in the archive" do
|
||||
# For this test, we'll create our own zip file with known content
|
||||
# to ensure we can test the contains_all_data? function properly
|
||||
zip_path = create_zip_with_directory()
|
||||
archive_data = File.read!(zip_path)
|
||||
|
||||
# Check if the archive contains the root file
|
||||
# Note: The function expects charlists (Erlang strings) in the MapSet
|
||||
assert SafeZip.contains_all_data?(archive_data, MapSet.new([~c"root_file.txt"]))
|
||||
end
|
||||
|
||||
test "returns false when files are missing" do
|
||||
archive_path = Path.join(@fixtures_dir, "emojis.zip")
|
||||
archive_data = File.read!(archive_path)
|
||||
|
||||
# Create a MapSet with non-existent files
|
||||
fset = MapSet.new([~c"nonexistent.txt"])
|
||||
|
||||
refute SafeZip.contains_all_data?(archive_data, fset)
|
||||
end
|
||||
|
||||
test "returns false for invalid archive data" do
|
||||
refute SafeZip.contains_all_data?("invalid data", MapSet.new([~c"file.txt"]))
|
||||
end
|
||||
|
||||
test "only checks for regular files, not directories" do
|
||||
# Create a zip with both files and directories
|
||||
zip_path = create_zip_with_directory()
|
||||
archive_data = File.read!(zip_path)
|
||||
|
||||
# Check if the archive contains a directory (should return false)
|
||||
refute SafeZip.contains_all_data?(archive_data, MapSet.new([~c"file_in_dir/"]))
|
||||
|
||||
# For this test, we'll manually check if the file exists in the archive
|
||||
# by extracting it and verifying it exists
|
||||
extract_dir = Path.join(@tmp_dir, "extract_check")
|
||||
Pleroma.Backports.mkdir_p!(extract_dir)
|
||||
{:ok, files} = SafeZip.unzip_file(zip_path, extract_dir)
|
||||
|
||||
# Verify the root file was extracted
|
||||
assert Enum.any?(files, fn file ->
|
||||
Path.basename(file) == "root_file.txt"
|
||||
end)
|
||||
|
||||
# Verify the file exists on disk
|
||||
assert File.exists?(Path.join(extract_dir, "root_file.txt"))
|
||||
end
|
||||
end
|
||||
|
||||
describe "zip/4" do
|
||||
test "creates a zip file on disk" do
|
||||
# Create a test file
|
||||
test_file_path = Path.join(@tmp_dir, "test_file.txt")
|
||||
File.write!(test_file_path, "test content")
|
||||
|
||||
# Create a zip file
|
||||
zip_path = Path.join(@tmp_dir, "test.zip")
|
||||
assert {:ok, ^zip_path} = SafeZip.zip(zip_path, ["test_file.txt"], @tmp_dir, false)
|
||||
|
||||
# Verify the zip file exists
|
||||
assert File.exists?(zip_path)
|
||||
end
|
||||
|
||||
test "creates a zip file in memory" do
|
||||
# Create a test file
|
||||
test_file_path = Path.join(@tmp_dir, "test_file.txt")
|
||||
File.write!(test_file_path, "test content")
|
||||
|
||||
# Create a zip file in memory
|
||||
zip_name = Path.join(@tmp_dir, "test.zip")
|
||||
|
||||
assert {:ok, {^zip_name, zip_data}} =
|
||||
SafeZip.zip(zip_name, ["test_file.txt"], @tmp_dir, true)
|
||||
|
||||
# Verify the zip data is binary
|
||||
assert is_binary(zip_data)
|
||||
end
|
||||
|
||||
test "returns error for unsafe paths" do
|
||||
# Try to zip a file with path traversal
|
||||
assert {:error, _} =
|
||||
SafeZip.zip(
|
||||
Path.join(@tmp_dir, "test.zip"),
|
||||
["../fixtures/test.txt"],
|
||||
@tmp_dir,
|
||||
false
|
||||
)
|
||||
end
|
||||
|
||||
test "can create zip with directories" do
|
||||
# Create a directory structure
|
||||
dir_path = Path.join(@tmp_dir, "test_dir")
|
||||
Pleroma.Backports.mkdir_p!(dir_path)
|
||||
|
||||
file_in_dir_path = Path.join(dir_path, "file_in_dir.txt")
|
||||
File.write!(file_in_dir_path, "file in directory")
|
||||
|
||||
# Create a zip file
|
||||
zip_path = Path.join(@tmp_dir, "dir_test.zip")
|
||||
|
||||
assert {:ok, ^zip_path} =
|
||||
SafeZip.zip(
|
||||
zip_path,
|
||||
["test_dir/file_in_dir.txt"],
|
||||
@tmp_dir,
|
||||
false
|
||||
)
|
||||
|
||||
# Verify the zip file exists
|
||||
assert File.exists?(zip_path)
|
||||
|
||||
# Extract and verify the directory structure is preserved
|
||||
extract_dir = Path.join(@tmp_dir, "extract")
|
||||
{:ok, files} = SafeZip.unzip_file(zip_path, extract_dir)
|
||||
|
||||
# Check if the file path is in the list, accounting for possible full paths
|
||||
assert Enum.any?(files, fn file ->
|
||||
String.ends_with?(file, "file_in_dir.txt")
|
||||
end)
|
||||
|
||||
# Verify the file exists in the expected location
|
||||
assert File.exists?(Path.join([extract_dir, "test_dir", "file_in_dir.txt"]))
|
||||
end
|
||||
end
|
||||
|
||||
describe "unzip_file/3" do
|
||||
test "extracts files from a zip archive" do
|
||||
archive_path = Path.join(@fixtures_dir, "emojis.zip")
|
||||
|
||||
# Extract the archive
|
||||
assert {:ok, files} = SafeZip.unzip_file(archive_path, @tmp_dir)
|
||||
|
||||
# Verify files were extracted
|
||||
assert is_list(files)
|
||||
assert length(files) > 0
|
||||
|
||||
# Verify at least one file exists
|
||||
first_file = List.first(files)
|
||||
|
||||
# Simply check that the file exists in the tmp directory
|
||||
assert File.exists?(first_file)
|
||||
end
|
||||
|
||||
test "extracts specific files from a zip archive" do
|
||||
archive_path = Path.join(@fixtures_dir, "emojis.zip")
|
||||
|
||||
# Get list of files in the archive
|
||||
{:ok, all_files} = SafeZip.list_dir_file(archive_path)
|
||||
file_to_extract = List.first(all_files)
|
||||
|
||||
# Extract only one file
|
||||
assert {:ok, [extracted_file]} =
|
||||
SafeZip.unzip_file(archive_path, @tmp_dir, [file_to_extract])
|
||||
|
||||
# Verify only the specified file was extracted
|
||||
assert Path.basename(extracted_file) == Path.basename(file_to_extract)
|
||||
|
||||
# Check that the file exists in the tmp directory
|
||||
assert File.exists?(Path.join(@tmp_dir, Path.basename(file_to_extract)))
|
||||
end
|
||||
|
||||
test "returns error for invalid zip file" do
|
||||
invalid_path = Path.join(@tmp_dir, "invalid.zip")
|
||||
File.write!(invalid_path, "not a zip file")
|
||||
|
||||
assert {:error, _} = SafeZip.unzip_file(invalid_path, @tmp_dir)
|
||||
end
|
||||
|
||||
test "creates directories when extracting files in subdirectories" do
|
||||
# Create a zip with files in subdirectories
|
||||
zip_path = create_zip_with_directory()
|
||||
|
||||
# Extract the archive
|
||||
assert {:ok, files} = SafeZip.unzip_file(zip_path, @tmp_dir)
|
||||
|
||||
# Verify files were extracted - handle both relative and absolute paths
|
||||
assert Enum.any?(files, fn file ->
|
||||
Path.basename(file) == "test_file.txt" &&
|
||||
String.contains?(file, "file_in_dir")
|
||||
end)
|
||||
|
||||
assert Enum.any?(files, fn file ->
|
||||
Path.basename(file) == "root_file.txt"
|
||||
end)
|
||||
|
||||
# Verify directory was created
|
||||
dir_path = Path.join(@tmp_dir, "file_in_dir")
|
||||
assert File.exists?(dir_path)
|
||||
assert File.dir?(dir_path)
|
||||
|
||||
# Verify file in directory was extracted
|
||||
file_path = Path.join(dir_path, "test_file.txt")
|
||||
assert File.exists?(file_path)
|
||||
end
|
||||
end
|
||||
|
||||
describe "unzip_data/3" do
|
||||
test "extracts files from zip data" do
|
||||
archive_path = Path.join(@fixtures_dir, "emojis.zip")
|
||||
archive_data = File.read!(archive_path)
|
||||
|
||||
# Extract the archive from data
|
||||
assert {:ok, files} = SafeZip.unzip_data(archive_data, @tmp_dir)
|
||||
|
||||
# Verify files were extracted
|
||||
assert is_list(files)
|
||||
assert length(files) > 0
|
||||
|
||||
# Verify at least one file exists
|
||||
first_file = List.first(files)
|
||||
|
||||
# Simply check that the file exists in the tmp directory
|
||||
assert File.exists?(first_file)
|
||||
end
|
||||
|
||||
test "extracts specific files from zip data" do
|
||||
archive_path = Path.join(@fixtures_dir, "emojis.zip")
|
||||
archive_data = File.read!(archive_path)
|
||||
|
||||
# Get list of files in the archive
|
||||
{:ok, all_files} = SafeZip.list_dir_file(archive_path)
|
||||
file_to_extract = List.first(all_files)
|
||||
|
||||
# Extract only one file
|
||||
assert {:ok, extracted_files} =
|
||||
SafeZip.unzip_data(archive_data, @tmp_dir, [file_to_extract])
|
||||
|
||||
# Verify only the specified file was extracted
|
||||
assert Enum.any?(extracted_files, fn path ->
|
||||
Path.basename(path) == Path.basename(file_to_extract)
|
||||
end)
|
||||
|
||||
# Simply check that the file exists in the tmp directory
|
||||
assert File.exists?(Path.join(@tmp_dir, Path.basename(file_to_extract)))
|
||||
end
|
||||
|
||||
test "returns error for invalid zip data" do
|
||||
assert {:error, _} = SafeZip.unzip_data("not a zip file", @tmp_dir)
|
||||
end
|
||||
|
||||
test "creates directories when extracting files in subdirectories from data" do
|
||||
# Create a zip with files in subdirectories
|
||||
zip_path = create_zip_with_directory()
|
||||
archive_data = File.read!(zip_path)
|
||||
|
||||
# Extract the archive from data
|
||||
assert {:ok, files} = SafeZip.unzip_data(archive_data, @tmp_dir)
|
||||
|
||||
# Verify files were extracted - handle both relative and absolute paths
|
||||
assert Enum.any?(files, fn file ->
|
||||
Path.basename(file) == "test_file.txt" &&
|
||||
String.contains?(file, "file_in_dir")
|
||||
end)
|
||||
|
||||
assert Enum.any?(files, fn file ->
|
||||
Path.basename(file) == "root_file.txt"
|
||||
end)
|
||||
|
||||
# Verify directory was created
|
||||
dir_path = Path.join(@tmp_dir, "file_in_dir")
|
||||
assert File.exists?(dir_path)
|
||||
assert File.dir?(dir_path)
|
||||
|
||||
# Verify file in directory was extracted
|
||||
file_path = Path.join(dir_path, "test_file.txt")
|
||||
assert File.exists?(file_path)
|
||||
end
|
||||
end
|
||||
|
||||
# Security tests
|
||||
describe "security checks" do
|
||||
test "prevents path traversal in zip extraction" do
|
||||
# Create a malicious zip file with path traversal
|
||||
malicious_zip_path = create_malicious_zip_with_path_traversal()
|
||||
|
||||
# Try to extract it with SafeZip
|
||||
assert {:error, _} = SafeZip.unzip_file(malicious_zip_path, @tmp_dir)
|
||||
|
||||
# Verify the file was not extracted outside the target directory
|
||||
refute File.exists?(Path.join(Path.dirname(@tmp_dir), "traversal_attempt.txt"))
|
||||
end
|
||||
|
||||
test "prevents directory traversal in zip listing" do
|
||||
# Create a malicious zip file with path traversal
|
||||
malicious_zip_path = create_malicious_zip_with_path_traversal()
|
||||
|
||||
# Try to list files with SafeZip
|
||||
assert {:error, _} = SafeZip.list_dir_file(malicious_zip_path)
|
||||
end
|
||||
|
||||
test "prevents path traversal in zip data extraction" do
|
||||
# Create a malicious zip file with path traversal
|
||||
malicious_zip_path = create_malicious_zip_with_path_traversal()
|
||||
malicious_data = File.read!(malicious_zip_path)
|
||||
|
||||
# Try to extract it with SafeZip
|
||||
assert {:error, _} = SafeZip.unzip_data(malicious_data, @tmp_dir)
|
||||
|
||||
# Verify the file was not extracted outside the target directory
|
||||
refute File.exists?(Path.join(Path.dirname(@tmp_dir), "traversal_attempt.txt"))
|
||||
end
|
||||
|
||||
test "handles zip bomb attempts" do
|
||||
# Create a zip bomb (a zip with many files or large files)
|
||||
zip_bomb_path = create_zip_bomb()
|
||||
|
||||
# The SafeZip module should handle this gracefully
|
||||
# Either by successfully extracting it (if it's not too large)
|
||||
# or by returning an error (if it detects a potential zip bomb)
|
||||
result = SafeZip.unzip_file(zip_bomb_path, @tmp_dir)
|
||||
|
||||
case result do
|
||||
{:ok, _} ->
|
||||
# If it successfully extracts, make sure it didn't fill up the disk
|
||||
# This is a simple check to ensure the extraction was controlled
|
||||
assert File.exists?(@tmp_dir)
|
||||
|
||||
{:error, _} ->
|
||||
# If it returns an error, that's also acceptable
|
||||
# The important thing is that it doesn't crash or hang
|
||||
assert true
|
||||
end
|
||||
end
|
||||
|
||||
test "handles deeply nested directory structures" do
|
||||
# Create a zip with deeply nested directories
|
||||
deep_nest_path = create_deeply_nested_zip()
|
||||
|
||||
# The SafeZip module should handle this gracefully
|
||||
result = SafeZip.unzip_file(deep_nest_path, @tmp_dir)
|
||||
|
||||
case result do
|
||||
{:ok, files} ->
|
||||
# If it successfully extracts, verify the files were extracted
|
||||
assert is_list(files)
|
||||
assert length(files) > 0
|
||||
|
||||
{:error, _} ->
|
||||
# If it returns an error, that's also acceptable
|
||||
# The important thing is that it doesn't crash or hang
|
||||
assert true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Helper functions to create test fixtures
|
||||
|
||||
# Creates a zip file with a path traversal attempt
|
||||
defp create_malicious_zip_with_path_traversal do
|
||||
malicious_zip_path = Path.join(@tmp_dir, "path_traversal.zip")
|
||||
|
||||
# Create a file to include in the zip
|
||||
test_file_path = Path.join(@tmp_dir, "test_file.txt")
|
||||
File.write!(test_file_path, "malicious content")
|
||||
|
||||
# Use Erlang's zip module directly to create a zip with path traversal
|
||||
{:ok, charlist_path} =
|
||||
:zip.create(
|
||||
String.to_charlist(malicious_zip_path),
|
||||
[{String.to_charlist("../traversal_attempt.txt"), File.read!(test_file_path)}]
|
||||
)
|
||||
|
||||
to_string(charlist_path)
|
||||
end
|
||||
|
||||
# Creates a zip file with directory entries
|
||||
defp create_zip_with_directory do
|
||||
zip_path = Path.join(@tmp_dir, "with_directory.zip")
|
||||
|
||||
# Create files to include in the zip
|
||||
root_file_path = Path.join(@tmp_dir, "root_file.txt")
|
||||
File.write!(root_file_path, "root file content")
|
||||
|
||||
# Create a directory and a file in it
|
||||
dir_path = Path.join(@tmp_dir, "file_in_dir")
|
||||
Pleroma.Backports.mkdir_p!(dir_path)
|
||||
|
||||
file_in_dir_path = Path.join(dir_path, "test_file.txt")
|
||||
File.write!(file_in_dir_path, "file in directory content")
|
||||
|
||||
# Use Erlang's zip module to create a zip with directory structure
|
||||
{:ok, charlist_path} =
|
||||
:zip.create(
|
||||
String.to_charlist(zip_path),
|
||||
[
|
||||
{String.to_charlist("root_file.txt"), File.read!(root_file_path)},
|
||||
{String.to_charlist("file_in_dir/test_file.txt"), File.read!(file_in_dir_path)}
|
||||
]
|
||||
)
|
||||
|
||||
to_string(charlist_path)
|
||||
end
|
||||
|
||||
# Creates a zip bomb (a zip with many small files)
|
||||
defp create_zip_bomb do
|
||||
zip_path = Path.join(@tmp_dir, "zip_bomb.zip")
|
||||
|
||||
# Create a small file to duplicate many times
|
||||
small_file_path = Path.join(@tmp_dir, "small_file.txt")
|
||||
File.write!(small_file_path, String.duplicate("A", 100))
|
||||
|
||||
# Create a list of many files to include in the zip
|
||||
file_entries =
|
||||
for i <- 1..100 do
|
||||
{String.to_charlist("file_#{i}.txt"), File.read!(small_file_path)}
|
||||
end
|
||||
|
||||
# Use Erlang's zip module to create a zip with many files
|
||||
{:ok, charlist_path} =
|
||||
:zip.create(
|
||||
String.to_charlist(zip_path),
|
||||
file_entries
|
||||
)
|
||||
|
||||
to_string(charlist_path)
|
||||
end
|
||||
|
||||
# Creates a zip with deeply nested directories
|
||||
defp create_deeply_nested_zip do
|
||||
zip_path = Path.join(@tmp_dir, "deep_nest.zip")
|
||||
|
||||
# Create a file to include in the zip
|
||||
file_content = "test content"
|
||||
|
||||
# Create a list of deeply nested files
|
||||
file_entries =
|
||||
for i <- 1..10 do
|
||||
nested_path = Enum.reduce(1..i, "nested", fn j, acc -> "#{acc}/level_#{j}" end)
|
||||
{String.to_charlist("#{nested_path}/file.txt"), file_content}
|
||||
end
|
||||
|
||||
# Use Erlang's zip module to create a zip with deeply nested directories
|
||||
{:ok, charlist_path} =
|
||||
:zip.create(
|
||||
String.to_charlist(zip_path),
|
||||
file_entries
|
||||
)
|
||||
|
||||
to_string(charlist_path)
|
||||
end
|
||||
end
|
||||
|
|
@ -51,7 +51,7 @@ defmodule Pleroma.Search.QdrantSearchTest do
|
|||
})
|
||||
|
||||
Config
|
||||
|> expect(:get, 3, fn
|
||||
|> expect(:get, 4, fn
|
||||
[Pleroma.Search, :module], nil ->
|
||||
QdrantSearch
|
||||
|
||||
|
|
@ -93,7 +93,7 @@ defmodule Pleroma.Search.QdrantSearchTest do
|
|||
})
|
||||
|
||||
Config
|
||||
|> expect(:get, 3, fn
|
||||
|> expect(:get, 4, fn
|
||||
[Pleroma.Search, :module], nil ->
|
||||
QdrantSearch
|
||||
|
||||
|
|
@ -158,7 +158,7 @@ defmodule Pleroma.Search.QdrantSearchTest do
|
|||
end)
|
||||
|
||||
Config
|
||||
|> expect(:get, 6, fn
|
||||
|> expect(:get, 7, fn
|
||||
[Pleroma.Search, :module], nil ->
|
||||
QdrantSearch
|
||||
|
||||
|
|
|
|||
|
|
@ -73,8 +73,8 @@ defmodule Pleroma.StatsTest do
|
|||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
{:ok, activity} = CommonAPI.post(user, %{visibility: "public", status: "hey"})
|
||||
_ = CommonAPI.follow(user, other_user)
|
||||
CommonAPI.favorite(other_user, activity.id)
|
||||
_ = CommonAPI.follow(other_user, user)
|
||||
CommonAPI.favorite(activity.id, other_user)
|
||||
CommonAPI.repeat(activity.id, other_user)
|
||||
|
||||
assert %{"direct" => 0, "private" => 0, "public" => 1, "unlisted" => 0} =
|
||||
|
|
|
|||
|
|
@ -34,6 +34,20 @@ defmodule Pleroma.Upload.Filter.AnalyzeMetadataTest do
|
|||
assert meta.blurhash == "eXJi-E:SwCEm5rCmn$+YWYn+15K#5A$xxCi{SiV]s*W:Efa#s.jE-T"
|
||||
end
|
||||
|
||||
test "it gets dimensions for grayscale images" do
|
||||
upload = %Pleroma.Upload{
|
||||
name: "break_analyze.png",
|
||||
content_type: "image/png",
|
||||
path: Path.absname("test/fixtures/break_analyze.png"),
|
||||
tempfile: Path.absname("test/fixtures/break_analyze.png")
|
||||
}
|
||||
|
||||
{:ok, :filtered, meta} = AnalyzeMetadata.filter(upload)
|
||||
|
||||
assert %{width: 1410, height: 2048} = meta
|
||||
assert is_nil(meta.blurhash)
|
||||
end
|
||||
|
||||
test "adds the dimensions for videos" do
|
||||
upload = %Pleroma.Upload{
|
||||
name: "coolvideo.mp4",
|
||||
|
|
|
|||
|
|
@ -3,8 +3,10 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Upload.Filter.AnonymizeFilenameTest do
|
||||
use Pleroma.DataCase
|
||||
use Pleroma.DataCase, async: true
|
||||
|
||||
import Mox
|
||||
alias Pleroma.StaticStubbedConfigMock, as: ConfigMock
|
||||
alias Pleroma.Upload
|
||||
|
||||
setup do
|
||||
|
|
@ -19,21 +21,26 @@ defmodule Pleroma.Upload.Filter.AnonymizeFilenameTest do
|
|||
%{upload_file: upload_file}
|
||||
end
|
||||
|
||||
setup do: clear_config([Pleroma.Upload.Filter.AnonymizeFilename, :text])
|
||||
|
||||
test "it replaces filename on pre-defined text", %{upload_file: upload_file} do
|
||||
clear_config([Upload.Filter.AnonymizeFilename, :text], "custom-file.png")
|
||||
ConfigMock
|
||||
|> stub(:get, fn [Upload.Filter.AnonymizeFilename, :text] -> "custom-file.png" end)
|
||||
|
||||
{:ok, :filtered, %Upload{name: name}} = Upload.Filter.AnonymizeFilename.filter(upload_file)
|
||||
assert name == "custom-file.png"
|
||||
end
|
||||
|
||||
test "it replaces filename on pre-defined text expression", %{upload_file: upload_file} do
|
||||
clear_config([Upload.Filter.AnonymizeFilename, :text], "custom-file.{extension}")
|
||||
ConfigMock
|
||||
|> stub(:get, fn [Upload.Filter.AnonymizeFilename, :text] -> "custom-file.{extension}" end)
|
||||
|
||||
{:ok, :filtered, %Upload{name: name}} = Upload.Filter.AnonymizeFilename.filter(upload_file)
|
||||
assert name == "custom-file.jpg"
|
||||
end
|
||||
|
||||
test "it replaces filename on random text", %{upload_file: upload_file} do
|
||||
ConfigMock
|
||||
|> stub(:get, fn [Upload.Filter.AnonymizeFilename, :text] -> nil end)
|
||||
|
||||
{:ok, :filtered, %Upload{name: name}} = Upload.Filter.AnonymizeFilename.filter(upload_file)
|
||||
assert <<_::bytes-size(14)>> <> ".jpg" = name
|
||||
refute name == "an… image.jpg"
|
||||
|
|
|
|||
|
|
@ -10,6 +10,10 @@ defmodule Pleroma.Upload.Filter.DedupeTest do
|
|||
|
||||
@shasum "e30397b58d226d6583ab5b8b3c5defb0c682bda5c31ef07a9f57c1c4986e3781"
|
||||
|
||||
test "generates a shard path for a shasum" do
|
||||
assert "e3/03/97/" <> _path = Dedupe.shard_path(@shasum)
|
||||
end
|
||||
|
||||
test "adds shasum" do
|
||||
File.cp!(
|
||||
"test/fixtures/image.jpg",
|
||||
|
|
@ -23,10 +27,12 @@ defmodule Pleroma.Upload.Filter.DedupeTest do
|
|||
tempfile: Path.absname("test/fixtures/image_tmp.jpg")
|
||||
}
|
||||
|
||||
expected_path = Dedupe.shard_path(@shasum <> ".jpg")
|
||||
|
||||
assert {
|
||||
:ok,
|
||||
:filtered,
|
||||
%Pleroma.Upload{id: @shasum, path: @shasum <> ".jpg"}
|
||||
%Pleroma.Upload{id: @shasum, path: ^expected_path}
|
||||
} = Dedupe.filter(upload)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -9,29 +9,31 @@ defmodule Pleroma.Upload.Filter.Exiftool.StripLocationTest do
|
|||
test "apply exiftool filter" do
|
||||
assert Pleroma.Utils.command_available?("exiftool")
|
||||
|
||||
File.cp!(
|
||||
"test/fixtures/DSCN0010.jpg",
|
||||
"test/fixtures/DSCN0010_tmp.jpg"
|
||||
)
|
||||
~w{jpg png}
|
||||
|> Enum.map(fn type ->
|
||||
File.cp!(
|
||||
"test/fixtures/DSCN0010.#{type}",
|
||||
"test/fixtures/DSCN0010_tmp.#{type}"
|
||||
)
|
||||
|
||||
upload = %Pleroma.Upload{
|
||||
name: "image_with_GPS_data.jpg",
|
||||
content_type: "image/jpeg",
|
||||
path: Path.absname("test/fixtures/DSCN0010.jpg"),
|
||||
tempfile: Path.absname("test/fixtures/DSCN0010_tmp.jpg")
|
||||
}
|
||||
upload = %Pleroma.Upload{
|
||||
name: "image_with_GPS_data.#{type}",
|
||||
content_type: "image/jpeg",
|
||||
path: Path.absname("test/fixtures/DSCN0010.#{type}"),
|
||||
tempfile: Path.absname("test/fixtures/DSCN0010_tmp.#{type}")
|
||||
}
|
||||
|
||||
assert Filter.Exiftool.StripLocation.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"])
|
||||
{exif_original, 0} = System.cmd("exiftool", ["-m", "test/fixtures/DSCN0010.#{type}"])
|
||||
{exif_filtered, 0} = System.cmd("exiftool", ["-m", "test/fixtures/DSCN0010_tmp.#{type}"])
|
||||
|
||||
refute exif_original == exif_filtered
|
||||
assert String.match?(exif_original, ~r/GPS/)
|
||||
refute String.match?(exif_filtered, ~r/GPS/)
|
||||
assert String.match?(exif_original, ~r/GPS/)
|
||||
refute String.match?(exif_filtered, ~r/GPS/)
|
||||
end)
|
||||
end
|
||||
|
||||
test "verify webp, heic, svg files are skipped" do
|
||||
test "verify webp, heic, svg files are skipped" do
|
||||
uploads =
|
||||
~w{webp heic svg svg+xml}
|
||||
|> Enum.map(fn type ->
|
||||
|
|
|
|||
|
|
@ -3,9 +3,10 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Upload.Filter.MogrifunTest do
|
||||
use Pleroma.DataCase
|
||||
import Mock
|
||||
use Pleroma.DataCase, async: true
|
||||
import Mox
|
||||
|
||||
alias Pleroma.MogrifyMock
|
||||
alias Pleroma.Upload
|
||||
alias Pleroma.Upload.Filter
|
||||
|
||||
|
|
@ -22,23 +23,12 @@ defmodule Pleroma.Upload.Filter.MogrifunTest do
|
|||
tempfile: Path.absname("test/fixtures/image_tmp.jpg")
|
||||
}
|
||||
|
||||
task =
|
||||
Task.async(fn ->
|
||||
assert_receive {:apply_filter, {}}, 4_000
|
||||
end)
|
||||
MogrifyMock
|
||||
|> stub(:open, fn _file -> %{} end)
|
||||
|> stub(:custom, fn _image, _action -> %{} end)
|
||||
|> stub(:custom, fn _image, _action, _options -> %{} end)
|
||||
|> stub(:save, fn _image, [in_place: true] -> :ok end)
|
||||
|
||||
with_mocks([
|
||||
{Mogrify, [],
|
||||
[
|
||||
open: fn _f -> %Mogrify.Image{} end,
|
||||
custom: fn _m, _a -> send(task.pid, {:apply_filter, {}}) end,
|
||||
custom: fn _m, _a, _o -> send(task.pid, {:apply_filter, {}}) end,
|
||||
save: fn _f, _o -> :ok end
|
||||
]}
|
||||
]) do
|
||||
assert Filter.Mogrifun.filter(upload) == {:ok, :filtered}
|
||||
end
|
||||
|
||||
Task.await(task)
|
||||
assert Filter.Mogrifun.filter(upload) == {:ok, :filtered}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3,13 +3,18 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Upload.Filter.MogrifyTest do
|
||||
use Pleroma.DataCase
|
||||
import Mock
|
||||
use Pleroma.DataCase, async: true
|
||||
import Mox
|
||||
|
||||
alias Pleroma.MogrifyMock
|
||||
alias Pleroma.StaticStubbedConfigMock, as: ConfigMock
|
||||
alias Pleroma.Upload.Filter
|
||||
|
||||
setup :verify_on_exit!
|
||||
|
||||
test "apply mogrify filter" do
|
||||
clear_config(Filter.Mogrify, args: [{"tint", "40"}])
|
||||
ConfigMock
|
||||
|> stub(:get!, fn [Filter.Mogrify, :args] -> [{"tint", "40"}] end)
|
||||
|
||||
File.cp!(
|
||||
"test/fixtures/image.jpg",
|
||||
|
|
@ -23,19 +28,11 @@ defmodule Pleroma.Upload.Filter.MogrifyTest do
|
|||
tempfile: Path.absname("test/fixtures/image_tmp.jpg")
|
||||
}
|
||||
|
||||
task =
|
||||
Task.async(fn ->
|
||||
assert_receive {:apply_filter, {_, "tint", "40"}}, 4_000
|
||||
end)
|
||||
MogrifyMock
|
||||
|> expect(:open, fn _file -> %{} end)
|
||||
|> expect(:custom, fn _image, "tint", "40" -> %{} end)
|
||||
|> expect(:save, fn _image, [in_place: true] -> :ok end)
|
||||
|
||||
with_mock Mogrify,
|
||||
open: fn _f -> %Mogrify.Image{} end,
|
||||
custom: fn _m, _a -> :ok end,
|
||||
custom: fn m, a, o -> send(task.pid, {:apply_filter, {m, a, o}}) end,
|
||||
save: fn _f, _o -> :ok end do
|
||||
assert Filter.Mogrify.filter(upload) == {:ok, :filtered}
|
||||
end
|
||||
|
||||
Task.await(task)
|
||||
assert Filter.Mogrify.filter(upload) == {:ok, :filtered}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,12 +5,13 @@
|
|||
defmodule Pleroma.Upload.FilterTest do
|
||||
use Pleroma.DataCase
|
||||
|
||||
import Mox
|
||||
alias Pleroma.StaticStubbedConfigMock, as: ConfigMock
|
||||
alias Pleroma.Upload.Filter
|
||||
|
||||
setup do: clear_config([Pleroma.Upload.Filter.AnonymizeFilename, :text])
|
||||
|
||||
test "applies filters" do
|
||||
clear_config([Pleroma.Upload.Filter.AnonymizeFilename, :text], "custom-file.png")
|
||||
ConfigMock
|
||||
|> stub(:get, fn [Pleroma.Upload.Filter.AnonymizeFilename, :text] -> "custom-file.png" end)
|
||||
|
||||
File.cp!(
|
||||
"test/fixtures/image.jpg",
|
||||
|
|
|
|||
|
|
@ -149,6 +149,9 @@ defmodule Pleroma.UploadTest do
|
|||
|
||||
test "copies the file to the configured folder with deduping" do
|
||||
File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg")
|
||||
expected_filename = "e30397b58d226d6583ab5b8b3c5defb0c682bda5c31ef07a9f57c1c4986e3781.jpg"
|
||||
|
||||
expected_path = Pleroma.Upload.Filter.Dedupe.shard_path(expected_filename)
|
||||
|
||||
file = %Plug.Upload{
|
||||
content_type: "image/jpeg",
|
||||
|
|
@ -159,8 +162,7 @@ defmodule Pleroma.UploadTest do
|
|||
{:ok, data} = Upload.store(file, filters: [Pleroma.Upload.Filter.Dedupe])
|
||||
|
||||
assert List.first(data["url"])["href"] ==
|
||||
Pleroma.Upload.base_url() <>
|
||||
"e30397b58d226d6583ab5b8b3c5defb0c682bda5c31ef07a9f57c1c4986e3781.jpg"
|
||||
Path.join([Pleroma.Upload.base_url(), expected_path])
|
||||
end
|
||||
|
||||
test "copies the file to the configured folder without deduping" do
|
||||
|
|
@ -225,20 +227,35 @@ defmodule Pleroma.UploadTest do
|
|||
assert Path.basename(attachment_url["href"]) == "an%E2%80%A6%20image.jpg"
|
||||
end
|
||||
|
||||
test "escapes reserved uri characters" do
|
||||
test "escapes disallowed reserved characters in uri path" do
|
||||
File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg")
|
||||
|
||||
file = %Plug.Upload{
|
||||
content_type: "image/jpeg",
|
||||
path: Path.absname("test/fixtures/image_tmp.jpg"),
|
||||
filename: ":?#[]@!$&\\'()*+,;=.jpg"
|
||||
filename: ":?#[]@!$&'()*+,;=.jpg"
|
||||
}
|
||||
|
||||
{:ok, data} = Upload.store(file)
|
||||
[attachment_url | _] = data["url"]
|
||||
|
||||
assert Path.basename(attachment_url["href"]) ==
|
||||
"%3A%3F%23%5B%5D%40%21%24%26%5C%27%28%29%2A%2B%2C%3B%3D.jpg"
|
||||
":%3F%23%5B%5D@!$&'()*+,;=.jpg"
|
||||
end
|
||||
|
||||
test "double %-encodes filename" do
|
||||
File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg")
|
||||
|
||||
file = %Plug.Upload{
|
||||
content_type: "image/jpeg",
|
||||
path: Path.absname("test/fixtures/image_tmp.jpg"),
|
||||
filename: "file with %20.jpg"
|
||||
}
|
||||
|
||||
{:ok, data} = Upload.store(file)
|
||||
[attachment_url | _] = data["url"]
|
||||
|
||||
assert Path.basename(attachment_url["href"]) == "file%20with%20%2520.jpg"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -265,4 +282,23 @@ defmodule Pleroma.UploadTest do
|
|||
refute String.starts_with?(url, base_url <> "/media/")
|
||||
end
|
||||
end
|
||||
|
||||
describe "Setting a link_name for uploaded media" do
|
||||
setup do: clear_config([Pleroma.Upload, :link_name], true)
|
||||
|
||||
test "encodes name parameter in query" do
|
||||
File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg")
|
||||
|
||||
file = %Plug.Upload{
|
||||
content_type: "image/jpeg",
|
||||
path: Path.absname("test/fixtures/image_tmp.jpg"),
|
||||
filename: "test file.jpg"
|
||||
}
|
||||
|
||||
{:ok, data} = Upload.store(file)
|
||||
[attachment_url | _] = data["url"]
|
||||
|
||||
assert Path.basename(attachment_url["href"]) == "test%20file.jpg?name=test+file.jpg"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,49 +0,0 @@
|
|||
# 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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
|
@ -6,7 +6,6 @@ defmodule Pleroma.User.BackupTest do
|
|||
use Oban.Testing, repo: Pleroma.Repo
|
||||
use Pleroma.DataCase
|
||||
|
||||
import Mock
|
||||
import Pleroma.Factory
|
||||
import Swoosh.TestAssertions
|
||||
import Mox
|
||||
|
|
@ -16,7 +15,6 @@ defmodule Pleroma.User.BackupTest do
|
|||
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
|
||||
|
||||
|
|
@ -28,79 +26,56 @@ defmodule Pleroma.User.BackupTest do
|
|||
ConfigMock
|
||||
|> stub_with(Pleroma.Config)
|
||||
|
||||
ProcessorMock
|
||||
|> stub_with(Pleroma.User.Backup.Processor)
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
test "it does not requrie enabled email" do
|
||||
clear_config([Pleroma.Emails.Mailer, :enabled], false)
|
||||
user = insert(:user)
|
||||
assert {:ok, _} = Backup.create(user)
|
||||
assert {:ok, _} = Backup.user(user)
|
||||
end
|
||||
|
||||
test "it does not require user's email" do
|
||||
user = insert(:user, %{email: nil})
|
||||
assert {:ok, _} = Backup.create(user)
|
||||
assert {:ok, _} = Backup.user(user)
|
||||
end
|
||||
|
||||
test "it creates a backup record and an Oban job" do
|
||||
%{id: user_id} = user = insert(:user)
|
||||
assert {:ok, %Oban.Job{args: args}} = Backup.create(user)
|
||||
user = insert(:user)
|
||||
assert {:ok, %Backup{} = backup} = Backup.user(user)
|
||||
assert {:ok, %Oban.Job{args: args}} = Backup.schedule_backup(backup)
|
||||
assert_enqueued(worker: BackupWorker, args: args)
|
||||
|
||||
backup = Backup.get(args["backup_id"])
|
||||
assert %Backup{user_id: ^user_id, processed: false, file_size: 0, state: :pending} = backup
|
||||
backup = Backup.get_by_id(args["backup_id"])
|
||||
assert %Backup{processed: false, file_size: 0} = backup
|
||||
end
|
||||
|
||||
test "it return an error if the export limit is over" do
|
||||
%{id: user_id} = user = insert(:user)
|
||||
user = insert(:user)
|
||||
limit_days = Pleroma.Config.get([Backup, :limit_days])
|
||||
assert {:ok, %Oban.Job{args: args}} = Backup.create(user)
|
||||
backup = Backup.get(args["backup_id"])
|
||||
assert %Backup{user_id: ^user_id, processed: false, file_size: 0} = backup
|
||||
{:ok, first_backup} = Backup.user(user)
|
||||
{:ok, _run_backup} = Backup.run(first_backup)
|
||||
|
||||
assert Backup.create(user) == {:error, "Last export was less than #{limit_days} days ago"}
|
||||
assert Backup.user(user) == {:error, "Last export was less than #{limit_days} days ago"}
|
||||
end
|
||||
|
||||
test "it process a backup record" 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 {:ok, %Backup{id: backup_id}} = Backup.user(user)
|
||||
|
||||
oban_args = %{"op" => "process", "backup_id" => backup_id}
|
||||
|
||||
assert {:ok, backup} = perform_job(BackupWorker, oban_args)
|
||||
assert backup.file_size > 0
|
||||
assert %Backup{id: ^backup_id, processed: true, user_id: ^user_id, state: :complete} = backup
|
||||
assert match?(%Backup{id: ^backup_id, processed: true, user_id: ^user_id}, 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 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)
|
||||
refute Backup.get_by_id(backup_id)
|
||||
|
||||
email = Pleroma.Emails.UserEmail.backup_is_ready_email(backup)
|
||||
|
||||
|
|
@ -114,10 +89,15 @@ defmodule Pleroma.User.BackupTest 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 {:ok, %Backup{} = backup} = Backup.user(user)
|
||||
|
||||
expected_args = %{"op" => "process", "backup_id" => backup.id}
|
||||
|
||||
assert_enqueued(worker: BackupWorker, args: %{"backup_id" => backup.id})
|
||||
assert {:ok, completed_backup} = perform_job(BackupWorker, expected_args)
|
||||
assert completed_backup.file_size > 0
|
||||
assert completed_backup.processed
|
||||
assert completed_backup.user_id == user_id
|
||||
|
||||
assert_no_email_sent()
|
||||
end
|
||||
|
|
@ -127,10 +107,13 @@ defmodule Pleroma.User.BackupTest 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 {:ok, %Backup{id: backup_id}} = Backup.user(user)
|
||||
|
||||
oban_args = %{"op" => "process", "backup_id" => backup_id}
|
||||
|
||||
assert {:ok, backup} = perform_job(BackupWorker, oban_args)
|
||||
assert backup.file_size > 0
|
||||
assert %Backup{id: ^backup_id, processed: true, user_id: ^user_id} = backup
|
||||
assert match?(%Backup{id: ^backup_id, processed: true, user_id: ^user_id}, backup)
|
||||
|
||||
assert_no_email_sent()
|
||||
end
|
||||
|
|
@ -139,10 +122,15 @@ defmodule Pleroma.User.BackupTest 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 {:ok, %Backup{id: backup_id} = backup} = Backup.user(user)
|
||||
|
||||
expected_args = %{"op" => "process", "backup_id" => backup.id}
|
||||
|
||||
assert_enqueued(worker: BackupWorker, args: expected_args)
|
||||
|
||||
assert {:ok, backup} = perform_job(BackupWorker, expected_args)
|
||||
assert backup.file_size > 0
|
||||
assert %Backup{id: ^backup_id, processed: true, user_id: ^user_id} = backup
|
||||
assert match?(%Backup{id: ^backup_id, processed: true, user_id: ^user_id}, backup)
|
||||
|
||||
assert_no_email_sent()
|
||||
end
|
||||
|
|
@ -152,16 +140,13 @@ defmodule Pleroma.User.BackupTest do
|
|||
clear_config([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)
|
||||
user = insert(:user)
|
||||
|
||||
assert {:ok, job1} = Backup.create(user)
|
||||
|
||||
assert {:ok, %Backup{}} = ObanHelpers.perform(job1)
|
||||
assert {:ok, job2} = Backup.create(user)
|
||||
assert Pleroma.Repo.aggregate(Backup, :count) == 2
|
||||
assert {:ok, backup2} = ObanHelpers.perform(job2)
|
||||
assert {:ok, %{id: backup_one_id}} = Backup.user(user)
|
||||
assert {:ok, %{id: _backup_two_id}} = Backup.user(user)
|
||||
|
||||
# Run the backups
|
||||
ObanHelpers.perform_all()
|
||||
|
||||
assert [^backup2] = Pleroma.Repo.all(Backup)
|
||||
assert_enqueued(worker: BackupWorker, args: %{"op" => "delete", "backup_id" => backup_one_id})
|
||||
end
|
||||
|
||||
test "it creates a zip archive with user data" do
|
||||
|
|
@ -177,17 +162,20 @@ defmodule Pleroma.User.BackupTest do
|
|||
{:ok, %{object: %{data: %{"id" => id3}}} = status3} =
|
||||
CommonAPI.post(user, %{status: "status3"})
|
||||
|
||||
CommonAPI.favorite(user, status1.id)
|
||||
CommonAPI.favorite(user, status2.id)
|
||||
CommonAPI.favorite(status1.id, user)
|
||||
CommonAPI.favorite(status2.id, user)
|
||||
|
||||
Bookmark.create(user.id, status2.id)
|
||||
Bookmark.create(user.id, status3.id)
|
||||
|
||||
CommonAPI.follow(user, other_user)
|
||||
CommonAPI.follow(other_user, user)
|
||||
|
||||
assert {:ok, backup} = user |> Backup.new() |> Repo.insert()
|
||||
assert {:ok, path} = Backup.export(backup, self())
|
||||
assert {:ok, zipfile} = :zip.zip_open(String.to_charlist(path), [:memory])
|
||||
assert {:ok, backup} = Backup.user(user)
|
||||
assert {:ok, run_backup} = Backup.run(backup)
|
||||
|
||||
tempfile = Path.join([run_backup.tempdir, run_backup.file_name])
|
||||
|
||||
assert {:ok, zipfile} = :zip.zip_open(String.to_charlist(tempfile), [:memory])
|
||||
assert {:ok, {~c"actor.json", json}} = :zip.zip_get(~c"actor.json", zipfile)
|
||||
|
||||
assert %{
|
||||
|
|
@ -197,13 +185,13 @@ defmodule Pleroma.User.BackupTest do
|
|||
%{"@language" => "und"}
|
||||
],
|
||||
"bookmarks" => "bookmarks.json",
|
||||
"followers" => "http://cofe.io/users/cofe/followers",
|
||||
"following" => "http://cofe.io/users/cofe/following",
|
||||
"followers" => "followers.json",
|
||||
"following" => "following.json",
|
||||
"id" => "http://cofe.io/users/cofe",
|
||||
"inbox" => "http://cofe.io/users/cofe/inbox",
|
||||
"likes" => "likes.json",
|
||||
"name" => "Cofe",
|
||||
"outbox" => "http://cofe.io/users/cofe/outbox",
|
||||
"outbox" => "outbox.json",
|
||||
"preferredUsername" => "cofe",
|
||||
"publicKey" => %{
|
||||
"id" => "http://cofe.io/users/cofe#main-key",
|
||||
|
|
@ -275,56 +263,34 @@ defmodule Pleroma.User.BackupTest do
|
|||
} = Jason.decode!(json)
|
||||
|
||||
:zip.zip_close(zipfile)
|
||||
File.rm!(path)
|
||||
File.rm_rf!(run_backup.tempdir)
|
||||
end
|
||||
|
||||
test "it counts the correct number processed" do
|
||||
test "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)
|
||||
CommonAPI.favorite(status.id, user)
|
||||
Bookmark.create(user.id, status.id)
|
||||
end)
|
||||
|
||||
assert {:ok, backup} = user |> Backup.new() |> Repo.insert()
|
||||
{:ok, backup} = Backup.process(backup)
|
||||
{:ok, backup} = Backup.run(backup)
|
||||
|
||||
assert backup.processed_number == 1 + 120 + 120 + 120
|
||||
zip_path = Path.join([backup.tempdir, backup.file_name])
|
||||
|
||||
Backup.delete(backup)
|
||||
end
|
||||
assert {:ok, zipfile} = :zip.zip_open(String.to_charlist(zip_path), [:memory])
|
||||
|
||||
test "it handles errors" do
|
||||
user = insert(:user, %{nickname: "cofe", name: "Cofe", ap_id: "http://cofe.io/users/cofe"})
|
||||
backup_parts = [~c"likes.json", ~c"bookmarks.json", ~c"outbox.json"]
|
||||
|
||||
Enum.map(1..120, fn i ->
|
||||
{:ok, _status} = CommonAPI.post(user, %{status: "status #{i}"})
|
||||
Enum.each(backup_parts, fn part ->
|
||||
assert {:ok, {_part, part_json}} = :zip.zip_get(part, zipfile)
|
||||
{:ok, decoded_part} = Jason.decode(part_json)
|
||||
assert decoded_part["totalItems"] == 120
|
||||
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
|
||||
Backup.delete_archive(backup)
|
||||
end
|
||||
|
||||
describe "it uploads and deletes a backup archive" do
|
||||
|
|
@ -337,18 +303,17 @@ defmodule Pleroma.User.BackupTest do
|
|||
{:ok, status1} = CommonAPI.post(user, %{status: "status1"})
|
||||
{:ok, status2} = CommonAPI.post(user, %{status: "status2"})
|
||||
{:ok, status3} = CommonAPI.post(user, %{status: "status3"})
|
||||
CommonAPI.favorite(user, status1.id)
|
||||
CommonAPI.favorite(user, status2.id)
|
||||
CommonAPI.favorite(status1.id, user)
|
||||
CommonAPI.favorite(status2.id, user)
|
||||
Bookmark.create(user.id, status2.id)
|
||||
Bookmark.create(user.id, status3.id)
|
||||
|
||||
assert {:ok, backup} = user |> Backup.new() |> Repo.insert()
|
||||
assert {:ok, path} = Backup.export(backup, self())
|
||||
|
||||
[path: path, backup: backup]
|
||||
[backup: backup]
|
||||
end
|
||||
|
||||
test "S3", %{path: path, backup: backup} do
|
||||
test "S3", %{backup: backup} do
|
||||
clear_config([Pleroma.Upload, :uploader], Pleroma.Uploaders.S3)
|
||||
clear_config([Pleroma.Uploaders.S3, :streaming_enabled], false)
|
||||
|
||||
|
|
@ -358,15 +323,17 @@ defmodule Pleroma.User.BackupTest do
|
|||
%{http_method: :delete} -> {:ok, %{status_code: 204}}
|
||||
end)
|
||||
|
||||
assert {:ok, %Pleroma.Upload{}} = Backup.upload(backup, path)
|
||||
assert {:ok, _backup} = Backup.delete(backup)
|
||||
assert {:ok, backup} = Backup.run(backup)
|
||||
assert {:ok, %Backup{processed: true}} = Backup.upload(backup)
|
||||
assert {:ok, _backup} = Backup.delete_archive(backup)
|
||||
end
|
||||
|
||||
test "Local", %{path: path, backup: backup} do
|
||||
test "Local", %{backup: backup} do
|
||||
clear_config([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)
|
||||
|
||||
assert {:ok, %Pleroma.Upload{}} = Backup.upload(backup, path)
|
||||
assert {:ok, _backup} = Backup.delete(backup)
|
||||
assert {:ok, backup} = Backup.run(backup)
|
||||
assert {:ok, %Backup{processed: true}} = Backup.upload(backup)
|
||||
assert {:ok, _backup} = Backup.delete_archive(backup)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -25,11 +25,12 @@ defmodule Pleroma.User.ImportTest do
|
|||
user3.nickname
|
||||
]
|
||||
|
||||
{:ok, job} = User.Import.follow_import(user1, identifiers)
|
||||
{:ok, jobs} = User.Import.follows_import(user1, identifiers)
|
||||
|
||||
for job <- jobs do
|
||||
assert {:ok, %User{}} = ObanHelpers.perform(job)
|
||||
end
|
||||
|
||||
assert {:ok, result} = ObanHelpers.perform(job)
|
||||
assert is_list(result)
|
||||
assert result == [refresh_record(user2), refresh_record(user3)]
|
||||
assert User.following?(user1, user2)
|
||||
assert User.following?(user1, user3)
|
||||
end
|
||||
|
|
@ -44,11 +45,12 @@ defmodule Pleroma.User.ImportTest do
|
|||
user3.nickname
|
||||
]
|
||||
|
||||
{:ok, job} = User.Import.blocks_import(user1, identifiers)
|
||||
{:ok, jobs} = User.Import.blocks_import(user1, identifiers)
|
||||
|
||||
for job <- jobs do
|
||||
assert {:ok, %User{}} = ObanHelpers.perform(job)
|
||||
end
|
||||
|
||||
assert {:ok, result} = ObanHelpers.perform(job)
|
||||
assert is_list(result)
|
||||
assert result == [user2, user3]
|
||||
assert User.blocks?(user1, user2)
|
||||
assert User.blocks?(user1, user3)
|
||||
end
|
||||
|
|
@ -63,11 +65,12 @@ defmodule Pleroma.User.ImportTest do
|
|||
user3.nickname
|
||||
]
|
||||
|
||||
{:ok, job} = User.Import.mutes_import(user1, identifiers)
|
||||
{:ok, jobs} = User.Import.mutes_import(user1, identifiers)
|
||||
|
||||
for job <- jobs do
|
||||
assert {:ok, %User{}} = ObanHelpers.perform(job)
|
||||
end
|
||||
|
||||
assert {:ok, result} = ObanHelpers.perform(job)
|
||||
assert is_list(result)
|
||||
assert result == [user2, user3]
|
||||
assert User.mutes?(user1, user2)
|
||||
assert User.mutes?(user1, user3)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3,11 +3,12 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.UserRelationshipTest do
|
||||
alias Pleroma.DateTimeMock
|
||||
alias Pleroma.UserRelationship
|
||||
|
||||
use Pleroma.DataCase, async: false
|
||||
use Pleroma.DataCase, async: true
|
||||
|
||||
import Mock
|
||||
import Mox
|
||||
import Pleroma.Factory
|
||||
|
||||
describe "*_exists?/2" do
|
||||
|
|
@ -52,6 +53,9 @@ defmodule Pleroma.UserRelationshipTest do
|
|||
end
|
||||
|
||||
test "creates user relationship record if it doesn't exist", %{users: [user1, user2]} do
|
||||
DateTimeMock
|
||||
|> stub_with(Pleroma.DateTime.Impl)
|
||||
|
||||
for relationship_type <- [
|
||||
:block,
|
||||
:mute,
|
||||
|
|
@ -80,13 +84,15 @@ defmodule Pleroma.UserRelationshipTest do
|
|||
end
|
||||
|
||||
test "if record already exists, returns it", %{users: [user1, user2]} do
|
||||
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
|
||||
fixed_datetime = ~N[2017-03-17 17:09:58]
|
||||
|
||||
assert user_block == UserRelationship.create_block(user1, user2)
|
||||
Pleroma.DateTimeMock
|
||||
|> expect(:utc_now, 2, fn -> fixed_datetime end)
|
||||
|
||||
{:ok, %{inserted_at: ^fixed_datetime}} = UserRelationship.create_block(user1, user2)
|
||||
|
||||
# Test the idempotency without caring about the exact time
|
||||
assert {:ok, _} = UserRelationship.create_block(user1, user2)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -366,5 +366,13 @@ defmodule Pleroma.UserSearchTest do
|
|||
|
||||
assert user == result |> Map.put(:search_rank, nil) |> Map.put(:search_type, nil)
|
||||
end
|
||||
|
||||
test "find users accepting chat messages only" do
|
||||
user1 = insert(:user, nickname: "user1", accepts_chat_messages: true)
|
||||
insert(:user, nickname: "user2", accepts_chat_messages: false)
|
||||
|
||||
[found_user1] = User.search("user", capabilities: ["accepts_chat_messages"])
|
||||
assert found_user1.id == user1.id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ defmodule Pleroma.UserTest do
|
|||
import Swoosh.TestAssertions
|
||||
|
||||
setup do
|
||||
Mox.stub_with(Pleroma.UnstubbedConfigMock, Pleroma.Config)
|
||||
Mox.stub_with(Pleroma.UnstubbedConfigMock, Pleroma.Test.StaticConfig)
|
||||
:ok
|
||||
end
|
||||
|
||||
|
|
@ -182,8 +182,8 @@ defmodule Pleroma.UserTest do
|
|||
locked = insert(:user, is_locked: true)
|
||||
follower = insert(:user)
|
||||
|
||||
CommonAPI.follow(follower, unlocked)
|
||||
CommonAPI.follow(follower, locked)
|
||||
CommonAPI.follow(unlocked, follower)
|
||||
CommonAPI.follow(locked, follower)
|
||||
|
||||
assert [] = User.get_follow_requests(unlocked)
|
||||
assert [activity] = User.get_follow_requests(locked)
|
||||
|
|
@ -196,9 +196,9 @@ defmodule Pleroma.UserTest do
|
|||
pending_follower = insert(:user)
|
||||
accepted_follower = insert(:user)
|
||||
|
||||
CommonAPI.follow(pending_follower, locked)
|
||||
CommonAPI.follow(pending_follower, locked)
|
||||
CommonAPI.follow(accepted_follower, locked)
|
||||
CommonAPI.follow(locked, pending_follower)
|
||||
CommonAPI.follow(locked, pending_follower)
|
||||
CommonAPI.follow(locked, accepted_follower)
|
||||
|
||||
Pleroma.FollowingRelationship.update(accepted_follower, locked, :follow_accept)
|
||||
|
||||
|
|
@ -209,7 +209,7 @@ defmodule Pleroma.UserTest do
|
|||
locked = insert(:user, is_locked: true)
|
||||
pending_follower = insert(:user, %{is_active: false})
|
||||
|
||||
CommonAPI.follow(pending_follower, locked)
|
||||
CommonAPI.follow(locked, pending_follower)
|
||||
|
||||
refute pending_follower.is_active
|
||||
assert [] = User.get_follow_requests(locked)
|
||||
|
|
@ -219,7 +219,7 @@ defmodule Pleroma.UserTest do
|
|||
followed = insert(:user, is_locked: true)
|
||||
follower = insert(:user)
|
||||
|
||||
CommonAPI.follow(follower, followed)
|
||||
CommonAPI.follow(followed, follower)
|
||||
assert [_activity] = User.get_follow_requests(followed)
|
||||
|
||||
{:ok, _user_relationship} = User.block(followed, follower)
|
||||
|
|
@ -1075,6 +1075,21 @@ defmodule Pleroma.UserTest do
|
|||
|
||||
refute cs.valid?
|
||||
end
|
||||
|
||||
test "it truncates fields" do
|
||||
clear_config([:instance, :max_remote_account_fields], 2)
|
||||
|
||||
fields = [
|
||||
%{"name" => "One", "value" => "Uno"},
|
||||
%{"name" => "Two", "value" => "Dos"},
|
||||
%{"name" => "Three", "value" => "Tres"}
|
||||
]
|
||||
|
||||
cs = User.remote_user_changeset(@valid_remote |> Map.put(:fields, fields))
|
||||
|
||||
assert [%{"name" => "One", "value" => "Uno"}, %{"name" => "Two", "value" => "Dos"}] ==
|
||||
Ecto.Changeset.get_field(cs, :fields)
|
||||
end
|
||||
end
|
||||
|
||||
describe "followers and friends" do
|
||||
|
|
@ -1526,7 +1541,7 @@ defmodule Pleroma.UserTest do
|
|||
|
||||
assert [activity] == ActivityPub.fetch_public_activities(%{}) |> Repo.preload(:bookmark)
|
||||
|
||||
assert [%{activity | thread_muted?: CommonAPI.thread_muted?(user2, activity)}] ==
|
||||
assert [%{activity | thread_muted?: CommonAPI.thread_muted?(activity, user2)}] ==
|
||||
ActivityPub.fetch_activities([user2.ap_id | User.following(user2)], %{
|
||||
user: user2
|
||||
})
|
||||
|
|
@ -1691,8 +1706,8 @@ defmodule Pleroma.UserTest do
|
|||
object_two = insert(:note, user: follower)
|
||||
activity_two = insert(:note_activity, user: follower, note: object_two)
|
||||
|
||||
{:ok, like} = CommonAPI.favorite(user, activity_two.id)
|
||||
{:ok, like_two} = CommonAPI.favorite(follower, activity.id)
|
||||
{:ok, like} = CommonAPI.favorite(activity_two.id, user)
|
||||
{:ok, like_two} = CommonAPI.favorite(activity.id, follower)
|
||||
{:ok, repeat} = CommonAPI.repeat(activity_two.id, user)
|
||||
|
||||
{:ok, job} = User.delete(user)
|
||||
|
|
@ -1866,6 +1881,11 @@ defmodule Pleroma.UserTest do
|
|||
end
|
||||
end
|
||||
|
||||
test "get_or_fetch_public_key_for_ap_id fetches a user that's not in the db" do
|
||||
assert {:ok, _key} =
|
||||
User.get_or_fetch_public_key_for_ap_id("http://mastodon.example.org/users/admin")
|
||||
end
|
||||
|
||||
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
|
||||
|
|
@ -2390,8 +2410,8 @@ defmodule Pleroma.UserTest do
|
|||
other_user =
|
||||
insert(:user,
|
||||
local: false,
|
||||
follower_address: "http://localhost:4001/users/masto_closed/followers",
|
||||
following_address: "http://localhost:4001/users/masto_closed/following"
|
||||
follower_address: "https://remote.org/users/masto_closed/followers",
|
||||
following_address: "https://remote.org/users/masto_closed/following"
|
||||
)
|
||||
|
||||
assert other_user.following_count == 0
|
||||
|
|
@ -2411,8 +2431,8 @@ defmodule Pleroma.UserTest do
|
|||
other_user =
|
||||
insert(:user,
|
||||
local: false,
|
||||
follower_address: "http://localhost:4001/users/masto_closed/followers",
|
||||
following_address: "http://localhost:4001/users/masto_closed/following"
|
||||
follower_address: "https://remote.org/users/masto_closed/followers",
|
||||
following_address: "https://remote.org/users/masto_closed/following"
|
||||
)
|
||||
|
||||
assert other_user.following_count == 0
|
||||
|
|
@ -2432,8 +2452,8 @@ defmodule Pleroma.UserTest do
|
|||
other_user =
|
||||
insert(:user,
|
||||
local: false,
|
||||
follower_address: "http://localhost:4001/users/masto_closed/followers",
|
||||
following_address: "http://localhost:4001/users/masto_closed/following"
|
||||
follower_address: "https://remote.org/users/masto_closed/followers",
|
||||
following_address: "https://remote.org/users/masto_closed/following"
|
||||
)
|
||||
|
||||
assert other_user.following_count == 0
|
||||
|
|
@ -2654,8 +2674,12 @@ defmodule Pleroma.UserTest do
|
|||
|
||||
assert {:ok, user} = User.update_last_active_at(user)
|
||||
|
||||
assert user.last_active_at >= test_started_at
|
||||
assert user.last_active_at <= NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second)
|
||||
assert NaiveDateTime.compare(user.last_active_at, test_started_at) in [:gt, :eq]
|
||||
|
||||
assert NaiveDateTime.compare(
|
||||
user.last_active_at,
|
||||
NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second)
|
||||
) in [:lt, :eq]
|
||||
|
||||
last_active_at =
|
||||
NaiveDateTime.utc_now()
|
||||
|
|
@ -2667,10 +2691,15 @@ defmodule Pleroma.UserTest do
|
|||
|> cast(%{last_active_at: last_active_at}, [:last_active_at])
|
||||
|> User.update_and_set_cache()
|
||||
|
||||
assert user.last_active_at == last_active_at
|
||||
assert NaiveDateTime.compare(user.last_active_at, last_active_at) == :eq
|
||||
|
||||
assert {:ok, user} = User.update_last_active_at(user)
|
||||
assert user.last_active_at >= test_started_at
|
||||
assert user.last_active_at <= NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second)
|
||||
assert NaiveDateTime.compare(user.last_active_at, test_started_at) in [:gt, :eq]
|
||||
|
||||
assert NaiveDateTime.compare(
|
||||
user.last_active_at,
|
||||
NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second)
|
||||
) in [:lt, :eq]
|
||||
end
|
||||
|
||||
test "active_user_count/1" do
|
||||
|
|
@ -2768,6 +2797,15 @@ defmodule Pleroma.UserTest do
|
|||
assert user_updated.also_known_as |> length() == 1
|
||||
assert user2.ap_id in user_updated.also_known_as
|
||||
end
|
||||
|
||||
test "should tolerate non-http(s) aliases" do
|
||||
user =
|
||||
insert(:user, %{
|
||||
also_known_as: ["at://did:plc:xgvzy7ni6ig6ievcbls5jaxe"]
|
||||
})
|
||||
|
||||
assert "at://did:plc:xgvzy7ni6ig6ievcbls5jaxe" in user.also_known_as
|
||||
end
|
||||
end
|
||||
|
||||
describe "alias_users/1" do
|
||||
|
|
@ -2904,4 +2942,74 @@ defmodule Pleroma.UserTest do
|
|||
|
||||
assert [%{"verified_at" => ^verified_at}] = user.fields
|
||||
end
|
||||
|
||||
describe "follow_hashtag/2" do
|
||||
test "should follow a hashtag" do
|
||||
user = insert(:user)
|
||||
hashtag = insert(:hashtag)
|
||||
|
||||
assert {:ok, _} = user |> User.follow_hashtag(hashtag)
|
||||
|
||||
user = User.get_cached_by_ap_id(user.ap_id)
|
||||
|
||||
assert user.followed_hashtags |> Enum.count() == 1
|
||||
assert hashtag.name in Enum.map(user.followed_hashtags, fn %{name: name} -> name end)
|
||||
end
|
||||
|
||||
test "should not follow a hashtag twice" do
|
||||
user = insert(:user)
|
||||
hashtag = insert(:hashtag)
|
||||
|
||||
assert {:ok, _} = user |> User.follow_hashtag(hashtag)
|
||||
|
||||
assert {:ok, _} = user |> User.follow_hashtag(hashtag)
|
||||
|
||||
user = User.get_cached_by_ap_id(user.ap_id)
|
||||
|
||||
assert user.followed_hashtags |> Enum.count() == 1
|
||||
assert hashtag.name in Enum.map(user.followed_hashtags, fn %{name: name} -> name end)
|
||||
end
|
||||
|
||||
test "can follow multiple hashtags" do
|
||||
user = insert(:user)
|
||||
hashtag = insert(:hashtag)
|
||||
other_hashtag = insert(:hashtag)
|
||||
|
||||
assert {:ok, _} = user |> User.follow_hashtag(hashtag)
|
||||
assert {:ok, _} = user |> User.follow_hashtag(other_hashtag)
|
||||
|
||||
user = User.get_cached_by_ap_id(user.ap_id)
|
||||
|
||||
assert user.followed_hashtags |> Enum.count() == 2
|
||||
assert hashtag.name in Enum.map(user.followed_hashtags, fn %{name: name} -> name end)
|
||||
assert other_hashtag.name in Enum.map(user.followed_hashtags, fn %{name: name} -> name end)
|
||||
end
|
||||
end
|
||||
|
||||
describe "unfollow_hashtag/2" do
|
||||
test "should unfollow a hashtag" do
|
||||
user = insert(:user)
|
||||
hashtag = insert(:hashtag)
|
||||
|
||||
assert {:ok, _} = user |> User.follow_hashtag(hashtag)
|
||||
assert {:ok, _} = user |> User.unfollow_hashtag(hashtag)
|
||||
|
||||
user = User.get_cached_by_ap_id(user.ap_id)
|
||||
|
||||
assert user.followed_hashtags |> Enum.count() == 0
|
||||
end
|
||||
|
||||
test "should not error when trying to unfollow a hashtag twice" do
|
||||
user = insert(:user)
|
||||
hashtag = insert(:hashtag)
|
||||
|
||||
assert {:ok, _} = user |> User.follow_hashtag(hashtag)
|
||||
assert {:ok, _} = user |> User.unfollow_hashtag(hashtag)
|
||||
assert {:ok, _} = user |> User.unfollow_hashtag(hashtag)
|
||||
|
||||
user = User.get_cached_by_ap_id(user.ap_id)
|
||||
|
||||
assert user.followed_hashtags |> Enum.count() == 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Delivery
|
||||
alias Pleroma.Instances
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Tests.ObanHelpers
|
||||
alias Pleroma.User
|
||||
|
|
@ -26,7 +25,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
require Pleroma.Constants
|
||||
|
||||
setup do
|
||||
Mox.stub_with(Pleroma.UnstubbedConfigMock, Pleroma.Config)
|
||||
Mox.stub_with(Pleroma.UnstubbedConfigMock, Pleroma.Test.StaticConfig)
|
||||
:ok
|
||||
end
|
||||
|
||||
|
|
@ -431,7 +430,133 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
end
|
||||
end
|
||||
|
||||
describe "/objects/:uuid/replies" do
|
||||
test "it renders the top-level collection", %{
|
||||
conn: conn
|
||||
} do
|
||||
user = insert(:user)
|
||||
note = insert(:note_activity)
|
||||
note = Pleroma.Activity.get_by_id_with_object(note.id)
|
||||
uuid = String.split(note.object.data["id"], "/") |> List.last()
|
||||
|
||||
{:ok, _} =
|
||||
CommonAPI.post(user, %{status: "reply1", in_reply_to_status_id: note.id})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("accept", "application/activity+json")
|
||||
|> get("/objects/#{uuid}/replies")
|
||||
|
||||
assert match?(
|
||||
%{
|
||||
"id" => _,
|
||||
"type" => "OrderedCollection",
|
||||
"totalItems" => 1,
|
||||
"first" => %{
|
||||
"id" => _,
|
||||
"type" => "OrderedCollectionPage",
|
||||
"orderedItems" => [_]
|
||||
}
|
||||
},
|
||||
json_response(conn, 200)
|
||||
)
|
||||
end
|
||||
|
||||
test "first page id includes `?page=true`", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
note = insert(:note_activity)
|
||||
note = Pleroma.Activity.get_by_id_with_object(note.id)
|
||||
uuid = String.split(note.object.data["id"], "/") |> List.last()
|
||||
|
||||
{:ok, _} =
|
||||
CommonAPI.post(user, %{status: "reply1", in_reply_to_status_id: note.id})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("accept", "application/activity+json")
|
||||
|> get("/objects/#{uuid}/replies")
|
||||
|
||||
%{"id" => collection_id, "first" => %{"id" => page_id, "partOf" => part_of}} =
|
||||
json_response(conn, 200)
|
||||
|
||||
assert part_of == collection_id
|
||||
assert String.contains?(page_id, "page=true")
|
||||
end
|
||||
|
||||
test "unknown query params do not crash the endpoint", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
note = insert(:note_activity)
|
||||
note = Pleroma.Activity.get_by_id_with_object(note.id)
|
||||
uuid = String.split(note.object.data["id"], "/") |> List.last()
|
||||
|
||||
{:ok, _} =
|
||||
CommonAPI.post(user, %{status: "reply1", in_reply_to_status_id: note.id})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("accept", "application/activity+json")
|
||||
|> get("/objects/#{uuid}/replies?unknown_param=1")
|
||||
|
||||
assert %{"type" => "OrderedCollection"} = json_response(conn, 200)
|
||||
end
|
||||
|
||||
test "it renders a collection page", %{
|
||||
conn: conn
|
||||
} do
|
||||
user = insert(:user)
|
||||
note = insert(:note_activity)
|
||||
note = Pleroma.Activity.get_by_id_with_object(note.id)
|
||||
uuid = String.split(note.object.data["id"], "/") |> List.last()
|
||||
|
||||
{:ok, r1} =
|
||||
CommonAPI.post(user, %{status: "reply1", in_reply_to_status_id: note.id})
|
||||
|
||||
{:ok, r2} =
|
||||
CommonAPI.post(user, %{status: "reply2", in_reply_to_status_id: note.id})
|
||||
|
||||
{:ok, _} =
|
||||
CommonAPI.post(user, %{status: "reply3", in_reply_to_status_id: note.id})
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("accept", "application/activity+json")
|
||||
|> get("/objects/#{uuid}/replies?page=true&min_id=#{r1.object.id}&limit=1")
|
||||
|
||||
expected_uris = [r2.object.data["id"]]
|
||||
|
||||
assert match?(
|
||||
%{
|
||||
"id" => _,
|
||||
"type" => "OrderedCollectionPage",
|
||||
"prev" => _,
|
||||
"next" => _,
|
||||
"orderedItems" => ^expected_uris
|
||||
},
|
||||
json_response(conn, 200)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
describe "/activities/:uuid" do
|
||||
test "it does not include a top-level replies collection on activities", %{conn: conn} do
|
||||
clear_config([:activitypub, :note_replies_output_limit], 1)
|
||||
|
||||
activity = insert(:note_activity)
|
||||
activity = Activity.get_by_id_with_object(activity.id)
|
||||
|
||||
uuid = String.split(activity.data["id"], "/") |> List.last()
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("accept", "application/activity+json")
|
||||
|> get("/activities/#{uuid}")
|
||||
|
||||
res = json_response(conn, 200)
|
||||
|
||||
refute Map.has_key?(res, "replies")
|
||||
assert get_in(res, ["object", "replies", "id"]) == activity.object.data["id"] <> "/replies"
|
||||
end
|
||||
|
||||
test "it doesn't return a local-only activity", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
{:ok, post} = CommonAPI.post(user, %{status: "test", visibility: "local"})
|
||||
|
|
@ -601,23 +726,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
assert Activity.get_by_ap_id(data["id"])
|
||||
end
|
||||
|
||||
test "it clears `unreachable` federation status of the sender", %{conn: conn} do
|
||||
data = File.read!("test/fixtures/mastodon-post-activity.json") |> Jason.decode!()
|
||||
|
||||
sender_url = data["actor"]
|
||||
Instances.set_consistently_unreachable(sender_url)
|
||||
refute Instances.reachable?(sender_url)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/inbox", data)
|
||||
|
||||
assert "ok" == json_response(conn, 200)
|
||||
assert Instances.reachable?(sender_url)
|
||||
end
|
||||
|
||||
test "accept follow activity", %{conn: conn} do
|
||||
clear_config([:instance, :federating], true)
|
||||
relay = Relay.get_actor()
|
||||
|
|
@ -657,7 +765,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
end
|
||||
|
||||
test "without valid signature, " <>
|
||||
"it only accepts Create activities and requires enabled federation",
|
||||
"it accepts Create activities and requires enabled federation",
|
||||
%{conn: conn} do
|
||||
data = File.read!("test/fixtures/mastodon-post-activity.json") |> Jason.decode!()
|
||||
non_create_data = File.read!("test/fixtures/mastodon-announce.json") |> Jason.decode!()
|
||||
|
|
@ -684,6 +792,54 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|> json_response(400)
|
||||
end
|
||||
|
||||
# When activity is delivered to the inbox and we cannot immediately verify signature
|
||||
# we capture all the params and process it later in the Oban job.
|
||||
# Once we begin processing it through Oban we risk fetching the actor to validate the
|
||||
# activity which just leads to inserting a new user to process a Delete not relevant to us.
|
||||
test "Activities of certain types from an unknown actor are discarded", %{conn: conn} do
|
||||
example_bad_types =
|
||||
Pleroma.Constants.activity_types() --
|
||||
Pleroma.Constants.allowed_activity_types_from_strangers()
|
||||
|
||||
Enum.each(example_bad_types, fn bad_type ->
|
||||
params =
|
||||
%{
|
||||
"type" => bad_type,
|
||||
"actor" => "https://unknown.mastodon.instance/users/somebody"
|
||||
}
|
||||
|> Jason.encode!()
|
||||
|
||||
conn
|
||||
|> assign(:valid_signature, false)
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/inbox", params)
|
||||
|> json_response(400)
|
||||
|
||||
assert all_enqueued() == []
|
||||
end)
|
||||
end
|
||||
|
||||
test "Unknown activity types are discarded", %{conn: conn} do
|
||||
unknown_types = ["Poke", "Read", "Dazzle"]
|
||||
|
||||
Enum.each(unknown_types, fn bad_type ->
|
||||
params =
|
||||
%{
|
||||
"type" => bad_type,
|
||||
"actor" => "https://unknown.mastodon.instance/users/somebody"
|
||||
}
|
||||
|> Jason.encode!()
|
||||
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/inbox", params)
|
||||
|> json_response(400)
|
||||
|
||||
assert all_enqueued() == []
|
||||
end)
|
||||
end
|
||||
|
||||
test "accepts Add/Remove activities", %{conn: conn} do
|
||||
object_id = "c61d6733-e256-4fe1-ab13-1e369789423f"
|
||||
|
||||
|
|
@ -893,23 +1049,6 @@ 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)
|
||||
|
||||
|
|
@ -1060,24 +1199,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
assert response(conn, 200) =~ note_object.data["content"]
|
||||
end
|
||||
|
||||
test "it clears `unreachable` federation status of the sender", %{conn: conn, data: data} do
|
||||
user = insert(:user)
|
||||
data = Map.put(data, "bcc", [user.ap_id])
|
||||
|
||||
sender_host = URI.parse(data["actor"]).host
|
||||
Instances.set_consistently_unreachable(sender_host)
|
||||
refute Instances.reachable?(sender_host)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{user.nickname}/inbox", data)
|
||||
|
||||
assert "ok" == json_response(conn, 200)
|
||||
assert Instances.reachable?(sender_host)
|
||||
end
|
||||
|
||||
test "it removes all follower collections but actor's", %{conn: conn} do
|
||||
[actor, recipient] = insert_pair(:user)
|
||||
|
||||
|
|
@ -1157,9 +1278,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
}
|
||||
],
|
||||
"actor" => actor.ap_id,
|
||||
"cc" => [
|
||||
reported_user.ap_id
|
||||
],
|
||||
# CC and TO might either not exist at all, or be empty. We should be able to handle either.
|
||||
# "cc" => [],
|
||||
"content" => "test",
|
||||
"context" => "context",
|
||||
"id" => "http://#{remote_domain}/activities/02be56cf-35e3-46b4-b2c6-47ae08dfee9e",
|
||||
|
|
@ -1224,7 +1344,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|
||||
note = insert(:note_activity, user: reported_user)
|
||||
|
||||
Pleroma.Web.CommonAPI.favorite(another, note.id)
|
||||
Pleroma.Web.CommonAPI.favorite(note.id, another)
|
||||
|
||||
mock_json_body =
|
||||
"test/fixtures/mastodon/application_actor.json"
|
||||
|
|
@ -1272,9 +1392,79 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
html_body: ~r/#{note.data["object"]}/i
|
||||
)
|
||||
end
|
||||
|
||||
test "it accepts an incoming Block", %{conn: conn, data: data} do
|
||||
user = insert(:user)
|
||||
|
||||
data =
|
||||
data
|
||||
|> Map.put("type", "Block")
|
||||
|> Map.put("to", [user.ap_id])
|
||||
|> Map.put("cc", [])
|
||||
|> Map.put("object", user.ap_id)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{user.nickname}/inbox", data)
|
||||
|
||||
assert "ok" == json_response(conn, 200)
|
||||
ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
|
||||
assert Activity.get_by_ap_id(data["id"])
|
||||
end
|
||||
|
||||
test "it returns an error when receiving an activity sent to a deactivated user", %{
|
||||
conn: conn,
|
||||
data: data
|
||||
} do
|
||||
user = insert(:user)
|
||||
{:ok, _} = User.set_activation(user, 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 "User deactivated" == json_response(conn, 404)
|
||||
end
|
||||
|
||||
test "it returns an error when receiving an activity sent from a deactivated user", %{
|
||||
conn: conn,
|
||||
data: data
|
||||
} do
|
||||
sender = insert(:user)
|
||||
user = insert(:user)
|
||||
{:ok, _} = User.set_activation(sender, false)
|
||||
|
||||
data =
|
||||
data
|
||||
|> Map.put("bcc", [user.ap_id])
|
||||
|> Map.put("actor", sender.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 "Sender deactivated" == json_response(conn, 404)
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET /users/:nickname/outbox" do
|
||||
setup do
|
||||
Mox.stub_with(Pleroma.StaticStubbedConfigMock, Pleroma.Config)
|
||||
:ok
|
||||
end
|
||||
|
||||
test "it paginates correctly", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
conn = assign(conn, :user, user)
|
||||
|
|
@ -1363,6 +1553,22 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
assert %{"orderedItems" => []} = resp
|
||||
end
|
||||
|
||||
test "it does not return a local note activity when C2S API is disabled", %{conn: conn} do
|
||||
clear_config([:activitypub, :client_api_enabled], false)
|
||||
user = insert(:user)
|
||||
reader = insert(:user)
|
||||
{:ok, _note_activity} = CommonAPI.post(user, %{status: "mew mew", visibility: "local"})
|
||||
|
||||
resp =
|
||||
conn
|
||||
|> assign(:user, reader)
|
||||
|> 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)
|
||||
|
|
@ -1402,7 +1608,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|
||||
assert question = Object.normalize(activity, fetch: false)
|
||||
|
||||
{:ok, [activity], _object} = CommonAPI.vote(voter, question, [1])
|
||||
{:ok, [activity], _object} = CommonAPI.vote(question, voter, [1])
|
||||
|
||||
assert outbox_get =
|
||||
conn
|
||||
|
|
@ -1414,6 +1620,35 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
assert [answer_outbox] = outbox_get["orderedItems"]
|
||||
assert answer_outbox["id"] == activity.data["id"]
|
||||
end
|
||||
|
||||
test "it works with authorized fetch forced when authenticated" do
|
||||
clear_config([:activitypub, :authorized_fetch_mode], true)
|
||||
|
||||
user = insert(:user)
|
||||
outbox_endpoint = user.ap_id <> "/outbox"
|
||||
|
||||
conn =
|
||||
build_conn()
|
||||
|> assign(:user, user)
|
||||
|> put_req_header("accept", "application/activity+json")
|
||||
|> get(outbox_endpoint)
|
||||
|
||||
assert json_response(conn, 200)
|
||||
end
|
||||
|
||||
test "it fails with authorized fetch forced when unauthenticated", %{conn: conn} do
|
||||
clear_config([:activitypub, :authorized_fetch_mode], true)
|
||||
|
||||
user = insert(:user)
|
||||
outbox_endpoint = user.ap_id <> "/outbox"
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("accept", "application/activity+json")
|
||||
|> get(outbox_endpoint)
|
||||
|
||||
assert response(conn, 401)
|
||||
end
|
||||
end
|
||||
|
||||
describe "POST /users/:nickname/outbox (C2S)" do
|
||||
|
|
@ -1471,6 +1706,41 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
assert object["content"] == activity["object"]["content"]
|
||||
end
|
||||
|
||||
test "it inserts an incoming reply create activity into the database", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
replying_user = insert(:user)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "cofe"})
|
||||
|
||||
data = %{
|
||||
type: "Create",
|
||||
object: %{
|
||||
to: [Pleroma.Constants.as_public(), user.ap_id],
|
||||
cc: [replying_user.follower_address],
|
||||
inReplyTo: activity.object.data["id"],
|
||||
content: "green tea",
|
||||
type: "Note"
|
||||
}
|
||||
}
|
||||
|
||||
result =
|
||||
conn
|
||||
|> assign(:user, replying_user)
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{replying_user.nickname}/outbox", data)
|
||||
|> json_response(201)
|
||||
|
||||
updated_object = Object.normalize(activity.object.data["id"], fetch: false)
|
||||
|
||||
assert Activity.get_by_ap_id(result["id"])
|
||||
assert result["object"]
|
||||
assert %Object{data: object} = Object.normalize(result["object"], fetch: false)
|
||||
assert object["content"] == data.object.content
|
||||
assert Pleroma.Web.ActivityPub.Visibility.public?(object)
|
||||
assert object["inReplyTo"] == activity.object.data["id"]
|
||||
assert updated_object.data["repliesCount"] == 1
|
||||
end
|
||||
|
||||
test "it rejects anything beyond 'Note' creations", %{conn: conn, activity: activity} do
|
||||
user = insert(:user)
|
||||
|
||||
|
|
@ -1575,6 +1845,311 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
assert json_response(conn, 403)
|
||||
end
|
||||
|
||||
test "it rejects update activity of object from other actor", %{conn: conn} do
|
||||
note_activity = insert(:note_activity)
|
||||
note_object = Object.normalize(note_activity, fetch: false)
|
||||
user = insert(:user)
|
||||
|
||||
data = %{
|
||||
type: "Update",
|
||||
object: %{
|
||||
id: note_object.data["id"]
|
||||
}
|
||||
}
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{user.nickname}/outbox", data)
|
||||
|
||||
assert json_response(conn, 400)
|
||||
assert note_object == Object.normalize(note_activity, fetch: false)
|
||||
end
|
||||
|
||||
test "it rejects Add to other user's collection", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
target_user = insert(:user)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "Post"})
|
||||
object = Object.normalize(activity, fetch: false)
|
||||
object_id = object.data["id"]
|
||||
|
||||
data = %{
|
||||
type: "Add",
|
||||
target:
|
||||
"#{Pleroma.Web.Endpoint.url()}/users/#{target_user.nickname}/collections/featured",
|
||||
object: object_id
|
||||
}
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{user.nickname}/outbox", data)
|
||||
|
||||
assert json_response(conn, 400)
|
||||
end
|
||||
|
||||
test "it rejects Remove to other user's collection", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
target_user = insert(:user)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "Post"})
|
||||
object = Object.normalize(activity, fetch: false)
|
||||
object_id = object.data["id"]
|
||||
|
||||
data = %{
|
||||
type: "Remove",
|
||||
target:
|
||||
"#{Pleroma.Web.Endpoint.url()}/users/#{target_user.nickname}/collections/featured",
|
||||
object: object_id
|
||||
}
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{user.nickname}/outbox", data)
|
||||
|
||||
assert json_response(conn, 400)
|
||||
end
|
||||
|
||||
test "it rejects updating Actor's profile", %{conn: conn} do
|
||||
user = insert(:user, local: true)
|
||||
|
||||
user_object = Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: user})
|
||||
user_object_new = Map.put(user_object, "name", "lain")
|
||||
|
||||
data = %{
|
||||
type: "Update",
|
||||
object: user_object_new
|
||||
}
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/users/#{user.nickname}/outbox", data)
|
||||
|
||||
updated_user_object = Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: user})
|
||||
|
||||
assert updated_user_object == user_object
|
||||
assert json_response(conn, 400)
|
||||
end
|
||||
|
||||
# Actor publicKey tests are redundant with above test,
|
||||
# left here for the case that Updating Actors is ever supported
|
||||
test "it rejects updating Actor's publicKey", %{conn: conn} do
|
||||
user = insert(:user, local: true)
|
||||
|
||||
{:ok, pem} = Pleroma.Keys.generate_rsa_pem()
|
||||
{:ok, _, public_key} = Pleroma.Keys.keys_from_pem(pem)
|
||||
# Taken from UserView
|
||||
public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
|
||||
public_key = :public_key.pem_encode([public_key])
|
||||
|
||||
user_object = Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: user})
|
||||
user_object_public_key = Map.fetch!(user_object, "publicKey")
|
||||
user_object_public_key = Map.put(user_object_public_key, "publicKeyPem", public_key)
|
||||
user_object_new = Map.put(user_object, "publicKey", user_object_public_key)
|
||||
|
||||
refute user_object == user_object_new
|
||||
|
||||
data = %{
|
||||
type: "Update",
|
||||
object: user_object_new
|
||||
}
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/users/#{user.nickname}/outbox", data)
|
||||
|
||||
new_user_object = Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: user})
|
||||
|
||||
assert user_object == new_user_object
|
||||
assert json_response(conn, 400)
|
||||
end
|
||||
|
||||
test "it rejects updating Actor's publicKey of another user", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
target_user = insert(:user, local: true)
|
||||
|
||||
{:ok, pem} = Pleroma.Keys.generate_rsa_pem()
|
||||
{:ok, _, public_key} = Pleroma.Keys.keys_from_pem(pem)
|
||||
# Taken from UserView
|
||||
public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
|
||||
public_key = :public_key.pem_encode([public_key])
|
||||
|
||||
target_user_object =
|
||||
Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: target_user})
|
||||
|
||||
target_user_object_public_key = Map.fetch!(target_user_object, "publicKey")
|
||||
|
||||
target_user_object_public_key =
|
||||
Map.put(target_user_object_public_key, "publicKeyPem", public_key)
|
||||
|
||||
target_user_object_new =
|
||||
Map.put(target_user_object, "publicKey", target_user_object_public_key)
|
||||
|
||||
refute target_user_object == target_user_object_new
|
||||
|
||||
data = %{
|
||||
type: "Update",
|
||||
object: target_user_object_new
|
||||
}
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/users/#{target_user.nickname}/outbox", data)
|
||||
|
||||
new_target_user_object =
|
||||
Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: target_user})
|
||||
|
||||
assert target_user_object == new_target_user_object
|
||||
assert json_response(conn, 403)
|
||||
end
|
||||
|
||||
test "it rejects creating Actors of type Application", %{conn: conn} do
|
||||
user = insert(:user, local: true)
|
||||
|
||||
data = %{
|
||||
type: "Create",
|
||||
object: %{
|
||||
type: "Application"
|
||||
}
|
||||
}
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/users/#{user.nickname}/outbox", data)
|
||||
|
||||
assert json_response(conn, 400)
|
||||
end
|
||||
|
||||
test "it rejects creating Actors of type Person", %{conn: conn} do
|
||||
user = insert(:user, local: true)
|
||||
|
||||
data = %{
|
||||
type: "Create",
|
||||
object: %{
|
||||
type: "Person"
|
||||
}
|
||||
}
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/users/#{user.nickname}/outbox", data)
|
||||
|
||||
assert json_response(conn, 400)
|
||||
end
|
||||
|
||||
test "it rejects creating Actors of type Service", %{conn: conn} do
|
||||
user = insert(:user, local: true)
|
||||
|
||||
data = %{
|
||||
type: "Create",
|
||||
object: %{
|
||||
type: "Service"
|
||||
}
|
||||
}
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/users/#{user.nickname}/outbox", data)
|
||||
|
||||
assert json_response(conn, 400)
|
||||
end
|
||||
|
||||
test "it rejects like activity to object invisible to actor", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
stranger = insert(:user, local: true)
|
||||
{:ok, post} = CommonAPI.post(user, %{status: "cofe", visibility: "private"})
|
||||
|
||||
assert Pleroma.Web.ActivityPub.Visibility.private?(post)
|
||||
refute Pleroma.Web.ActivityPub.Visibility.visible_for_user?(post, stranger)
|
||||
|
||||
post_object = Object.normalize(post, fetch: false)
|
||||
|
||||
data = %{
|
||||
type: "Like",
|
||||
object: %{
|
||||
id: post_object.data["id"]
|
||||
}
|
||||
}
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:user, stranger)
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{stranger.nickname}/outbox", data)
|
||||
|
||||
assert json_response(conn, 403)
|
||||
end
|
||||
|
||||
test "it rejects announce activity to object invisible to actor", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
stranger = insert(:user, local: true)
|
||||
{:ok, post} = CommonAPI.post(user, %{status: "cofe", visibility: "private"})
|
||||
|
||||
assert Pleroma.Web.ActivityPub.Visibility.private?(post)
|
||||
refute Pleroma.Web.ActivityPub.Visibility.visible_for_user?(post, stranger)
|
||||
|
||||
post_object = Object.normalize(post, fetch: false)
|
||||
|
||||
data = %{
|
||||
type: "Announce",
|
||||
object: %{
|
||||
id: post_object.data["id"]
|
||||
}
|
||||
}
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:user, stranger)
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{stranger.nickname}/outbox", data)
|
||||
|
||||
assert json_response(conn, 403)
|
||||
end
|
||||
|
||||
test "it rejects emojireact activity to object invisible to actor", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
stranger = insert(:user, local: true)
|
||||
{:ok, post} = CommonAPI.post(user, %{status: "cofe", visibility: "private"})
|
||||
|
||||
assert Pleroma.Web.ActivityPub.Visibility.private?(post)
|
||||
refute Pleroma.Web.ActivityPub.Visibility.visible_for_user?(post, stranger)
|
||||
|
||||
post_object = Object.normalize(post, fetch: false)
|
||||
|
||||
data = %{
|
||||
type: "EmojiReact",
|
||||
object: %{
|
||||
id: post_object.data["id"]
|
||||
},
|
||||
content: "😀"
|
||||
}
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:user, stranger)
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{stranger.nickname}/outbox", data)
|
||||
|
||||
assert json_response(conn, 403)
|
||||
end
|
||||
|
||||
test "it increases like count when receiving a like action", %{conn: conn} do
|
||||
note_activity = insert(:note_activity)
|
||||
note_object = Object.normalize(note_activity, fetch: false)
|
||||
|
|
@ -1747,7 +2322,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
%{conn: conn} do
|
||||
user = insert(:user, hide_followers: true)
|
||||
other_user = insert(:user)
|
||||
{:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user)
|
||||
{:ok, user, _other_user, _activity} = CommonAPI.follow(user, other_user)
|
||||
|
||||
result =
|
||||
conn
|
||||
|
|
@ -1843,7 +2418,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
%{conn: conn} do
|
||||
user = insert(:user, hide_follows: true)
|
||||
other_user = insert(:user)
|
||||
{:ok, user, _other_user, _activity} = CommonAPI.follow(user, other_user)
|
||||
{:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user)
|
||||
|
||||
result =
|
||||
conn
|
||||
|
|
@ -2062,6 +2637,30 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|> post("/api/ap/upload_media", %{"file" => image, "description" => desc})
|
||||
|> json_response(403)
|
||||
end
|
||||
|
||||
test "they don't work when C2S API is disabled", %{conn: conn} do
|
||||
clear_config([:activitypub, :client_api_enabled], false)
|
||||
|
||||
user = insert(:user)
|
||||
|
||||
assert conn
|
||||
|> assign(:user, user)
|
||||
|> get("/api/ap/whoami")
|
||||
|> response(403)
|
||||
|
||||
desc = "Description of the image"
|
||||
|
||||
image = %Plug.Upload{
|
||||
content_type: "image/jpeg",
|
||||
path: Path.absname("test/fixtures/image.jpg"),
|
||||
filename: "an_image.jpg"
|
||||
}
|
||||
|
||||
assert conn
|
||||
|> assign(:user, user)
|
||||
|> post("/api/ap/upload_media", %{"file" => image, "description" => desc})
|
||||
|> response(403)
|
||||
end
|
||||
end
|
||||
|
||||
test "pinned collection", %{conn: conn} do
|
||||
|
|
|
|||
|
|
@ -232,12 +232,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
|
||||
assert user.avatar == %{
|
||||
"type" => "Image",
|
||||
"url" => [%{"href" => "https://jk.nipponalba.scot/images/profile.jpg"}]
|
||||
"url" => [%{"href" => "https://jk.nipponalba.scot/images/profile.jpg"}],
|
||||
"name" => "profile picture"
|
||||
}
|
||||
|
||||
assert user.banner == %{
|
||||
"type" => "Image",
|
||||
"url" => [%{"href" => "https://jk.nipponalba.scot/images/profile.jpg"}]
|
||||
"url" => [%{"href" => "https://jk.nipponalba.scot/images/profile.jpg"}],
|
||||
"name" => "profile picture"
|
||||
}
|
||||
end
|
||||
|
||||
|
|
@ -432,6 +434,98 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
|
||||
assert user.birthday == ~D[2001-02-12]
|
||||
end
|
||||
|
||||
test "fetches avatar description" do
|
||||
user_id = "https://example.com/users/marcin"
|
||||
|
||||
user_data =
|
||||
"test/fixtures/users_mock/user.json"
|
||||
|> File.read!()
|
||||
|> String.replace("{{nickname}}", "marcin")
|
||||
|> Jason.decode!()
|
||||
|> Map.delete("featured")
|
||||
|> Map.update("icon", %{}, fn image -> Map.put(image, "name", "image description") end)
|
||||
|> Jason.encode!()
|
||||
|
||||
Tesla.Mock.mock(fn
|
||||
%{
|
||||
method: :get,
|
||||
url: ^user_id
|
||||
} ->
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: user_data,
|
||||
headers: [{"content-type", "application/activity+json"}]
|
||||
}
|
||||
end)
|
||||
|
||||
{:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
|
||||
|
||||
assert user.avatar["name"] == "image description"
|
||||
end
|
||||
end
|
||||
|
||||
test "works with avatar/banner href as list" do
|
||||
user_id = "https://queef.in/cute_cat"
|
||||
|
||||
user_data =
|
||||
"test/fixtures/users_mock/href_as_array.json"
|
||||
|> File.read!()
|
||||
|> Jason.decode!()
|
||||
|> Map.delete("featured")
|
||||
|> Jason.encode!()
|
||||
|
||||
Tesla.Mock.mock(fn
|
||||
%{
|
||||
method: :get,
|
||||
url: ^user_id
|
||||
} ->
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: user_data,
|
||||
headers: [{"content-type", "application/activity+json"}]
|
||||
}
|
||||
end)
|
||||
|
||||
{:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
|
||||
|
||||
assert length(user.avatar["url"]) == 1
|
||||
assert length(user.banner["url"]) == 1
|
||||
|
||||
assert user.avatar["url"] |> List.first() |> Map.fetch!("href") ==
|
||||
"https://queef.in/storage/profile.webp"
|
||||
|
||||
assert user.banner["url"] |> List.first() |> Map.fetch!("href") ==
|
||||
"https://queef.in/storage/banner.gif"
|
||||
end
|
||||
|
||||
test "works with alsoKnownAs as string" do
|
||||
user_id = "https://hub.netzgemeinde.eu/channel/jupiter_rowland"
|
||||
|
||||
user_data =
|
||||
"test/fixtures/users_mock/hubzilla-actor-alsoknownas-string.json"
|
||||
|> File.read!()
|
||||
|
||||
user_data_decoded =
|
||||
user_data
|
||||
|> Jason.decode!()
|
||||
|
||||
Tesla.Mock.mock(fn
|
||||
%{
|
||||
method: :get,
|
||||
url: ^user_id
|
||||
} ->
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: user_data,
|
||||
headers: [{"content-type", "application/activity+json"}]
|
||||
}
|
||||
end)
|
||||
|
||||
{:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
|
||||
|
||||
assert is_list(user.also_known_as)
|
||||
assert user.also_known_as == [user_data_decoded["alsoKnownAs"]]
|
||||
end
|
||||
|
||||
test "it fetches the appropriate tag-restricted posts" do
|
||||
|
|
@ -795,12 +889,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
assert object.data["repliesCount"] == 2
|
||||
end
|
||||
|
||||
test "increates quotes count", %{user: user} do
|
||||
test "increases quotes count", %{user: user} do
|
||||
user2 = insert(:user)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "1", visibility: "public"})
|
||||
ap_id = activity.data["id"]
|
||||
quote_data = %{status: "1", quote_id: activity.id}
|
||||
quote_data = %{status: "1", quoted_status_id: activity.id}
|
||||
|
||||
# public
|
||||
{:ok, _} = CommonAPI.post(user2, Map.put(quote_data, :visibility, "public"))
|
||||
|
|
@ -836,6 +930,33 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
end
|
||||
end
|
||||
|
||||
describe "fetch activities for followed hashtags" do
|
||||
test "it should return public activities that reference a given hashtag" do
|
||||
hashtag = insert(:hashtag, name: "tenshi")
|
||||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
|
||||
{:ok, normally_visible} =
|
||||
CommonAPI.post(other_user, %{status: "hello :)", visibility: "public"})
|
||||
|
||||
{:ok, public} = CommonAPI.post(user, %{status: "maji #tenshi", visibility: "public"})
|
||||
{:ok, _unrelated} = CommonAPI.post(user, %{status: "dai #tensh", visibility: "public"})
|
||||
{:ok, unlisted} = CommonAPI.post(user, %{status: "maji #tenshi", visibility: "unlisted"})
|
||||
{:ok, _private} = CommonAPI.post(user, %{status: "maji #tenshi", visibility: "private"})
|
||||
|
||||
activities =
|
||||
ActivityPub.fetch_activities([other_user.follower_address], %{
|
||||
followed_hashtags: [hashtag.id]
|
||||
})
|
||||
|
||||
assert length(activities) == 3
|
||||
normal_id = normally_visible.id
|
||||
public_id = public.id
|
||||
unlisted_id = unlisted.id
|
||||
assert [%{id: ^normal_id}, %{id: ^public_id}, %{id: ^unlisted_id}] = activities
|
||||
end
|
||||
end
|
||||
|
||||
describe "fetch activities in context" do
|
||||
test "retrieves activities that have a given context" do
|
||||
{:ok, activity} = ActivityBuilder.insert(%{"type" => "Create", "context" => "2hu"})
|
||||
|
|
@ -1038,7 +1159,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
refute activity in activities
|
||||
|
||||
followed_user = insert(:user)
|
||||
CommonAPI.follow(user, followed_user)
|
||||
CommonAPI.follow(followed_user, user)
|
||||
{:ok, repeat_activity} = CommonAPI.repeat(activity.id, followed_user)
|
||||
|
||||
activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
|
||||
|
|
@ -1171,7 +1292,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
note_two = insert(:note, data: %{"context" => "suya.."})
|
||||
activity_two = insert(:note_activity, note: note_two)
|
||||
|
||||
{:ok, _activity_two} = CommonAPI.add_mute(user, activity_two)
|
||||
{:ok, _activity_two} = CommonAPI.add_mute(activity_two, user)
|
||||
|
||||
assert [_activity_one] = ActivityPub.fetch_activities([], %{muting_user: user})
|
||||
end
|
||||
|
|
@ -1182,7 +1303,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
note_two = insert(:note, data: %{"context" => "suya.."})
|
||||
activity_two = insert(:note_activity, note: note_two)
|
||||
|
||||
{:ok, _activity_two} = CommonAPI.add_mute(user, activity_two)
|
||||
{:ok, _activity_two} = CommonAPI.add_mute(activity_two, user)
|
||||
|
||||
assert [_activity_two, _activity_one] =
|
||||
ActivityPub.fetch_activities([], %{muting_user: user, with_muted: true})
|
||||
|
|
@ -1212,6 +1333,16 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
assert activity == expected_activity
|
||||
end
|
||||
|
||||
test "includes only reblogs on request" do
|
||||
user = insert(:user)
|
||||
{:ok, _} = ActivityBuilder.insert(%{"type" => "Create"}, %{:user => user})
|
||||
{:ok, expected_activity} = ActivityBuilder.insert(%{"type" => "Announce"}, %{:user => user})
|
||||
|
||||
[activity] = ActivityPub.fetch_user_activities(user, nil, %{only_reblogs: true})
|
||||
|
||||
assert activity == expected_activity
|
||||
end
|
||||
|
||||
describe "irreversible filters" do
|
||||
setup do
|
||||
user = insert(:user)
|
||||
|
|
@ -1358,7 +1489,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
activity = insert(:note_activity)
|
||||
user = insert(:user)
|
||||
booster = insert(:user)
|
||||
{:ok, _reblog_mute} = CommonAPI.hide_reblogs(user, booster)
|
||||
{:ok, _reblog_mute} = CommonAPI.hide_reblogs(booster, user)
|
||||
|
||||
{:ok, activity} = CommonAPI.repeat(activity.id, booster)
|
||||
|
||||
|
|
@ -1371,8 +1502,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
activity = insert(:note_activity)
|
||||
user = insert(:user)
|
||||
booster = insert(:user)
|
||||
{:ok, _reblog_mute} = CommonAPI.hide_reblogs(user, booster)
|
||||
{:ok, _reblog_mute} = CommonAPI.show_reblogs(user, booster)
|
||||
{:ok, _reblog_mute} = CommonAPI.hide_reblogs(booster, user)
|
||||
{:ok, _reblog_mute} = CommonAPI.show_reblogs(booster, user)
|
||||
|
||||
{:ok, activity} = CommonAPI.repeat(activity.id, booster)
|
||||
|
||||
|
|
@ -1393,8 +1524,8 @@ 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"}
|
||||
test "strips / from filename", %{test_file: %Plug.Upload{} = file} do
|
||||
file = %{file | filename: "../../../../../nested/bad.jpg"}
|
||||
{:ok, %Object{} = object} = ActivityPub.upload(file)
|
||||
[%{"href" => href}] = object.data["url"]
|
||||
assert Regex.match?(~r"/bad.jpg$", href)
|
||||
|
|
@ -1452,7 +1583,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
follower = insert(:user)
|
||||
followed = insert(:user)
|
||||
|
||||
{:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed)
|
||||
{:ok, _, _, follow_activity} = CommonAPI.follow(followed, follower)
|
||||
|
||||
with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
|
||||
assert {:error, :reverted} = ActivityPub.unfollow(follower, followed)
|
||||
|
|
@ -1469,7 +1600,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
follower = insert(:user)
|
||||
followed = insert(:user)
|
||||
|
||||
{:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed)
|
||||
{:ok, _, _, follow_activity} = CommonAPI.follow(followed, follower)
|
||||
{:ok, activity} = ActivityPub.unfollow(follower, followed)
|
||||
|
||||
assert activity.data["type"] == "Undo"
|
||||
|
|
@ -1486,7 +1617,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
follower = insert(:user)
|
||||
followed = insert(:user, %{is_locked: true})
|
||||
|
||||
{:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed)
|
||||
{:ok, _, _, follow_activity} = CommonAPI.follow(followed, follower)
|
||||
{:ok, activity} = ActivityPub.unfollow(follower, followed)
|
||||
|
||||
assert activity.data["type"] == "Undo"
|
||||
|
|
@ -1623,32 +1754,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
} = activity
|
||||
end
|
||||
|
||||
test_with_mock "strips status data from Flag, before federating it",
|
||||
%{
|
||||
reporter: reporter,
|
||||
context: context,
|
||||
target_account: target_account,
|
||||
reported_activity: reported_activity,
|
||||
object_ap_id: object_ap_id,
|
||||
content: content
|
||||
},
|
||||
Utils,
|
||||
[:passthrough],
|
||||
[] do
|
||||
{:ok, activity} =
|
||||
ActivityPub.flag(%{
|
||||
actor: reporter,
|
||||
context: context,
|
||||
account: target_account,
|
||||
statuses: [reported_activity],
|
||||
content: content
|
||||
})
|
||||
|
||||
new_data = put_in(activity.data, ["object"], [target_account.ap_id, object_ap_id])
|
||||
|
||||
assert_called(Utils.maybe_federate(%{activity | data: new_data}))
|
||||
end
|
||||
|
||||
test_with_mock "reverts on error",
|
||||
%{
|
||||
reporter: reporter,
|
||||
|
|
@ -1678,13 +1783,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
test "fetch_activities/2 returns activities addressed to a list " do
|
||||
user = insert(:user)
|
||||
member = insert(:user)
|
||||
{:ok, list} = Pleroma.List.create("foo", user)
|
||||
{:ok, list} = Pleroma.List.create(%{title: "foo"}, user)
|
||||
{:ok, list} = Pleroma.List.follow(list, member)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})
|
||||
{:ok, %Activity{} = activity} =
|
||||
CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})
|
||||
|
||||
activity = Repo.preload(activity, :bookmark)
|
||||
activity = %Activity{activity | thread_muted?: !!activity.thread_muted?}
|
||||
activity = %{activity | thread_muted?: !!activity.thread_muted?}
|
||||
|
||||
assert ActivityPub.fetch_activities([], %{user: user}) == [activity]
|
||||
end
|
||||
|
|
@ -1727,8 +1833,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
user =
|
||||
insert(:user,
|
||||
local: false,
|
||||
follower_address: "http://localhost:4001/users/fuser2/followers",
|
||||
following_address: "http://localhost:4001/users/fuser2/following"
|
||||
follower_address: "https://remote.org/users/fuser2/followers",
|
||||
following_address: "https://remote.org/users/fuser2/following"
|
||||
)
|
||||
|
||||
{:ok, info} = ActivityPub.fetch_follow_information_for_user(user)
|
||||
|
|
@ -1739,7 +1845,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
test "detects hidden followers" do
|
||||
mock(fn env ->
|
||||
case env.url do
|
||||
"http://localhost:4001/users/masto_closed/followers?page=1" ->
|
||||
"https://remote.org/users/masto_closed/followers?page=1" ->
|
||||
%Tesla.Env{status: 403, body: ""}
|
||||
|
||||
_ ->
|
||||
|
|
@ -1750,8 +1856,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
user =
|
||||
insert(:user,
|
||||
local: false,
|
||||
follower_address: "http://localhost:4001/users/masto_closed/followers",
|
||||
following_address: "http://localhost:4001/users/masto_closed/following"
|
||||
follower_address: "https://remote.org/users/masto_closed/followers",
|
||||
following_address: "https://remote.org/users/masto_closed/following"
|
||||
)
|
||||
|
||||
{:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
|
||||
|
|
@ -1762,7 +1868,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
test "detects hidden follows" do
|
||||
mock(fn env ->
|
||||
case env.url do
|
||||
"http://localhost:4001/users/masto_closed/following?page=1" ->
|
||||
"https://remote.org/users/masto_closed/following?page=1" ->
|
||||
%Tesla.Env{status: 403, body: ""}
|
||||
|
||||
_ ->
|
||||
|
|
@ -1773,8 +1879,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
user =
|
||||
insert(:user,
|
||||
local: false,
|
||||
follower_address: "http://localhost:4001/users/masto_closed/followers",
|
||||
following_address: "http://localhost:4001/users/masto_closed/following"
|
||||
follower_address: "https://remote.org/users/masto_closed/followers",
|
||||
following_address: "https://remote.org/users/masto_closed/following"
|
||||
)
|
||||
|
||||
{:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
|
||||
|
|
@ -1786,8 +1892,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
user =
|
||||
insert(:user,
|
||||
local: false,
|
||||
follower_address: "http://localhost:8080/followers/fuser3",
|
||||
following_address: "http://localhost:8080/following/fuser3"
|
||||
follower_address: "https://remote.org/followers/fuser3",
|
||||
following_address: "https://remote.org/following/fuser3"
|
||||
)
|
||||
|
||||
{:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
|
||||
|
|
@ -1800,28 +1906,28 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
test "doesn't crash when follower and following counters are hidden" do
|
||||
mock(fn env ->
|
||||
case env.url do
|
||||
"http://localhost:4001/users/masto_hidden_counters/following" ->
|
||||
"https://remote.org/users/masto_hidden_counters/following" ->
|
||||
json(
|
||||
%{
|
||||
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"id" => "http://localhost:4001/users/masto_hidden_counters/followers"
|
||||
"id" => "https://remote.org/users/masto_hidden_counters/followers"
|
||||
},
|
||||
headers: HttpRequestMock.activitypub_object_headers()
|
||||
)
|
||||
|
||||
"http://localhost:4001/users/masto_hidden_counters/following?page=1" ->
|
||||
"https://remote.org/users/masto_hidden_counters/following?page=1" ->
|
||||
%Tesla.Env{status: 403, body: ""}
|
||||
|
||||
"http://localhost:4001/users/masto_hidden_counters/followers" ->
|
||||
"https://remote.org/users/masto_hidden_counters/followers" ->
|
||||
json(
|
||||
%{
|
||||
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"id" => "http://localhost:4001/users/masto_hidden_counters/following"
|
||||
"id" => "https://remote.org/users/masto_hidden_counters/following"
|
||||
},
|
||||
headers: HttpRequestMock.activitypub_object_headers()
|
||||
)
|
||||
|
||||
"http://localhost:4001/users/masto_hidden_counters/followers?page=1" ->
|
||||
"https://remote.org/users/masto_hidden_counters/followers?page=1" ->
|
||||
%Tesla.Env{status: 403, body: ""}
|
||||
end
|
||||
end)
|
||||
|
|
@ -1829,8 +1935,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
user =
|
||||
insert(:user,
|
||||
local: false,
|
||||
follower_address: "http://localhost:4001/users/masto_hidden_counters/followers",
|
||||
following_address: "http://localhost:4001/users/masto_hidden_counters/following"
|
||||
follower_address: "https://remote.org/users/masto_hidden_counters/followers",
|
||||
following_address: "https://remote.org/users/masto_hidden_counters/following"
|
||||
)
|
||||
|
||||
{:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
|
||||
|
|
@ -1854,14 +1960,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
{:ok, a4} = CommonAPI.post(user2, %{status: "Agent Smith "})
|
||||
{:ok, a5} = CommonAPI.post(user1, %{status: "Red or Blue "})
|
||||
|
||||
{:ok, _} = CommonAPI.favorite(user, a4.id)
|
||||
{:ok, _} = CommonAPI.favorite(other_user, a3.id)
|
||||
{:ok, _} = CommonAPI.favorite(user, a3.id)
|
||||
{:ok, _} = CommonAPI.favorite(other_user, a5.id)
|
||||
{:ok, _} = CommonAPI.favorite(user, a5.id)
|
||||
{:ok, _} = CommonAPI.favorite(other_user, a4.id)
|
||||
{:ok, _} = CommonAPI.favorite(user, a1.id)
|
||||
{:ok, _} = CommonAPI.favorite(other_user, a1.id)
|
||||
{:ok, _} = CommonAPI.favorite(a4.id, user)
|
||||
{:ok, _} = CommonAPI.favorite(a3.id, other_user)
|
||||
{:ok, _} = CommonAPI.favorite(a3.id, user)
|
||||
{:ok, _} = CommonAPI.favorite(a5.id, other_user)
|
||||
{:ok, _} = CommonAPI.favorite(a5.id, user)
|
||||
{:ok, _} = CommonAPI.favorite(a4.id, other_user)
|
||||
{:ok, _} = CommonAPI.favorite(a1.id, user)
|
||||
{:ok, _} = CommonAPI.favorite(a1.id, other_user)
|
||||
result = ActivityPub.fetch_favourites(user)
|
||||
|
||||
assert Enum.map(result, & &1.id) == [a1.id, a5.id, a3.id, a4.id]
|
||||
|
|
@ -1884,7 +1990,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
assert User.following?(follower, old_user)
|
||||
assert User.following?(follower_move_opted_out, old_user)
|
||||
|
||||
assert {:ok, activity} = ActivityPub.move(old_user, new_user)
|
||||
assert {:ok, %Activity{} = activity} = ActivityPub.move(old_user, new_user)
|
||||
|
||||
assert %Activity{
|
||||
actor: ^old_ap_id,
|
||||
|
|
@ -1916,7 +2022,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
assert User.following?(follower_move_opted_out, old_user)
|
||||
refute User.following?(follower_move_opted_out, new_user)
|
||||
|
||||
activity = %Activity{activity | object: nil}
|
||||
activity = %{activity | object: nil}
|
||||
|
||||
assert [%Notification{activity: ^activity}] = Notification.for_user(follower)
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.HashtagPolicyTest do
|
|||
user = insert(:user)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "#nsfw hey"})
|
||||
{:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
|
||||
{:ok, modified} = Transmogrifier.prepare_activity(activity.data)
|
||||
|
||||
assert modified["object"]["sensitive"]
|
||||
end
|
||||
|
|
@ -94,7 +94,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.HashtagPolicyTest do
|
|||
user = insert(:user)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "#cofe hey"})
|
||||
{:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
|
||||
{:ok, modified} = Transmogrifier.prepare_activity(activity.data)
|
||||
|
||||
refute modified["object"]["sensitive"]
|
||||
end
|
||||
|
|
|
|||
|
|
@ -109,4 +109,22 @@ defmodule Pleroma.Web.ActivityPub.MRF.InlineQuotePolicyTest do
|
|||
{:ok, filtered} = InlineQuotePolicy.filter(activity)
|
||||
assert filtered == activity
|
||||
end
|
||||
|
||||
# Mastodon uses p tags instead of span in their quote posts
|
||||
# URLs in quoteUri and post content are already mismatched
|
||||
test "skips objects which already have an .inline-quote p" do
|
||||
object = File.read!("test/fixtures/quote_post/mastodon_quote_post.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://mastodon.social/users/gwynnion",
|
||||
"object" => object
|
||||
}
|
||||
|
||||
{:ok, filtered} = InlineQuotePolicy.filter(activity)
|
||||
assert filtered == activity
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -54,14 +54,17 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicyTest do
|
|||
setup do: clear_config([:media_proxy, :enabled], true)
|
||||
|
||||
test "it prefetches media proxy URIs" 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
|
||||
with_mock HTTP,
|
||||
get: fn _, _, opts ->
|
||||
send(self(), {:prefetch_opts, opts})
|
||||
{:ok, []}
|
||||
end do
|
||||
MediaProxyWarmingPolicy.filter(@message)
|
||||
|
||||
assert called(HTTP.get(:_, :_, :_))
|
||||
assert_receive {:prefetch_opts, opts}
|
||||
refute Keyword.has_key?(opts, :follow_redirect)
|
||||
refute Keyword.has_key?(opts, :force_redirect)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -81,10 +84,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicyTest do
|
|||
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)
|
||||
|
||||
|
|
@ -93,10 +92,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicyTest do
|
|||
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"))
|
||||
|
||||
|
|
|
|||
139
test/pleroma/web/activity_pub/mrf/quiet_reply_test.exs
Normal file
139
test/pleroma/web/activity_pub/mrf/quiet_reply_test.exs
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.MRF.QuietReplyTest do
|
||||
use Pleroma.DataCase
|
||||
import Pleroma.Factory
|
||||
|
||||
require Pleroma.Constants
|
||||
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Web.ActivityPub.MRF.QuietReply
|
||||
alias Pleroma.Web.CommonAPI
|
||||
|
||||
test "replying to public post is forced to be quiet" do
|
||||
batman = insert(:user, nickname: "batman")
|
||||
robin = insert(:user, nickname: "robin")
|
||||
|
||||
{:ok, post} = CommonAPI.post(batman, %{status: "To the Batmobile!"})
|
||||
|
||||
reply = %{
|
||||
"type" => "Create",
|
||||
"actor" => robin.ap_id,
|
||||
"to" => [
|
||||
batman.ap_id,
|
||||
Pleroma.Constants.as_public()
|
||||
],
|
||||
"cc" => [robin.follower_address],
|
||||
"object" => %{
|
||||
"type" => "Note",
|
||||
"actor" => robin.ap_id,
|
||||
"content" => "@batman Wait up, I forgot my spandex!",
|
||||
"to" => [
|
||||
batman.ap_id,
|
||||
Pleroma.Constants.as_public()
|
||||
],
|
||||
"cc" => [robin.follower_address],
|
||||
"inReplyTo" => Object.normalize(post).data["id"]
|
||||
}
|
||||
}
|
||||
|
||||
assert {:ok, filtered} = QuietReply.filter(reply)
|
||||
|
||||
assert batman.ap_id in filtered["to"]
|
||||
assert batman.ap_id in filtered["object"]["to"]
|
||||
assert robin.follower_address in filtered["to"]
|
||||
assert robin.follower_address in filtered["object"]["to"]
|
||||
assert Pleroma.Constants.as_public() in filtered["cc"]
|
||||
assert Pleroma.Constants.as_public() in filtered["object"]["cc"]
|
||||
end
|
||||
|
||||
test "replying to unlisted post is unmodified" do
|
||||
batman = insert(:user, nickname: "batman")
|
||||
robin = insert(:user, nickname: "robin")
|
||||
|
||||
{:ok, post} = CommonAPI.post(batman, %{status: "To the Batmobile!", visibility: "private"})
|
||||
|
||||
reply = %{
|
||||
"type" => "Create",
|
||||
"actor" => robin.ap_id,
|
||||
"to" => [batman.ap_id],
|
||||
"cc" => [],
|
||||
"object" => %{
|
||||
"type" => "Note",
|
||||
"actor" => robin.ap_id,
|
||||
"content" => "@batman Wait up, I forgot my spandex!",
|
||||
"to" => [batman.ap_id],
|
||||
"cc" => [],
|
||||
"inReplyTo" => Object.normalize(post).data["id"]
|
||||
}
|
||||
}
|
||||
|
||||
assert {:ok, filtered} = QuietReply.filter(reply)
|
||||
|
||||
assert match?(^filtered, reply)
|
||||
end
|
||||
|
||||
test "replying direct is unmodified" do
|
||||
batman = insert(:user, nickname: "batman")
|
||||
robin = insert(:user, nickname: "robin")
|
||||
|
||||
{:ok, post} = CommonAPI.post(batman, %{status: "To the Batmobile!"})
|
||||
|
||||
reply = %{
|
||||
"type" => "Create",
|
||||
"actor" => robin.ap_id,
|
||||
"to" => [batman.ap_id],
|
||||
"cc" => [],
|
||||
"object" => %{
|
||||
"type" => "Note",
|
||||
"actor" => robin.ap_id,
|
||||
"content" => "@batman Wait up, I forgot my spandex!",
|
||||
"to" => [batman.ap_id],
|
||||
"cc" => [],
|
||||
"inReplyTo" => Object.normalize(post).data["id"]
|
||||
}
|
||||
}
|
||||
|
||||
assert {:ok, filtered} = QuietReply.filter(reply)
|
||||
|
||||
assert match?(^filtered, reply)
|
||||
end
|
||||
|
||||
test "replying followers-only is unmodified" do
|
||||
batman = insert(:user, nickname: "batman")
|
||||
robin = insert(:user, nickname: "robin")
|
||||
|
||||
{:ok, post} = CommonAPI.post(batman, %{status: "To the Batmobile!"})
|
||||
|
||||
reply = %{
|
||||
"type" => "Create",
|
||||
"actor" => robin.ap_id,
|
||||
"to" => [batman.ap_id, robin.follower_address],
|
||||
"cc" => [],
|
||||
"object" => %{
|
||||
"type" => "Note",
|
||||
"actor" => robin.ap_id,
|
||||
"content" => "@batman Wait up, I forgot my spandex!",
|
||||
"to" => [batman.ap_id, robin.follower_address],
|
||||
"cc" => [],
|
||||
"inReplyTo" => Object.normalize(post).data["id"]
|
||||
}
|
||||
}
|
||||
|
||||
assert {:ok, filtered} = QuietReply.filter(reply)
|
||||
|
||||
assert match?(^filtered, reply)
|
||||
end
|
||||
|
||||
test "non-reply posts are unmodified" do
|
||||
batman = insert(:user, nickname: "batman")
|
||||
|
||||
{:ok, post} = CommonAPI.post(batman, %{status: "To the Batmobile!"})
|
||||
|
||||
assert {:ok, filtered} = QuietReply.filter(post)
|
||||
|
||||
assert match?(^filtered, post)
|
||||
end
|
||||
end
|
||||
155
test/pleroma/web/activity_pub/mrf/remote_report_policy_test.exs
Normal file
155
test/pleroma/web/activity_pub/mrf/remote_report_policy_test.exs
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
defmodule Pleroma.Web.ActivityPub.MRF.RemoteReportPolicyTest do
|
||||
use Pleroma.DataCase, async: false
|
||||
|
||||
alias Pleroma.Web.ActivityPub.MRF.RemoteReportPolicy
|
||||
|
||||
setup do
|
||||
clear_config([:mrf_remote_report, :reject_all], false)
|
||||
end
|
||||
|
||||
test "doesn't impact local report" do
|
||||
clear_config([:mrf_remote_report, :reject_anonymous], true)
|
||||
clear_config([:mrf_remote_report, :reject_empty_message], true)
|
||||
|
||||
activity = %{
|
||||
"type" => "Flag",
|
||||
"actor" => "http://localhost:4001/actor",
|
||||
"object" => ["https://mastodon.online/users/Gargron"]
|
||||
}
|
||||
|
||||
assert {:ok, _} = RemoteReportPolicy.filter(activity)
|
||||
end
|
||||
|
||||
test "rejects anonymous report if `reject_anonymous: true`" do
|
||||
clear_config([:mrf_remote_report, :reject_anonymous], true)
|
||||
clear_config([:mrf_remote_report, :reject_empty_message], true)
|
||||
|
||||
activity = %{
|
||||
"type" => "Flag",
|
||||
"actor" => "https://mastodon.social/actor",
|
||||
"object" => ["https://mastodon.online/users/Gargron"]
|
||||
}
|
||||
|
||||
assert {:reject, _} = RemoteReportPolicy.filter(activity)
|
||||
end
|
||||
|
||||
test "preserves anonymous report if `reject_anonymous: false`" do
|
||||
clear_config([:mrf_remote_report, :reject_anonymous], false)
|
||||
clear_config([:mrf_remote_report, :reject_empty_message], false)
|
||||
|
||||
activity = %{
|
||||
"type" => "Flag",
|
||||
"actor" => "https://mastodon.social/actor",
|
||||
"object" => ["https://mastodon.online/users/Gargron"]
|
||||
}
|
||||
|
||||
assert {:ok, _} = RemoteReportPolicy.filter(activity)
|
||||
end
|
||||
|
||||
test "rejects report on third party if `reject_third_party: true`" do
|
||||
clear_config([:mrf_remote_report, :reject_third_party], true)
|
||||
clear_config([:mrf_remote_report, :reject_empty_message], false)
|
||||
|
||||
activity = %{
|
||||
"type" => "Flag",
|
||||
"actor" => "https://mastodon.social/users/Gargron",
|
||||
"object" => ["https://mastodon.online/users/Gargron"]
|
||||
}
|
||||
|
||||
assert {:reject, _} = RemoteReportPolicy.filter(activity)
|
||||
end
|
||||
|
||||
test "preserves report on first party if `reject_third_party: true`" do
|
||||
clear_config([:mrf_remote_report, :reject_third_party], true)
|
||||
clear_config([:mrf_remote_report, :reject_empty_message], false)
|
||||
|
||||
activity = %{
|
||||
"type" => "Flag",
|
||||
"actor" => "https://mastodon.social/users/Gargron",
|
||||
"object" => ["http://localhost:4001/actor"]
|
||||
}
|
||||
|
||||
assert {:ok, _} = RemoteReportPolicy.filter(activity)
|
||||
end
|
||||
|
||||
test "preserves report on third party if `reject_third_party: false`" do
|
||||
clear_config([:mrf_remote_report, :reject_third_party], false)
|
||||
clear_config([:mrf_remote_report, :reject_empty_message], false)
|
||||
|
||||
activity = %{
|
||||
"type" => "Flag",
|
||||
"actor" => "https://mastodon.social/users/Gargron",
|
||||
"object" => ["https://mastodon.online/users/Gargron"]
|
||||
}
|
||||
|
||||
assert {:ok, _} = RemoteReportPolicy.filter(activity)
|
||||
end
|
||||
|
||||
test "rejects empty message report if `reject_empty_message: true`" do
|
||||
clear_config([:mrf_remote_report, :reject_anonymous], false)
|
||||
clear_config([:mrf_remote_report, :reject_empty_message], true)
|
||||
|
||||
activity = %{
|
||||
"type" => "Flag",
|
||||
"actor" => "https://mastodon.social/users/Gargron",
|
||||
"object" => ["https://mastodon.online/users/Gargron"]
|
||||
}
|
||||
|
||||
assert {:reject, _} = RemoteReportPolicy.filter(activity)
|
||||
end
|
||||
|
||||
test "rejects empty message report (\"\") if `reject_empty_message: true`" do
|
||||
clear_config([:mrf_remote_report, :reject_anonymous], false)
|
||||
clear_config([:mrf_remote_report, :reject_empty_message], true)
|
||||
|
||||
activity = %{
|
||||
"type" => "Flag",
|
||||
"actor" => "https://mastodon.social/users/Gargron",
|
||||
"object" => ["https://mastodon.online/users/Gargron"],
|
||||
"content" => ""
|
||||
}
|
||||
|
||||
assert {:reject, _} = RemoteReportPolicy.filter(activity)
|
||||
end
|
||||
|
||||
test "preserves empty message report if `reject_empty_message: false`" do
|
||||
clear_config([:mrf_remote_report, :reject_anonymous], false)
|
||||
clear_config([:mrf_remote_report, :reject_empty_message], false)
|
||||
|
||||
activity = %{
|
||||
"type" => "Flag",
|
||||
"actor" => "https://mastodon.social/users/Gargron",
|
||||
"object" => ["https://mastodon.online/users/Gargron"]
|
||||
}
|
||||
|
||||
assert {:ok, _} = RemoteReportPolicy.filter(activity)
|
||||
end
|
||||
|
||||
test "preserves anonymous, empty message report with all settings disabled" do
|
||||
clear_config([:mrf_remote_report, :reject_anonymous], false)
|
||||
clear_config([:mrf_remote_report, :reject_empty_message], false)
|
||||
|
||||
activity = %{
|
||||
"type" => "Flag",
|
||||
"actor" => "https://mastodon.social/actor",
|
||||
"object" => ["https://mastodon.online/users/Gargron"]
|
||||
}
|
||||
|
||||
assert {:ok, _} = RemoteReportPolicy.filter(activity)
|
||||
end
|
||||
|
||||
test "reject remote report if `reject_all: true`" do
|
||||
clear_config([:mrf_remote_report, :reject_all], true)
|
||||
clear_config([:mrf_remote_report, :reject_anonymous], false)
|
||||
clear_config([:mrf_remote_report, :reject_empty_message], false)
|
||||
|
||||
activity = %{
|
||||
"type" => "Flag",
|
||||
"actor" => "https://mastodon.social/users/Gargron",
|
||||
"content" => "Transphobia",
|
||||
"object" => ["https://mastodon.online/users/Gargron"]
|
||||
}
|
||||
|
||||
assert {:reject, _} = RemoteReportPolicy.filter(activity)
|
||||
end
|
||||
end
|
||||
|
|
@ -252,6 +252,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
|
|||
remote_message = build_remote_message()
|
||||
|
||||
assert SimplePolicy.filter(remote_message) == {:ok, remote_message}
|
||||
assert SimplePolicy.id_filter(remote_message["actor"])
|
||||
end
|
||||
|
||||
test "activity has a matching host" do
|
||||
|
|
@ -260,6 +261,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
|
|||
remote_message = build_remote_message()
|
||||
|
||||
assert {:reject, _} = SimplePolicy.filter(remote_message)
|
||||
refute SimplePolicy.id_filter(remote_message["actor"])
|
||||
end
|
||||
|
||||
test "activity matches with wildcard domain" do
|
||||
|
|
@ -268,6 +270,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
|
|||
remote_message = build_remote_message()
|
||||
|
||||
assert {:reject, _} = SimplePolicy.filter(remote_message)
|
||||
refute SimplePolicy.id_filter(remote_message["actor"])
|
||||
end
|
||||
|
||||
test "actor has a matching host" do
|
||||
|
|
@ -276,6 +279,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
|
|||
remote_user = build_remote_user()
|
||||
|
||||
assert {:reject, _} = SimplePolicy.filter(remote_user)
|
||||
refute SimplePolicy.id_filter(remote_user["id"])
|
||||
end
|
||||
|
||||
test "reject Announce when object would be rejected" do
|
||||
|
|
@ -288,6 +292,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
|
|||
}
|
||||
|
||||
assert {:reject, _} = SimplePolicy.filter(announce)
|
||||
# Note: Non-Applicable for id_filter/1
|
||||
end
|
||||
|
||||
test "reject by URI object" do
|
||||
|
|
@ -300,6 +305,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
|
|||
}
|
||||
|
||||
assert {:reject, _} = SimplePolicy.filter(announce)
|
||||
# Note: Non-Applicable for id_filter/1
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -318,7 +324,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
|
|||
following_user = insert(:user)
|
||||
non_following_user = insert(:user)
|
||||
|
||||
{:ok, _, _, _} = CommonAPI.follow(following_user, actor)
|
||||
{:ok, _, _, _} = CommonAPI.follow(actor, following_user)
|
||||
|
||||
activity = %{
|
||||
"actor" => actor.ap_id,
|
||||
|
|
@ -370,6 +376,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
|
|||
|
||||
assert SimplePolicy.filter(local_message) == {:ok, local_message}
|
||||
assert SimplePolicy.filter(remote_message) == {:ok, remote_message}
|
||||
assert SimplePolicy.id_filter(local_message["actor"])
|
||||
assert SimplePolicy.id_filter(remote_message["actor"])
|
||||
end
|
||||
|
||||
test "is not empty but activity doesn't have a matching host" do
|
||||
|
|
@ -380,6 +388,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
|
|||
|
||||
assert SimplePolicy.filter(local_message) == {:ok, local_message}
|
||||
assert {:reject, _} = SimplePolicy.filter(remote_message)
|
||||
assert SimplePolicy.id_filter(local_message["actor"])
|
||||
refute SimplePolicy.id_filter(remote_message["actor"])
|
||||
end
|
||||
|
||||
test "activity has a matching host" do
|
||||
|
|
@ -390,6 +400,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
|
|||
|
||||
assert SimplePolicy.filter(local_message) == {:ok, local_message}
|
||||
assert SimplePolicy.filter(remote_message) == {:ok, remote_message}
|
||||
assert SimplePolicy.id_filter(local_message["actor"])
|
||||
assert SimplePolicy.id_filter(remote_message["actor"])
|
||||
end
|
||||
|
||||
test "activity matches with wildcard domain" do
|
||||
|
|
@ -400,6 +412,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
|
|||
|
||||
assert SimplePolicy.filter(local_message) == {:ok, local_message}
|
||||
assert SimplePolicy.filter(remote_message) == {:ok, remote_message}
|
||||
assert SimplePolicy.id_filter(local_message["actor"])
|
||||
assert SimplePolicy.id_filter(remote_message["actor"])
|
||||
end
|
||||
|
||||
test "actor has a matching host" do
|
||||
|
|
@ -408,6 +422,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
|
|||
remote_user = build_remote_user()
|
||||
|
||||
assert SimplePolicy.filter(remote_user) == {:ok, remote_user}
|
||||
assert SimplePolicy.id_filter(remote_user["id"])
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicyTest do
|
|||
assert File.exists?(fullpath)
|
||||
end
|
||||
|
||||
test "rejects invalid shortcodes", %{path: path} do
|
||||
test "rejects invalid shortcodes with slashes", %{path: path} do
|
||||
message = %{
|
||||
"type" => "Create",
|
||||
"object" => %{
|
||||
|
|
@ -113,6 +113,58 @@ defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicyTest do
|
|||
refute File.exists?(fullpath)
|
||||
end
|
||||
|
||||
test "rejects invalid shortcodes with dots", %{path: path} do
|
||||
message = %{
|
||||
"type" => "Create",
|
||||
"object" => %{
|
||||
"emoji" => [{"fired.fox", "https://example.org/emoji/firedfox"}],
|
||||
"actor" => "https://example.org/users/admin"
|
||||
}
|
||||
}
|
||||
|
||||
fullpath = Path.join(path, "fired.fox.png")
|
||||
|
||||
Tesla.Mock.mock(fn %{method: :get, url: "https://example.org/emoji/firedfox"} ->
|
||||
%Tesla.Env{status: 200, body: File.read!("test/fixtures/image.jpg")}
|
||||
end)
|
||||
|
||||
clear_config(:mrf_steal_emoji, hosts: ["example.org"], size_limit: 284_468)
|
||||
|
||||
refute "fired.fox" in installed()
|
||||
refute File.exists?(path)
|
||||
|
||||
assert {:ok, _message} = StealEmojiPolicy.filter(message)
|
||||
|
||||
refute "fired.fox" in installed()
|
||||
refute File.exists?(fullpath)
|
||||
end
|
||||
|
||||
test "rejects invalid shortcodes with special characters", %{path: path} do
|
||||
message = %{
|
||||
"type" => "Create",
|
||||
"object" => %{
|
||||
"emoji" => [{"fired:fox", "https://example.org/emoji/firedfox"}],
|
||||
"actor" => "https://example.org/users/admin"
|
||||
}
|
||||
}
|
||||
|
||||
fullpath = Path.join(path, "fired:fox.png")
|
||||
|
||||
Tesla.Mock.mock(fn %{method: :get, url: "https://example.org/emoji/firedfox"} ->
|
||||
%Tesla.Env{status: 200, body: File.read!("test/fixtures/image.jpg")}
|
||||
end)
|
||||
|
||||
clear_config(:mrf_steal_emoji, hosts: ["example.org"], size_limit: 284_468)
|
||||
|
||||
refute "fired:fox" in installed()
|
||||
refute File.exists?(path)
|
||||
|
||||
assert {:ok, _message} = StealEmojiPolicy.filter(message)
|
||||
|
||||
refute "fired:fox" in installed()
|
||||
refute File.exists?(fullpath)
|
||||
end
|
||||
|
||||
test "reject regex shortcode", %{message: message} do
|
||||
refute "firedfox" in installed()
|
||||
|
||||
|
|
@ -171,5 +223,74 @@ defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicyTest do
|
|||
refute "firedfox" in installed()
|
||||
end
|
||||
|
||||
test "accepts valid alphanum shortcodes", %{path: path} do
|
||||
message = %{
|
||||
"type" => "Create",
|
||||
"object" => %{
|
||||
"emoji" => [{"fire1fox", "https://example.org/emoji/fire1fox.png"}],
|
||||
"actor" => "https://example.org/users/admin"
|
||||
}
|
||||
}
|
||||
|
||||
Tesla.Mock.mock(fn %{method: :get, url: "https://example.org/emoji/fire1fox.png"} ->
|
||||
%Tesla.Env{status: 200, body: File.read!("test/fixtures/image.jpg")}
|
||||
end)
|
||||
|
||||
clear_config(:mrf_steal_emoji, hosts: ["example.org"], size_limit: 284_468)
|
||||
|
||||
refute "fire1fox" in installed()
|
||||
refute File.exists?(path)
|
||||
|
||||
assert {:ok, _message} = StealEmojiPolicy.filter(message)
|
||||
|
||||
assert "fire1fox" in installed()
|
||||
end
|
||||
|
||||
test "accepts valid shortcodes with underscores", %{path: path} do
|
||||
message = %{
|
||||
"type" => "Create",
|
||||
"object" => %{
|
||||
"emoji" => [{"fire_fox", "https://example.org/emoji/fire_fox.png"}],
|
||||
"actor" => "https://example.org/users/admin"
|
||||
}
|
||||
}
|
||||
|
||||
Tesla.Mock.mock(fn %{method: :get, url: "https://example.org/emoji/fire_fox.png"} ->
|
||||
%Tesla.Env{status: 200, body: File.read!("test/fixtures/image.jpg")}
|
||||
end)
|
||||
|
||||
clear_config(:mrf_steal_emoji, hosts: ["example.org"], size_limit: 284_468)
|
||||
|
||||
refute "fire_fox" in installed()
|
||||
refute File.exists?(path)
|
||||
|
||||
assert {:ok, _message} = StealEmojiPolicy.filter(message)
|
||||
|
||||
assert "fire_fox" in installed()
|
||||
end
|
||||
|
||||
test "accepts valid shortcodes with hyphens", %{path: path} do
|
||||
message = %{
|
||||
"type" => "Create",
|
||||
"object" => %{
|
||||
"emoji" => [{"fire-fox", "https://example.org/emoji/fire-fox.png"}],
|
||||
"actor" => "https://example.org/users/admin"
|
||||
}
|
||||
}
|
||||
|
||||
Tesla.Mock.mock(fn %{method: :get, url: "https://example.org/emoji/fire-fox.png"} ->
|
||||
%Tesla.Env{status: 200, body: File.read!("test/fixtures/image.jpg")}
|
||||
end)
|
||||
|
||||
clear_config(:mrf_steal_emoji, hosts: ["example.org"], size_limit: 284_468)
|
||||
|
||||
refute "fire-fox" in installed()
|
||||
refute File.exists?(path)
|
||||
|
||||
assert {:ok, _message} = StealEmojiPolicy.filter(message)
|
||||
|
||||
assert "fire-fox" in installed()
|
||||
end
|
||||
|
||||
defp installed, do: Emoji.get_all() |> Enum.map(fn {k, _} -> k end)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -10,18 +10,25 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do
|
|||
|
||||
alias Pleroma.Web.ActivityPub.MRF
|
||||
|
||||
defp regexes_match!([],[]), do: true
|
||||
|
||||
defp regexes_match!([authority | authority_rest], [checked | checked_rest]) do
|
||||
authority.source == checked.source and regexes_match!(authority_rest, checked_rest)
|
||||
end
|
||||
|
||||
defp regexes_match!(_, _), do: false
|
||||
|
||||
test "subdomains_regex/1" do
|
||||
assert MRF.subdomains_regex(["unsafe.tld", "*.unsafe.tld"]) == [
|
||||
~r/^unsafe.tld$/i,
|
||||
~r/^(.*\.)*unsafe.tld$/i
|
||||
]
|
||||
regexes = MRF.subdomains_regex(["unsafe.tld", "*.unsafe.tld"])
|
||||
|
||||
assert regexes_match!(regexes, [~r/^unsafe.tld$/i, ~r/^(.*\.)*unsafe.tld$/i])
|
||||
end
|
||||
|
||||
describe "subdomain_match/2" do
|
||||
test "common domains" do
|
||||
regexes = MRF.subdomains_regex(["unsafe.tld", "unsafe2.tld"])
|
||||
|
||||
assert regexes == [~r/^unsafe.tld$/i, ~r/^unsafe2.tld$/i]
|
||||
assert regexes_match!(regexes, [~r/^unsafe.tld$/i, ~r/^unsafe2.tld$/i])
|
||||
|
||||
assert MRF.subdomain_match?(regexes, "unsafe.tld")
|
||||
assert MRF.subdomain_match?(regexes, "unsafe2.tld")
|
||||
|
|
@ -32,7 +39,7 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do
|
|||
test "wildcard domains with one subdomain" do
|
||||
regexes = MRF.subdomains_regex(["*.unsafe.tld"])
|
||||
|
||||
assert regexes == [~r/^(.*\.)*unsafe.tld$/i]
|
||||
assert regexes_match!(regexes, [~r/^(.*\.)*unsafe.tld$/i])
|
||||
|
||||
assert MRF.subdomain_match?(regexes, "unsafe.tld")
|
||||
assert MRF.subdomain_match?(regexes, "sub.unsafe.tld")
|
||||
|
|
@ -43,7 +50,7 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do
|
|||
test "wildcard domains with two subdomains" do
|
||||
regexes = MRF.subdomains_regex(["*.unsafe.tld"])
|
||||
|
||||
assert regexes == [~r/^(.*\.)*unsafe.tld$/i]
|
||||
assert regexes_match!(regexes, [~r/^(.*\.)*unsafe.tld$/i])
|
||||
|
||||
assert MRF.subdomain_match?(regexes, "unsafe.tld")
|
||||
assert MRF.subdomain_match?(regexes, "sub.sub.unsafe.tld")
|
||||
|
|
@ -54,7 +61,7 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do
|
|||
test "matches are case-insensitive" do
|
||||
regexes = MRF.subdomains_regex(["UnSafe.TLD", "UnSAFE2.Tld"])
|
||||
|
||||
assert regexes == [~r/^UnSafe.TLD$/i, ~r/^UnSAFE2.Tld$/i]
|
||||
assert regexes_match!(regexes, [~r/^UnSafe.TLD$/i, ~r/^UnSAFE2.Tld$/i])
|
||||
|
||||
assert MRF.subdomain_match?(regexes, "UNSAFE.TLD")
|
||||
assert MRF.subdomain_match?(regexes, "UNSAFE2.TLD")
|
||||
|
|
|
|||
|
|
@ -86,23 +86,32 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidationTest do
|
|||
object = Object.normalize(post_activity, fetch: false)
|
||||
|
||||
# Another user can't announce it
|
||||
{:ok, announce, []} = Builder.announce(announcer, object, public: false)
|
||||
{:ok, announce, []} = Builder.announce(announcer, object, visibility: "private")
|
||||
|
||||
{:error, cng} = ObjectValidator.validate(announce, [])
|
||||
|
||||
assert {:actor, {"can not announce this object", []}} in cng.errors
|
||||
|
||||
# The actor of the object can announce it
|
||||
{:ok, announce, []} = Builder.announce(user, object, public: false)
|
||||
# The actor of the object can announce it with a restrictive scope
|
||||
{:ok, announce, []} = Builder.announce(user, object, visibility: "private")
|
||||
assert {:ok, _, _} = ObjectValidator.validate(announce, [])
|
||||
|
||||
{:ok, announce, []} = Builder.announce(user, object, visibility: "direct")
|
||||
assert {:ok, _, _} = ObjectValidator.validate(announce, [])
|
||||
|
||||
# The actor of the object can not announce it publicly
|
||||
{:ok, announce, []} = Builder.announce(user, object, public: true)
|
||||
{:ok, announce, []} = Builder.announce(user, object, visibility: "public")
|
||||
{:error, cng1} = ObjectValidator.validate(announce, [])
|
||||
|
||||
{:error, cng} = ObjectValidator.validate(announce, [])
|
||||
{:ok, announce, []} = Builder.announce(user, object, visibility: "unlisted")
|
||||
{:error, cng2} = ObjectValidator.validate(announce, [])
|
||||
|
||||
assert {:actor, {"can not announce this object publicly", []}} in cng.errors
|
||||
{:ok, announce, []} = Builder.announce(user, object, visibility: "local")
|
||||
{:error, cng3} = ObjectValidator.validate(announce, [])
|
||||
|
||||
for cng <- [cng1, cng2, cng3] do
|
||||
assert {:actor, {"can not announce this object publicly", []}} in cng.errors
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,12 +5,33 @@
|
|||
defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidatorTest do
|
||||
use Pleroma.DataCase, async: true
|
||||
|
||||
alias Pleroma.Language.LanguageDetectorMock
|
||||
alias Pleroma.StaticStubbedConfigMock
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidator
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
|
||||
import Mox
|
||||
import Pleroma.Factory
|
||||
|
||||
# Setup for all tests
|
||||
setup do
|
||||
# Stub the StaticStubbedConfigMock to return our mock for the provider
|
||||
StaticStubbedConfigMock
|
||||
|> stub(:get, fn
|
||||
[Pleroma.Language.LanguageDetector, :provider] -> LanguageDetectorMock
|
||||
_other -> nil
|
||||
end)
|
||||
|
||||
# Stub the LanguageDetectorMock with default implementations
|
||||
LanguageDetectorMock
|
||||
|> stub(:missing_dependencies, fn -> [] end)
|
||||
|> stub(:configured?, fn -> true end)
|
||||
|> stub(:detect, fn _text -> nil end)
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
describe "Notes" do
|
||||
setup do
|
||||
user = insert(:user)
|
||||
|
|
@ -43,10 +64,10 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidatorTest
|
|||
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, edit} = Pleroma.Web.CommonAPI.update(activity, user, %{status: "edited :blank:"})
|
||||
|
||||
{:ok, %{"object" => external_rep}} =
|
||||
Pleroma.Web.ActivityPub.Transmogrifier.prepare_outgoing(edit.data)
|
||||
Pleroma.Web.ActivityPub.Transmogrifier.prepare_activity(edit.data)
|
||||
|
||||
%{external_rep: external_rep}
|
||||
end
|
||||
|
|
@ -128,6 +149,17 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidatorTest
|
|||
%{valid?: true} = ArticleNotePageValidator.cast_and_validate(note)
|
||||
end
|
||||
|
||||
test "a Note with validated likes collection validates" do
|
||||
insert(:user, ap_id: "https://pol.social/users/mkljczk")
|
||||
|
||||
%{"object" => note} =
|
||||
"test/fixtures/mastodon-update-with-likes.json"
|
||||
|> File.read!()
|
||||
|> Jason.decode!()
|
||||
|
||||
%{valid?: true} = ArticleNotePageValidator.cast_and_validate(note)
|
||||
end
|
||||
|
||||
test "Fedibird quote post" do
|
||||
insert(:user, ap_id: "https://fedibird.com/users/noellabo")
|
||||
|
||||
|
|
@ -176,4 +208,102 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidatorTest
|
|||
name: "RE: https://server.example/objects/123"
|
||||
}
|
||||
end
|
||||
|
||||
describe "Note language" do
|
||||
test "it detects language from JSON-LD context" do
|
||||
user = insert(:user)
|
||||
|
||||
note_activity = %{
|
||||
"@context" => ["https://www.w3.org/ns/activitystreams", %{"@language" => "pl"}],
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"type" => "Create",
|
||||
"object" => %{
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"id" => Utils.generate_object_id(),
|
||||
"type" => "Note",
|
||||
"content" => "Szczęść Boże",
|
||||
"attributedTo" => user.ap_id
|
||||
},
|
||||
"actor" => user.ap_id
|
||||
}
|
||||
|
||||
{:ok, _create_activity, meta} = ObjectValidator.validate(note_activity, [])
|
||||
|
||||
assert meta[:object_data]["language"] == "pl"
|
||||
end
|
||||
|
||||
test "it detects language from contentMap" do
|
||||
user = insert(:user)
|
||||
|
||||
note = %{
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"id" => Utils.generate_object_id(),
|
||||
"type" => "Note",
|
||||
"content" => "Szczęść Boże",
|
||||
"contentMap" => %{
|
||||
"de" => "Gott segne",
|
||||
"pl" => "Szczęść Boże"
|
||||
},
|
||||
"attributedTo" => user.ap_id
|
||||
}
|
||||
|
||||
{:ok, object} = ArticleNotePageValidator.cast_and_apply(note)
|
||||
|
||||
assert object.language == "pl"
|
||||
end
|
||||
|
||||
test "it doesn't call LanguageDetector when language is specified" do
|
||||
# Set up expectation that detect should not be called
|
||||
LanguageDetectorMock
|
||||
|> expect(:detect, 0, fn _ -> flunk("LanguageDetector.detect should not be called") end)
|
||||
|> stub(:missing_dependencies, fn -> [] end)
|
||||
|> stub(:configured?, fn -> true end)
|
||||
|
||||
# Stub the StaticStubbedConfigMock to return our mock for the provider
|
||||
StaticStubbedConfigMock
|
||||
|> stub(:get, fn
|
||||
[Pleroma.Language.LanguageDetector, :provider] -> LanguageDetectorMock
|
||||
_other -> nil
|
||||
end)
|
||||
|
||||
user = insert(:user)
|
||||
|
||||
note = %{
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"id" => Utils.generate_object_id(),
|
||||
"type" => "Note",
|
||||
"content" => "a post in English",
|
||||
"contentMap" => %{
|
||||
"en" => "a post in English"
|
||||
},
|
||||
"attributedTo" => user.ap_id
|
||||
}
|
||||
|
||||
ArticleNotePageValidator.cast_and_apply(note)
|
||||
end
|
||||
|
||||
test "it adds contentMap if language is specified" do
|
||||
user = insert(:user)
|
||||
|
||||
note = %{
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"id" => Utils.generate_object_id(),
|
||||
"type" => "Note",
|
||||
"content" => "тест",
|
||||
"language" => "uk",
|
||||
"attributedTo" => user.ap_id
|
||||
}
|
||||
|
||||
{:ok, object} = ArticleNotePageValidator.cast_and_apply(note)
|
||||
|
||||
assert object.contentMap == %{
|
||||
"uk" => "тест"
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -13,6 +13,23 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidatorTest do
|
|||
import Pleroma.Factory
|
||||
|
||||
describe "attachments" do
|
||||
test "works with apng" do
|
||||
attachment =
|
||||
%{
|
||||
"mediaType" => "image/apng",
|
||||
"name" => "",
|
||||
"type" => "Document",
|
||||
"url" =>
|
||||
"https://media.misskeyusercontent.com/io/2859c26e-cd43-4550-848b-b6243bc3fe28.apng"
|
||||
}
|
||||
|
||||
assert {:ok, attachment} =
|
||||
AttachmentValidator.cast_and_validate(attachment)
|
||||
|> Ecto.Changeset.apply_action(:insert)
|
||||
|
||||
assert attachment.mediaType == "image/apng"
|
||||
end
|
||||
|
||||
test "fails without url" do
|
||||
attachment = %{
|
||||
"mediaType" => "",
|
||||
|
|
|
|||
|
|
@ -59,4 +59,37 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateGenericValidatorTest do
|
|||
assert validated.valid?
|
||||
assert {:context, note["context"]} in validated.changes
|
||||
end
|
||||
|
||||
test "a Create/Note without addressing falls back to the Note's recipients" do
|
||||
user = insert(:user)
|
||||
|
||||
note = %{
|
||||
"id" => Utils.generate_object_id(),
|
||||
"type" => "Note",
|
||||
"actor" => user.ap_id,
|
||||
"to" => [user.follower_address],
|
||||
"cc" => [],
|
||||
"content" => "Hello world",
|
||||
"context" => Utils.generate_context_id()
|
||||
}
|
||||
|
||||
note_activity = %{
|
||||
"id" => Utils.generate_activity_id(),
|
||||
"type" => "Create",
|
||||
"actor" => note["actor"],
|
||||
"object" => note,
|
||||
"published" => DateTime.utc_now() |> DateTime.to_iso8601(),
|
||||
"context" => Utils.generate_context_id()
|
||||
}
|
||||
|
||||
# Build metadata
|
||||
{:ok, object_data} = ObjectValidator.cast_and_apply(note_activity["object"])
|
||||
meta = [object_data: ObjectValidator.stringify_keys(object_data)]
|
||||
|
||||
validated = CreateGenericValidator.cast_and_validate(note_activity, meta)
|
||||
|
||||
assert validated.valid?
|
||||
assert Ecto.Changeset.get_field(validated, :to) == note["to"]
|
||||
assert Ecto.Changeset.get_field(validated, :cc) == note["cc"]
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -22,5 +22,15 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.FollowValidationTest do
|
|||
test "validates a basic follow object", %{valid_follow: valid_follow} do
|
||||
assert {:ok, _follow, []} = ObjectValidator.validate(valid_follow, [])
|
||||
end
|
||||
|
||||
test "supports a nil cc", %{valid_follow: valid_follow} do
|
||||
valid_follow_with_nil_cc = Map.put(valid_follow, "cc", nil)
|
||||
assert {:ok, _follow, []} = ObjectValidator.validate(valid_follow_with_nil_cc, [])
|
||||
end
|
||||
|
||||
test "supports an empty cc", %{valid_follow: valid_follow} do
|
||||
valid_follow_with_empty_cc = Map.put(valid_follow, "cc", [])
|
||||
assert {:ok, _follow, []} = ObjectValidator.validate(valid_follow_with_empty_cc, [])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.LikeValidationTest do
|
|||
user: user,
|
||||
post_activity: post_activity
|
||||
} do
|
||||
_like = CommonAPI.favorite(user, post_activity.id)
|
||||
_like = CommonAPI.favorite(post_activity.id, user)
|
||||
|
||||
refute LikeValidator.cast_and_validate(valid_like).valid?
|
||||
end
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.UndoHandlingTest do
|
|||
setup do
|
||||
user = insert(:user)
|
||||
{:ok, post_activity} = CommonAPI.post(user, %{status: "uguu"})
|
||||
{:ok, like} = CommonAPI.favorite(user, post_activity.id)
|
||||
{:ok, like} = CommonAPI.favorite(post_activity.id, user)
|
||||
{:ok, valid_like_undo, []} = Builder.undo(user, like)
|
||||
|
||||
%{user: user, like: like, valid_like_undo: valid_like_undo}
|
||||
|
|
|
|||
|
|
@ -132,8 +132,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.UpdateHandlingTest 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)
|
||||
{:ok, edit} = Pleroma.Web.CommonAPI.update(activity, user, %{status: "edited :blank:"})
|
||||
{:ok, external_rep} = Pleroma.Web.ActivityPub.Transmogrifier.prepare_activity(edit.data)
|
||||
%{external_rep: external_rep}
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -3,17 +3,18 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.PublisherTest do
|
||||
use Oban.Testing, repo: Pleroma.Repo
|
||||
use Pleroma.Web.ConnCase
|
||||
|
||||
import ExUnit.CaptureLog
|
||||
import Pleroma.Factory
|
||||
import Tesla.Mock
|
||||
import Mock
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Instances
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Tests.ObanHelpers
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.ActivityPub.Publisher
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
alias Pleroma.Web.CommonAPI
|
||||
|
||||
@as_public "https://www.w3.org/ns/activitystreams#Public"
|
||||
|
|
@ -137,6 +138,7 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do
|
|||
test "publish to url with with different ports" do
|
||||
inbox80 = "http://42.site/users/nick1/inbox"
|
||||
inbox42 = "http://42.site:42/users/nick1/inbox"
|
||||
activity = insert(:note_activity)
|
||||
|
||||
mock(fn
|
||||
%{method: :post, url: "http://42.site:42/users/nick1/inbox"} ->
|
||||
|
|
@ -146,143 +148,28 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do
|
|||
{:ok, %Tesla.Env{status: 200, body: "port 80"}}
|
||||
end)
|
||||
|
||||
actor = insert(:user)
|
||||
_actor = insert(:user)
|
||||
|
||||
assert {:ok, %{body: "port 42"}} =
|
||||
Publisher.publish_one(%{
|
||||
Publisher.prepare_one(%{
|
||||
inbox: inbox42,
|
||||
json: "{}",
|
||||
actor: actor,
|
||||
id: 1,
|
||||
activity_id: activity.id,
|
||||
unreachable_since: true
|
||||
})
|
||||
|> Publisher.publish_one()
|
||||
|
||||
assert {:ok, %{body: "port 80"}} =
|
||||
Publisher.publish_one(%{
|
||||
Publisher.prepare_one(%{
|
||||
inbox: inbox80,
|
||||
json: "{}",
|
||||
actor: actor,
|
||||
id: 1,
|
||||
activity_id: activity.id,
|
||||
unreachable_since: true
|
||||
})
|
||||
end
|
||||
|
||||
test_with_mock "calls `Instances.set_reachable` on successful federation if `unreachable_since` is not specified",
|
||||
Instances,
|
||||
[:passthrough],
|
||||
[] do
|
||||
actor = insert(:user)
|
||||
inbox = "http://200.site/users/nick1/inbox"
|
||||
|
||||
assert {:ok, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
|
||||
assert called(Instances.set_reachable(inbox))
|
||||
end
|
||||
|
||||
test_with_mock "calls `Instances.set_reachable` on successful federation if `unreachable_since` is set",
|
||||
Instances,
|
||||
[:passthrough],
|
||||
[] do
|
||||
actor = insert(:user)
|
||||
inbox = "http://200.site/users/nick1/inbox"
|
||||
|
||||
assert {:ok, _} =
|
||||
Publisher.publish_one(%{
|
||||
inbox: inbox,
|
||||
json: "{}",
|
||||
actor: actor,
|
||||
id: 1,
|
||||
unreachable_since: NaiveDateTime.utc_now()
|
||||
})
|
||||
|
||||
assert called(Instances.set_reachable(inbox))
|
||||
end
|
||||
|
||||
test_with_mock "does NOT call `Instances.set_reachable` on successful federation if `unreachable_since` is nil",
|
||||
Instances,
|
||||
[:passthrough],
|
||||
[] do
|
||||
actor = insert(:user)
|
||||
inbox = "http://200.site/users/nick1/inbox"
|
||||
|
||||
assert {:ok, _} =
|
||||
Publisher.publish_one(%{
|
||||
inbox: inbox,
|
||||
json: "{}",
|
||||
actor: actor,
|
||||
id: 1,
|
||||
unreachable_since: nil
|
||||
})
|
||||
|
||||
refute called(Instances.set_reachable(inbox))
|
||||
end
|
||||
|
||||
test_with_mock "calls `Instances.set_unreachable` on target inbox on non-2xx HTTP response code",
|
||||
Instances,
|
||||
[:passthrough],
|
||||
[] do
|
||||
actor = insert(:user)
|
||||
inbox = "http://404.site/users/nick1/inbox"
|
||||
|
||||
assert {:discard, _} =
|
||||
Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
|
||||
|
||||
assert called(Instances.set_unreachable(inbox))
|
||||
end
|
||||
|
||||
test_with_mock "it calls `Instances.set_unreachable` on target inbox on request error of any kind",
|
||||
Instances,
|
||||
[:passthrough],
|
||||
[] do
|
||||
actor = insert(:user)
|
||||
inbox = "http://connrefused.site/users/nick1/inbox"
|
||||
|
||||
assert capture_log(fn ->
|
||||
assert {:error, _} =
|
||||
Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
|
||||
end) =~ "connrefused"
|
||||
|
||||
assert called(Instances.set_unreachable(inbox))
|
||||
end
|
||||
|
||||
test_with_mock "does NOT call `Instances.set_unreachable` if target is reachable",
|
||||
Instances,
|
||||
[:passthrough],
|
||||
[] do
|
||||
actor = insert(:user)
|
||||
inbox = "http://200.site/users/nick1/inbox"
|
||||
|
||||
assert {:ok, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
|
||||
|
||||
refute called(Instances.set_unreachable(inbox))
|
||||
end
|
||||
|
||||
test_with_mock "does NOT call `Instances.set_unreachable` if target instance has non-nil `unreachable_since`",
|
||||
Instances,
|
||||
[:passthrough],
|
||||
[] do
|
||||
actor = insert(:user)
|
||||
inbox = "http://connrefused.site/users/nick1/inbox"
|
||||
|
||||
assert capture_log(fn ->
|
||||
assert {:error, _} =
|
||||
Publisher.publish_one(%{
|
||||
inbox: inbox,
|
||||
json: "{}",
|
||||
actor: actor,
|
||||
id: 1,
|
||||
unreachable_since: NaiveDateTime.utc_now()
|
||||
})
|
||||
end) =~ "connrefused"
|
||||
|
||||
refute called(Instances.set_unreachable(inbox))
|
||||
|> Publisher.publish_one()
|
||||
end
|
||||
end
|
||||
|
||||
describe "publish/2" do
|
||||
test_with_mock "doesn't publish a non-public activity to quarantined instances.",
|
||||
Pleroma.Web.ActivityPub.Publisher,
|
||||
[:passthrough],
|
||||
[] do
|
||||
test "doesn't publish a non-public activity to quarantined instances." do
|
||||
Config.put([:instance, :quarantined_instances], [{"domain.com", "some reason"}])
|
||||
|
||||
follower =
|
||||
|
|
@ -306,19 +193,18 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do
|
|||
|
||||
assert res == :ok
|
||||
|
||||
assert not called(
|
||||
Publisher.enqueue_one(%{
|
||||
inbox: "https://domain.com/users/nick1/inbox",
|
||||
actor_id: actor.id,
|
||||
id: note_activity.data["id"]
|
||||
})
|
||||
)
|
||||
refute_enqueued(
|
||||
worker: "Pleroma.Workers.PublisherWorker",
|
||||
args: %{
|
||||
"params" => %{
|
||||
inbox: "https://domain.com/users/nick1/inbox",
|
||||
activity_id: note_activity.id
|
||||
}
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
test_with_mock "Publishes a non-public activity to non-quarantined instances.",
|
||||
Pleroma.Web.ActivityPub.Publisher,
|
||||
[:passthrough],
|
||||
[] do
|
||||
test "Publishes a non-public activity to non-quarantined instances." do
|
||||
Config.put([:instance, :quarantined_instances], [{"somedomain.com", "some reason"}])
|
||||
|
||||
follower =
|
||||
|
|
@ -342,22 +228,19 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do
|
|||
|
||||
assert res == :ok
|
||||
|
||||
assert called(
|
||||
Publisher.enqueue_one(
|
||||
%{
|
||||
inbox: "https://domain.com/users/nick1/inbox",
|
||||
actor_id: actor.id,
|
||||
id: note_activity.data["id"]
|
||||
},
|
||||
priority: 1
|
||||
)
|
||||
)
|
||||
assert_enqueued(
|
||||
worker: "Pleroma.Workers.PublisherWorker",
|
||||
args: %{
|
||||
"params" => %{
|
||||
inbox: "https://domain.com/users/nick1/inbox",
|
||||
activity_id: note_activity.id
|
||||
}
|
||||
},
|
||||
priority: 1
|
||||
)
|
||||
end
|
||||
|
||||
test_with_mock "Publishes to directly addressed actors with higher priority.",
|
||||
Pleroma.Web.ActivityPub.Publisher,
|
||||
[:passthrough],
|
||||
[] do
|
||||
test "Publishes to directly addressed actors with higher priority." do
|
||||
note_activity = insert(:direct_note_activity)
|
||||
|
||||
actor = Pleroma.User.get_by_ap_id(note_activity.data["actor"])
|
||||
|
|
@ -366,22 +249,58 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do
|
|||
|
||||
assert res == :ok
|
||||
|
||||
assert called(
|
||||
Publisher.enqueue_one(
|
||||
%{
|
||||
inbox: :_,
|
||||
actor_id: actor.id,
|
||||
id: note_activity.data["id"]
|
||||
},
|
||||
priority: 0
|
||||
)
|
||||
)
|
||||
assert_enqueued(
|
||||
worker: "Pleroma.Workers.PublisherWorker",
|
||||
args: %{
|
||||
"op" => "publish_one",
|
||||
"params" => %{"activity_id" => note_activity.id}
|
||||
},
|
||||
priority: 0
|
||||
)
|
||||
end
|
||||
|
||||
test_with_mock "publishes an activity with BCC to all relevant peers.",
|
||||
Pleroma.Web.ActivityPub.Publisher,
|
||||
[:passthrough],
|
||||
[] do
|
||||
test "Publishes with the new actor if prepare_activity changes the actor." do
|
||||
mock(fn
|
||||
%{method: :post, url: "https://domain.com/users/nick1/inbox", body: body} ->
|
||||
{:ok, %Tesla.Env{status: 200, body: body}}
|
||||
end)
|
||||
|
||||
other_user =
|
||||
insert(:user, %{
|
||||
local: false,
|
||||
inbox: "https://domain.com/users/nick1/inbox"
|
||||
})
|
||||
|
||||
actor = insert(:user)
|
||||
replaced_actor = insert(:user)
|
||||
|
||||
note_activity =
|
||||
insert(:note_activity,
|
||||
user: actor,
|
||||
data_attrs: %{"to" => [other_user.ap_id]}
|
||||
)
|
||||
|
||||
Pleroma.Web.ActivityPub.TransmogrifierMock
|
||||
|> Mox.expect(:prepare_activity, fn data ->
|
||||
{:ok, Map.put(data, "actor", replaced_actor.ap_id)}
|
||||
end)
|
||||
|
||||
prepared =
|
||||
Publisher.prepare_one(%{
|
||||
inbox: "https://domain.com/users/nick1/inbox",
|
||||
activity_id: note_activity.id,
|
||||
cc: ["https://domain.com/users/nick2/inbox"]
|
||||
})
|
||||
|
||||
{:ok, decoded} = Jason.decode(prepared.json)
|
||||
assert decoded["actor"] == replaced_actor.ap_id
|
||||
|
||||
{:ok, published} = Publisher.publish_one(prepared)
|
||||
sent_activity = Jason.decode!(published.body)
|
||||
assert sent_activity["actor"] == replaced_actor.ap_id
|
||||
end
|
||||
|
||||
test "publishes an activity with BCC to all relevant peers." do
|
||||
follower =
|
||||
insert(:user, %{
|
||||
local: false,
|
||||
|
|
@ -402,19 +321,45 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do
|
|||
res = Publisher.publish(actor, note_activity)
|
||||
assert res == :ok
|
||||
|
||||
assert called(
|
||||
Publisher.enqueue_one(%{
|
||||
inbox: "https://domain.com/users/nick1/inbox",
|
||||
actor_id: actor.id,
|
||||
id: note_activity.data["id"]
|
||||
})
|
||||
)
|
||||
assert_enqueued(
|
||||
worker: "Pleroma.Workers.PublisherWorker",
|
||||
args: %{
|
||||
"params" => %{
|
||||
inbox: "https://domain.com/users/nick1/inbox",
|
||||
activity_id: note_activity.id
|
||||
}
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
test_with_mock "publishes a delete activity to peers who signed fetch requests to the create acitvity/object.",
|
||||
Pleroma.Web.ActivityPub.Publisher,
|
||||
[:passthrough],
|
||||
[] do
|
||||
test "activity with BCC is published to a list member." do
|
||||
actor = insert(:user)
|
||||
{:ok, list} = Pleroma.List.create(%{title: "list"}, actor)
|
||||
list_member = insert(:user, %{local: false})
|
||||
|
||||
Pleroma.List.follow(list, list_member)
|
||||
|
||||
note_activity =
|
||||
insert(:note_activity,
|
||||
# recipients: [follower.ap_id],
|
||||
data_attrs: %{"bcc" => [list.ap_id]}
|
||||
)
|
||||
|
||||
res = Publisher.publish(actor, note_activity)
|
||||
assert res == :ok
|
||||
|
||||
assert_enqueued(
|
||||
worker: "Pleroma.Workers.PublisherWorker",
|
||||
args: %{
|
||||
"params" => %{
|
||||
inbox: list_member.inbox,
|
||||
activity_id: note_activity.id
|
||||
}
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
test "publishes a delete activity to peers who signed fetch requests to the create acitvity/object." do
|
||||
fetcher =
|
||||
insert(:user,
|
||||
local: false,
|
||||
|
|
@ -452,27 +397,209 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do
|
|||
res = Publisher.publish(actor, delete)
|
||||
assert res == :ok
|
||||
|
||||
assert called(
|
||||
Publisher.enqueue_one(
|
||||
%{
|
||||
inbox: "https://domain.com/users/nick1/inbox",
|
||||
actor_id: actor.id,
|
||||
id: delete.data["id"]
|
||||
},
|
||||
priority: 1
|
||||
)
|
||||
)
|
||||
assert_enqueued(
|
||||
worker: "Pleroma.Workers.PublisherWorker",
|
||||
args: %{
|
||||
"params" => %{
|
||||
inbox: "https://domain.com/users/nick1/inbox",
|
||||
activity_id: delete.id
|
||||
}
|
||||
},
|
||||
priority: 1
|
||||
)
|
||||
|
||||
assert called(
|
||||
Publisher.enqueue_one(
|
||||
%{
|
||||
inbox: "https://domain2.com/users/nick1/inbox",
|
||||
actor_id: actor.id,
|
||||
id: delete.data["id"]
|
||||
},
|
||||
priority: 1
|
||||
)
|
||||
)
|
||||
assert_enqueued(
|
||||
worker: "Pleroma.Workers.PublisherWorker",
|
||||
args: %{
|
||||
"params" => %{
|
||||
inbox: "https://domain2.com/users/nick1/inbox",
|
||||
activity_id: delete.id
|
||||
}
|
||||
},
|
||||
priority: 1
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
test "cc in prepared json for a follow request is an empty list" do
|
||||
user = insert(:user)
|
||||
remote_user = insert(:user, local: false)
|
||||
|
||||
{:ok, _, _, activity} = CommonAPI.follow(remote_user, user)
|
||||
|
||||
assert_enqueued(
|
||||
worker: "Pleroma.Workers.PublisherWorker",
|
||||
args: %{
|
||||
"activity_id" => activity.id,
|
||||
"op" => "publish"
|
||||
}
|
||||
)
|
||||
|
||||
ObanHelpers.perform_all()
|
||||
|
||||
expected_params =
|
||||
%{
|
||||
"activity_id" => activity.id,
|
||||
"inbox" => remote_user.inbox,
|
||||
"unreachable_since" => nil
|
||||
}
|
||||
|
||||
assert_enqueued(
|
||||
worker: "Pleroma.Workers.PublisherWorker",
|
||||
args: %{
|
||||
"op" => "publish_one",
|
||||
"params" => expected_params
|
||||
}
|
||||
)
|
||||
|
||||
# params need to be atom keys for Publisher.prepare_one.
|
||||
# this is done in the Oban job.
|
||||
expected_params = Map.new(expected_params, fn {k, v} -> {String.to_atom(k), v} end)
|
||||
|
||||
%{json: json} = Publisher.prepare_one(expected_params)
|
||||
|
||||
{:ok, decoded} = Jason.decode(json)
|
||||
|
||||
assert decoded["cc"] == []
|
||||
end
|
||||
|
||||
test "unlisted activities retain public address in cc" do
|
||||
user = insert(:user)
|
||||
|
||||
# simulate unlistd activity by only having
|
||||
# public address in cc
|
||||
activity =
|
||||
insert(:note_activity,
|
||||
user: user,
|
||||
data_attrs: %{
|
||||
"cc" => [@as_public],
|
||||
"to" => [user.follower_address]
|
||||
}
|
||||
)
|
||||
|
||||
assert @as_public in activity.data["cc"]
|
||||
|
||||
prepared =
|
||||
Publisher.prepare_one(%{
|
||||
inbox: "https://remote.instance/users/someone/inbox",
|
||||
activity_id: activity.id
|
||||
})
|
||||
|
||||
{:ok, decoded} = Jason.decode(prepared.json)
|
||||
|
||||
assert @as_public in decoded["cc"]
|
||||
|
||||
# maybe we also have another inbox in cc
|
||||
# during Publishing
|
||||
activity =
|
||||
insert(:note_activity,
|
||||
user: user,
|
||||
data_attrs: %{
|
||||
"cc" => [@as_public],
|
||||
"to" => [user.follower_address]
|
||||
}
|
||||
)
|
||||
|
||||
prepared =
|
||||
Publisher.prepare_one(%{
|
||||
inbox: "https://remote.instance/users/someone/inbox",
|
||||
activity_id: activity.id,
|
||||
cc: ["https://remote.instance/users/someone_else/inbox"]
|
||||
})
|
||||
|
||||
{:ok, decoded} = Jason.decode(prepared.json)
|
||||
|
||||
assert decoded["cc"] == [@as_public, "https://remote.instance/users/someone_else/inbox"]
|
||||
end
|
||||
|
||||
test "public address in cc parameter is preserved" do
|
||||
user = insert(:user)
|
||||
|
||||
cc_with_public = [@as_public, "https://example.org/users/other"]
|
||||
|
||||
activity =
|
||||
insert(:note_activity,
|
||||
user: user,
|
||||
data_attrs: %{
|
||||
"cc" => cc_with_public,
|
||||
"to" => [user.follower_address]
|
||||
}
|
||||
)
|
||||
|
||||
assert @as_public in activity.data["cc"]
|
||||
|
||||
prepared =
|
||||
Publisher.prepare_one(%{
|
||||
inbox: "https://remote.instance/users/someone/inbox",
|
||||
activity_id: activity.id,
|
||||
cc: cc_with_public
|
||||
})
|
||||
|
||||
{:ok, decoded} = Jason.decode(prepared.json)
|
||||
|
||||
assert cc_with_public == decoded["cc"]
|
||||
end
|
||||
|
||||
test "cc parameter is preserved" do
|
||||
user = insert(:user)
|
||||
|
||||
activity =
|
||||
insert(:note_activity,
|
||||
user: user,
|
||||
data_attrs: %{
|
||||
"cc" => ["https://example.com/specific/user"],
|
||||
"to" => [user.follower_address]
|
||||
}
|
||||
)
|
||||
|
||||
prepared =
|
||||
Publisher.prepare_one(%{
|
||||
inbox: "https://remote.instance/users/someone/inbox",
|
||||
activity_id: activity.id,
|
||||
cc: ["https://example.com/specific/user"]
|
||||
})
|
||||
|
||||
{:ok, decoded} = Jason.decode(prepared.json)
|
||||
|
||||
assert decoded["cc"] == ["https://example.com/specific/user"]
|
||||
end
|
||||
|
||||
describe "prepare_one/1 with reporter anonymization" do
|
||||
test "signs with the anonymized actor keys when Transmogrifier changes actor" do
|
||||
Pleroma.SignatureMock
|
||||
|> Mox.stub(:signed_date, fn -> Pleroma.Signature.signed_date() end)
|
||||
|> Mox.expect(:sign, fn %Pleroma.User{} = user, _headers ->
|
||||
send(self(), {:signed_as, user.ap_id})
|
||||
"TESTSIG"
|
||||
end)
|
||||
|
||||
placeholder = insert(:user)
|
||||
reporter = insert(:user)
|
||||
target_account = insert(:user)
|
||||
|
||||
clear_config([:activitypub, :anonymize_reporter], true)
|
||||
clear_config([:activitypub, :anonymize_reporter_local_nickname], placeholder.nickname)
|
||||
|
||||
{:ok, reported} = CommonAPI.post(target_account, %{status: "content"})
|
||||
context = Utils.generate_context_id()
|
||||
|
||||
{:ok, activity} =
|
||||
ActivityPub.flag(%{
|
||||
actor: reporter,
|
||||
context: context,
|
||||
account: target_account,
|
||||
statuses: [reported],
|
||||
content: "reason"
|
||||
})
|
||||
|
||||
_prepared =
|
||||
Publisher.prepare_one(%{
|
||||
inbox: "http://remote.example/users/alice/inbox",
|
||||
activity_id: activity.id
|
||||
})
|
||||
|
||||
assert_received {:signed_as, ap_id}
|
||||
assert ap_id == placeholder.ap_id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ defmodule Pleroma.Web.ActivityPub.RelayTest do
|
|||
test "returns activity" do
|
||||
user = insert(:user)
|
||||
service_actor = Relay.get_actor()
|
||||
CommonAPI.follow(service_actor, user)
|
||||
CommonAPI.follow(user, service_actor)
|
||||
assert "#{user.ap_id}/followers" in User.following(service_actor)
|
||||
assert {:ok, %Activity{} = activity} = Relay.unfollow(user.ap_id)
|
||||
assert activity.actor == "#{Pleroma.Web.Endpoint.url()}/relay"
|
||||
|
|
@ -74,7 +74,7 @@ defmodule Pleroma.Web.ActivityPub.RelayTest do
|
|||
end)
|
||||
|
||||
service_actor = Relay.get_actor()
|
||||
CommonAPI.follow(service_actor, user)
|
||||
CommonAPI.follow(user, service_actor)
|
||||
assert "#{user.ap_id}/followers" in User.following(service_actor)
|
||||
|
||||
assert Pleroma.Repo.get_by(
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects.DeleteTest do
|
|||
|
||||
{:ok, op} = CommonAPI.post(other_user, %{status: "big oof"})
|
||||
{:ok, post} = CommonAPI.post(user, %{status: "hey", in_reply_to_id: op})
|
||||
{:ok, favorite} = CommonAPI.favorite(user, post.id)
|
||||
{:ok, favorite} = CommonAPI.favorite(post.id, user)
|
||||
object = Object.normalize(post, fetch: false)
|
||||
{:ok, delete_data, _meta} = Builder.delete(user, object.data["id"])
|
||||
{:ok, delete, _meta} = ActivityPub.persist(delete_data, local: true)
|
||||
|
|
|
|||
|
|
@ -54,20 +54,17 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do
|
|||
[
|
||||
stream: fn _, _ -> nil end
|
||||
]
|
||||
},
|
||||
{
|
||||
Pleroma.Web.Push,
|
||||
[],
|
||||
[
|
||||
send: fn _ -> nil end
|
||||
]
|
||||
}
|
||||
]) do
|
||||
SideEffects.handle_after_transaction(meta)
|
||||
|
||||
assert called(Pleroma.Web.Streamer.stream(["user", "user:notification"], notification))
|
||||
assert called(Pleroma.Web.Streamer.stream(["user", "user:pleroma_chat"], :_))
|
||||
assert called(Pleroma.Web.Push.send(notification))
|
||||
|
||||
assert_enqueued(
|
||||
worker: "Pleroma.Workers.WebPusherWorker",
|
||||
args: %{"notification_id" => notification.id, "op" => "web_push"}
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -516,10 +513,10 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do
|
|||
poster = insert(:user)
|
||||
user = insert(:user)
|
||||
{:ok, post} = CommonAPI.post(poster, %{status: "hey"})
|
||||
{:ok, like} = CommonAPI.favorite(user, post.id)
|
||||
{:ok, like} = CommonAPI.favorite(post.id, user)
|
||||
{:ok, reaction} = CommonAPI.react_with_emoji(post.id, user, "👍")
|
||||
{:ok, announce} = CommonAPI.repeat(post.id, user)
|
||||
{:ok, block} = CommonAPI.block(user, poster)
|
||||
{:ok, block} = CommonAPI.block(poster, user)
|
||||
|
||||
{:ok, undo_data, _meta} = Builder.undo(user, like)
|
||||
{:ok, like_undo, _meta} = ActivityPub.persist(undo_data, local: true)
|
||||
|
|
@ -787,13 +784,15 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do
|
|||
{:ok, post} = CommonAPI.post(poster, %{status: "hey"})
|
||||
{:ok, private_post} = CommonAPI.post(poster, %{status: "hey", visibility: "private"})
|
||||
|
||||
{:ok, announce_data, _meta} = Builder.announce(user, post.object, public: true)
|
||||
{:ok, announce_data, _meta} = Builder.announce(user, post.object, visibility: "public")
|
||||
|
||||
{:ok, private_announce_data, _meta} =
|
||||
Builder.announce(user, private_post.object, public: false)
|
||||
Builder.announce(user, private_post.object, visibility: "private")
|
||||
|
||||
{:ok, relay_announce_data, _meta} =
|
||||
Builder.announce(Pleroma.Web.ActivityPub.Relay.get_actor(), post.object, public: true)
|
||||
Builder.announce(Pleroma.Web.ActivityPub.Relay.get_actor(), post.object,
|
||||
visibility: "public"
|
||||
)
|
||||
|
||||
{:ok, announce, _meta} = ActivityPub.persist(announce_data, local: true)
|
||||
{:ok, private_announce, _meta} = ActivityPub.persist(private_announce_data, local: true)
|
||||
|
|
@ -834,7 +833,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do
|
|||
user = insert(:user)
|
||||
followed = insert(:user)
|
||||
|
||||
{:ok, _, _, follow_activity} = CommonAPI.follow(user, followed)
|
||||
{:ok, _, _, follow_activity} = CommonAPI.follow(followed, user)
|
||||
|
||||
{:ok, reject_data, []} = Builder.reject(followed, follow_activity)
|
||||
{:ok, reject, _meta} = ActivityPub.persist(reject_data, local: true)
|
||||
|
|
@ -965,7 +964,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do
|
|||
group: group,
|
||||
poster: poster
|
||||
} do
|
||||
{:ok, _} = CommonAPI.block(group, poster)
|
||||
{:ok, _} = CommonAPI.block(poster, group)
|
||||
create_activity_data = make_create.([group])
|
||||
{:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false)
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.AcceptHandlingTest do
|
|||
{:ok, follower, followed} = User.follow(follower, followed)
|
||||
assert User.following?(follower, followed) == true
|
||||
|
||||
{:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed)
|
||||
{:ok, _, _, follow_activity} = CommonAPI.follow(followed, follower)
|
||||
|
||||
accept_data =
|
||||
File.read!("test/fixtures/mastodon-accept-activity.json")
|
||||
|
|
@ -48,7 +48,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.AcceptHandlingTest do
|
|||
follower = insert(:user)
|
||||
followed = insert(:user, is_locked: true)
|
||||
|
||||
{:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed)
|
||||
{:ok, _, _, follow_activity} = CommonAPI.follow(followed, follower)
|
||||
|
||||
accept_data =
|
||||
File.read!("test/fixtures/mastodon-accept-activity.json")
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.AnswerHandlingTest do
|
|||
|> Kernel.put_in(["object", "to"], user.ap_id)
|
||||
|
||||
{:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data)
|
||||
{:ok, data} = Transmogrifier.prepare_outgoing(activity.data)
|
||||
{:ok, data} = Transmogrifier.prepare_activity(activity.data)
|
||||
|
||||
assert data["object"]["type"] == "Note"
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,14 +1,46 @@
|
|||
defmodule Pleroma.Web.ActivityPub.Transmogrifier.EmojiTagBuildingTest do
|
||||
use Pleroma.DataCase, async: true
|
||||
|
||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||
|
||||
test "it encodes the id to be a valid url" do
|
||||
name = "hanapog"
|
||||
url = "https://misskey.local.live/emojis/hana pog.png"
|
||||
|
||||
tag = Transmogrifier.build_emoji_tag({name, url})
|
||||
tag = Pleroma.Emoji.build_emoji_tag({name, url})
|
||||
|
||||
assert tag["id"] == "https://misskey.local.live/emojis/hana%20pog.png"
|
||||
end
|
||||
|
||||
test "it does not double-encode already encoded urls" do
|
||||
name = "hanapog"
|
||||
url = "https://misskey.local.live/emojis/hana%20pog.png"
|
||||
|
||||
tag = Pleroma.Emoji.build_emoji_tag({name, url})
|
||||
|
||||
assert tag["id"] == url
|
||||
end
|
||||
|
||||
test "it encodes disallowed path characters" do
|
||||
name = "hanapog"
|
||||
url = "https://example.com/emojis/hana[pog].png"
|
||||
|
||||
tag = Pleroma.Emoji.build_emoji_tag({name, url})
|
||||
|
||||
assert tag["id"] == "https://example.com/emojis/hana%5Bpog%5D.png"
|
||||
end
|
||||
|
||||
test "local_url does not decode percent in filenames" do
|
||||
url = Pleroma.Emoji.local_url("/emoji/hana%20pog.png")
|
||||
|
||||
assert url == Pleroma.Web.Endpoint.url() <> "/emoji/hana%2520pog.png"
|
||||
|
||||
tag = Pleroma.Emoji.build_emoji_tag({"hanapog", url})
|
||||
|
||||
assert tag["id"] == url
|
||||
end
|
||||
|
||||
test "local_url encodes question marks in filenames" do
|
||||
url = Pleroma.Emoji.local_url("/emoji/file?name.png")
|
||||
|
||||
assert url == Pleroma.Web.Endpoint.url() <> "/emoji/file%3Fname.png"
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.LikeHandlingTest do
|
|||
use Pleroma.DataCase, async: true
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||
alias Pleroma.Web.CommonAPI
|
||||
|
||||
|
|
@ -75,4 +76,107 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.LikeHandlingTest do
|
|||
assert activity_data["object"] == activity.data["object"]
|
||||
assert activity_data["content"] == "⭐"
|
||||
end
|
||||
|
||||
test "it works for misskey likes with custom emoji" do
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "hello"})
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/misskey-custom-emoji-like.json")
|
||||
|> Jason.decode!()
|
||||
|> Map.put("object", activity.data["object"])
|
||||
|
||||
_actor = insert(:user, ap_id: data["actor"], local: false)
|
||||
|
||||
{:ok, %Activity{data: activity_data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||
|
||||
assert activity_data["actor"] == data["actor"]
|
||||
assert activity_data["type"] == "EmojiReact"
|
||||
assert activity_data["id"] == data["id"]
|
||||
assert activity_data["object"] == activity.data["object"]
|
||||
assert activity_data["content"] == ":blobwtfnotlikethis:"
|
||||
|
||||
assert [["blobwtfnotlikethis", _, _]] =
|
||||
Object.get_by_ap_id(activity.data["object"])
|
||||
|> Object.get_emoji_reactions()
|
||||
end
|
||||
|
||||
test "it works for mitra likes with custom emoji" do
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "hello"})
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/mitra-custom-emoji-like.json")
|
||||
|> Jason.decode!()
|
||||
|> Map.put("object", activity.data["object"])
|
||||
|
||||
_actor = insert(:user, ap_id: data["actor"], local: false)
|
||||
|
||||
{:ok, %Activity{data: activity_data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||
|
||||
assert activity_data["actor"] == data["actor"]
|
||||
assert activity_data["type"] == "EmojiReact"
|
||||
assert activity_data["id"] == data["id"]
|
||||
assert activity_data["object"] == activity.data["object"]
|
||||
assert activity_data["content"] == ":ablobcatheartsqueeze:"
|
||||
|
||||
assert [["ablobcatheartsqueeze", _, _]] =
|
||||
Object.get_by_ap_id(activity.data["object"])
|
||||
|> Object.get_emoji_reactions()
|
||||
end
|
||||
|
||||
test "it works for likes with wrong content" do
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "hello"})
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/mitra-custom-emoji-like.json")
|
||||
|> Jason.decode!()
|
||||
|> Map.put("object", activity.data["object"])
|
||||
|> Map.put("content", 1)
|
||||
|
||||
_actor = insert(:user, ap_id: data["actor"], local: false)
|
||||
|
||||
assert {:ok, activity} = Transmogrifier.handle_incoming(data)
|
||||
assert activity.data["type"] == "Like"
|
||||
end
|
||||
|
||||
test "it changes incoming dislikes into emoji reactions" do
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "hello"})
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/friendica-dislike.json")
|
||||
|> Jason.decode!()
|
||||
|> Map.put("object", activity.data["object"])
|
||||
|
||||
_actor = insert(:user, ap_id: data["actor"], local: false)
|
||||
|
||||
{:ok, %Activity{data: data, local: false} = activity} = Transmogrifier.handle_incoming(data)
|
||||
|
||||
refute Enum.empty?(activity.recipients)
|
||||
|
||||
assert data["actor"] == "https://my-place.social/profile/vaartis"
|
||||
assert data["type"] == "EmojiReact"
|
||||
assert data["content"] == "👎"
|
||||
assert data["id"] == "https://my-place.social/objects/e599373b-1368-4b20-cd24-837166957182"
|
||||
assert data["object"] == activity.data["object"]
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/friendica-dislike-undo.json")
|
||||
|> Jason.decode!()
|
||||
|> put_in(["object", "object"], activity.data["object"])
|
||||
|
||||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||
|
||||
assert data["actor"] == "https://my-place.social/profile/vaartis"
|
||||
assert data["type"] == "Undo"
|
||||
|
||||
assert data["object"] ==
|
||||
"https://my-place.social/objects/e599373b-1368-4b20-cd24-837166957182"
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -219,6 +219,36 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.NoteHandlingTest do
|
|||
"<p><span class=\"h-card\"><a href=\"http://localtesting.pleroma.lol/users/lain\" class=\"u-url mention\">@<span>lain</span></a></span></p>"
|
||||
end
|
||||
|
||||
test "it only uses contentMap if content is not present" do
|
||||
user = insert(:user)
|
||||
|
||||
message = %{
|
||||
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"type" => "Create",
|
||||
"object" => %{
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"id" => Utils.generate_object_id(),
|
||||
"type" => "Note",
|
||||
"content" => "Hi",
|
||||
"contentMap" => %{
|
||||
"de" => "Hallo",
|
||||
"uk" => "Привіт"
|
||||
},
|
||||
"inReplyTo" => nil,
|
||||
"attributedTo" => user.ap_id
|
||||
},
|
||||
"actor" => user.ap_id
|
||||
}
|
||||
|
||||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(message)
|
||||
object = Object.normalize(data["object"], fetch: false)
|
||||
|
||||
assert object.data["content"] == "Hi"
|
||||
end
|
||||
|
||||
test "it works for incoming notices with a nil contentMap (firefish)" do
|
||||
data =
|
||||
File.read!("test/fixtures/mastodon-post-activity-contentmap.json")
|
||||
|
|
@ -478,7 +508,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.NoteHandlingTest do
|
|||
|
||||
{:ok, activity} = Transmogrifier.handle_incoming(message)
|
||||
|
||||
{:ok, _} = Transmogrifier.prepare_outgoing(activity.data)
|
||||
{:ok, _} = Transmogrifier.prepare_activity(activity.data)
|
||||
end
|
||||
|
||||
test "successfully reserializes a message with AS2 objects in IR" do
|
||||
|
|
@ -507,7 +537,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.NoteHandlingTest do
|
|||
|
||||
{:ok, activity} = Transmogrifier.handle_incoming(message)
|
||||
|
||||
{:ok, _} = Transmogrifier.prepare_outgoing(activity.data)
|
||||
{:ok, _} = Transmogrifier.prepare_activity(activity.data)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -666,12 +696,19 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.NoteHandlingTest do
|
|||
describe "set_replies/1" do
|
||||
setup do: clear_config([:activitypub, :note_replies_output_limit], 2)
|
||||
|
||||
test "returns unmodified object if activity doesn't have self-replies" do
|
||||
test "still provides reply collection id even if activity doesn't have replies yet" do
|
||||
data = Jason.decode!(File.read!("test/fixtures/mastodon-post-activity.json"))
|
||||
assert Transmogrifier.set_replies(data) == data
|
||||
object = data["object"] |> Map.delete("replies")
|
||||
modified = Transmogrifier.set_replies(object)
|
||||
|
||||
refute object["replies"]
|
||||
assert modified["replies"]
|
||||
assert match?(%{"id" => "http" <> _, "totalItems" => 0}, modified["replies"])
|
||||
# first page should be omitted if there are no entries anyway
|
||||
refute modified["replies"]["first"]
|
||||
end
|
||||
|
||||
test "sets `replies` collection with a limited number of self-replies" do
|
||||
test "sets `replies` collection with a limited number of replies, preferring oldest" do
|
||||
[user, another_user] = insert_list(2, :user)
|
||||
|
||||
{:ok, %{id: id1} = activity} = CommonAPI.post(user, %{status: "1"})
|
||||
|
|
@ -700,7 +737,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.NoteHandlingTest do
|
|||
object = Object.normalize(activity, fetch: false)
|
||||
replies_uris = Enum.map([self_reply1, self_reply2], fn a -> a.object.data["id"] end)
|
||||
|
||||
assert %{"type" => "Collection", "items" => ^replies_uris} =
|
||||
assert %{"type" => "OrderedCollection", "first" => %{"orderedItems" => ^replies_uris}} =
|
||||
Transmogrifier.set_replies(object.data)["replies"]
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.RejectHandlingTest do
|
|||
followed = insert(:user, is_locked: true)
|
||||
|
||||
{:ok, follower, followed} = User.follow(follower, followed)
|
||||
{:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed)
|
||||
{:ok, _, _, follow_activity} = CommonAPI.follow(followed, follower)
|
||||
|
||||
assert User.following?(follower, followed) == true
|
||||
|
||||
|
|
|
|||
|
|
@ -119,8 +119,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.UserUpdateHandlingTest do
|
|||
user = User.get_cached_by_ap_id(user.ap_id)
|
||||
|
||||
assert user.fields == [
|
||||
%{"name" => "foo", "value" => "updated"},
|
||||
%{"name" => "foo1", "value" => "updated"}
|
||||
%{"name" => "foo", "value" => "bar"},
|
||||
%{"name" => "foo11", "value" => "bar11"}
|
||||
]
|
||||
|
||||
update_data =
|
||||
|
|
|
|||
|
|
@ -9,7 +9,10 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
|||
alias Pleroma.Activity
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.Builder
|
||||
alias Pleroma.Web.ActivityPub.Pipeline
|
||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||
alias Pleroma.Web.ActivityPub.UserView
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
alias Pleroma.Web.AdminAPI.AccountView
|
||||
alias Pleroma.Web.CommonAPI
|
||||
|
|
@ -123,6 +126,30 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
|||
assert activity.data["context"] == object.data["context"]
|
||||
end
|
||||
|
||||
test "it fixes the public scope addressing" do
|
||||
insert(:user,
|
||||
ap_id: "https://mymath.rocks/endpoints/SYn3cl_N4HAPfPHgo2x37XunLEmhV9LnxCggcYwyec0"
|
||||
)
|
||||
|
||||
object =
|
||||
"test/fixtures/bovine-bogus-public-note.json"
|
||||
|> File.read!()
|
||||
|> Jason.decode!()
|
||||
|
||||
message = %{
|
||||
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"type" => "Create",
|
||||
"actor" => "https://mymath.rocks/endpoints/SYn3cl_N4HAPfPHgo2x37XunLEmhV9LnxCggcYwyec0",
|
||||
"object" => object
|
||||
}
|
||||
|
||||
assert {:ok, activity} = Transmogrifier.handle_incoming(message)
|
||||
|
||||
object = Object.normalize(activity, fetch: false)
|
||||
assert "https://www.w3.org/ns/activitystreams#Public" in activity.data["to"]
|
||||
assert "https://www.w3.org/ns/activitystreams#Public" in object.data["to"]
|
||||
end
|
||||
|
||||
test "it keeps link tags" do
|
||||
insert(:user, ap_id: "https://example.org/users/alice")
|
||||
|
||||
|
|
@ -156,6 +183,246 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
|||
# It fetched the quoted post
|
||||
assert Object.normalize("https://misskey.io/notes/8vs6wxufd0")
|
||||
end
|
||||
|
||||
test "doesn't allow remote edits to fake local likes" do
|
||||
# as a spot check for no internal fields getting injected
|
||||
now = DateTime.utc_now()
|
||||
pub_date = DateTime.to_iso8601(Timex.subtract(now, Timex.Duration.from_minutes(3)))
|
||||
edit_date = DateTime.to_iso8601(now)
|
||||
|
||||
local_user = insert(:user)
|
||||
|
||||
create_data = %{
|
||||
"type" => "Create",
|
||||
"id" => "http://mastodon.example.org/users/admin/statuses/2619539638/activity",
|
||||
"actor" => "http://mastodon.example.org/users/admin",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => %{
|
||||
"type" => "Note",
|
||||
"id" => "http://mastodon.example.org/users/admin/statuses/2619539638",
|
||||
"attributedTo" => "http://mastodon.example.org/users/admin",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"published" => pub_date,
|
||||
"content" => "miaow",
|
||||
"likes" => [local_user.ap_id]
|
||||
}
|
||||
}
|
||||
|
||||
update_data =
|
||||
create_data
|
||||
|> Map.put("type", "Update")
|
||||
|> Map.put("id", create_data["object"]["id"] <> "/update/1")
|
||||
|> put_in(["object", "content"], "miaow :3")
|
||||
|> put_in(["object", "updated"], edit_date)
|
||||
|> put_in(["object", "formerRepresentations"], %{
|
||||
"type" => "OrderedCollection",
|
||||
"totalItems" => 1,
|
||||
"orderedItems" => [create_data["object"]]
|
||||
})
|
||||
|
||||
{:ok, %Pleroma.Activity{} = activity} = Transmogrifier.handle_incoming(create_data)
|
||||
%Pleroma.Object{} = object = Object.get_by_ap_id(activity.data["object"])
|
||||
assert object.data["content"] == "miaow"
|
||||
assert object.data["likes"] == []
|
||||
assert object.data["like_count"] == 0
|
||||
|
||||
{:ok, %Pleroma.Activity{} = activity} = Transmogrifier.handle_incoming(update_data)
|
||||
%Pleroma.Object{} = object = Object.get_by_ap_id(activity.data["object"]["id"])
|
||||
assert object.data["content"] == "miaow :3"
|
||||
assert object.data["likes"] == []
|
||||
assert object.data["like_count"] == 0
|
||||
end
|
||||
|
||||
test "strips internal fields from history items in edited notes" do
|
||||
now = DateTime.utc_now()
|
||||
pub_date = DateTime.to_iso8601(Timex.subtract(now, Timex.Duration.from_minutes(3)))
|
||||
edit_date = DateTime.to_iso8601(now)
|
||||
|
||||
local_user = insert(:user)
|
||||
|
||||
create_data = %{
|
||||
"type" => "Create",
|
||||
"id" => "http://mastodon.example.org/users/admin/statuses/2619539638/activity",
|
||||
"actor" => "http://mastodon.example.org/users/admin",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => %{
|
||||
"type" => "Note",
|
||||
"id" => "http://mastodon.example.org/users/admin/statuses/2619539638",
|
||||
"attributedTo" => "http://mastodon.example.org/users/admin",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"published" => pub_date,
|
||||
"content" => "miaow",
|
||||
"likes" => [],
|
||||
"like_count" => 0
|
||||
}
|
||||
}
|
||||
|
||||
update_data =
|
||||
create_data
|
||||
|> Map.put("type", "Update")
|
||||
|> Map.put("id", create_data["object"]["id"] <> "/update/1")
|
||||
|> put_in(["object", "content"], "miaow :3")
|
||||
|> put_in(["object", "updated"], edit_date)
|
||||
|> put_in(["object", "formerRepresentations"], %{
|
||||
"type" => "OrderedCollection",
|
||||
"totalItems" => 1,
|
||||
"orderedItems" => [
|
||||
Map.merge(create_data["object"], %{
|
||||
"likes" => [local_user.ap_id],
|
||||
"like_count" => 1,
|
||||
"pleroma" => %{"internal_field" => "should_be_stripped"}
|
||||
})
|
||||
]
|
||||
})
|
||||
|
||||
{:ok, %Pleroma.Activity{} = activity} = Transmogrifier.handle_incoming(create_data)
|
||||
%Pleroma.Object{} = object = Object.get_by_ap_id(activity.data["object"])
|
||||
assert object.data["content"] == "miaow"
|
||||
assert object.data["likes"] == []
|
||||
assert object.data["like_count"] == 0
|
||||
|
||||
{:ok, %Pleroma.Activity{} = activity} = Transmogrifier.handle_incoming(update_data)
|
||||
%Pleroma.Object{} = object = Object.get_by_ap_id(activity.data["object"]["id"])
|
||||
assert object.data["content"] == "miaow :3"
|
||||
assert object.data["likes"] == []
|
||||
assert object.data["like_count"] == 0
|
||||
|
||||
# Check that internal fields are stripped from history items
|
||||
history_item = List.first(object.data["formerRepresentations"]["orderedItems"])
|
||||
assert history_item["likes"] == []
|
||||
assert history_item["like_count"] == 0
|
||||
refute Map.has_key?(history_item, "pleroma")
|
||||
end
|
||||
|
||||
test "doesn't trip over remote likes in notes" do
|
||||
now = DateTime.utc_now()
|
||||
pub_date = DateTime.to_iso8601(Timex.subtract(now, Timex.Duration.from_minutes(3)))
|
||||
edit_date = DateTime.to_iso8601(now)
|
||||
|
||||
create_data = %{
|
||||
"type" => "Create",
|
||||
"id" => "http://mastodon.example.org/users/admin/statuses/3409297097/activity",
|
||||
"actor" => "http://mastodon.example.org/users/admin",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => %{
|
||||
"type" => "Note",
|
||||
"id" => "http://mastodon.example.org/users/admin/statuses/3409297097",
|
||||
"attributedTo" => "http://mastodon.example.org/users/admin",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"published" => pub_date,
|
||||
"content" => "miaow",
|
||||
"likes" => %{
|
||||
"id" => "http://mastodon.example.org/users/admin/statuses/3409297097/likes",
|
||||
"totalItems" => 0,
|
||||
"type" => "Collection"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
update_data =
|
||||
create_data
|
||||
|> Map.put("type", "Update")
|
||||
|> Map.put("id", create_data["object"]["id"] <> "/update/1")
|
||||
|> put_in(["object", "content"], "miaow :3")
|
||||
|> put_in(["object", "updated"], edit_date)
|
||||
|> put_in(["object", "likes", "totalItems"], 666)
|
||||
|> put_in(["object", "formerRepresentations"], %{
|
||||
"type" => "OrderedCollection",
|
||||
"totalItems" => 1,
|
||||
"orderedItems" => [create_data["object"]]
|
||||
})
|
||||
|
||||
{:ok, %Pleroma.Activity{} = activity} = Transmogrifier.handle_incoming(create_data)
|
||||
%Pleroma.Object{} = object = Object.get_by_ap_id(activity.data["object"])
|
||||
assert object.data["content"] == "miaow"
|
||||
assert object.data["likes"] == []
|
||||
assert object.data["like_count"] == 0
|
||||
|
||||
{:ok, %Pleroma.Activity{} = activity} = Transmogrifier.handle_incoming(update_data)
|
||||
%Pleroma.Object{} = object = Object.get_by_ap_id(activity.data["object"]["id"])
|
||||
assert object.data["content"] == "miaow :3"
|
||||
assert object.data["likes"] == []
|
||||
# in the future this should retain remote likes, but for now:
|
||||
assert object.data["like_count"] == 0
|
||||
end
|
||||
|
||||
test "doesn't trip over remote likes in polls" do
|
||||
now = DateTime.utc_now()
|
||||
pub_date = DateTime.to_iso8601(Timex.subtract(now, Timex.Duration.from_minutes(3)))
|
||||
edit_date = DateTime.to_iso8601(now)
|
||||
|
||||
create_data = %{
|
||||
"type" => "Create",
|
||||
"id" => "http://mastodon.example.org/users/admin/statuses/2471790073/activity",
|
||||
"actor" => "http://mastodon.example.org/users/admin",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => %{
|
||||
"type" => "Question",
|
||||
"id" => "http://mastodon.example.org/users/admin/statuses/2471790073",
|
||||
"attributedTo" => "http://mastodon.example.org/users/admin",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"published" => pub_date,
|
||||
"content" => "vote!",
|
||||
"anyOf" => [
|
||||
%{
|
||||
"type" => "Note",
|
||||
"name" => "a",
|
||||
"replies" => %{
|
||||
"type" => "Collection",
|
||||
"totalItems" => 3
|
||||
}
|
||||
},
|
||||
%{
|
||||
"type" => "Note",
|
||||
"name" => "b",
|
||||
"replies" => %{
|
||||
"type" => "Collection",
|
||||
"totalItems" => 1
|
||||
}
|
||||
}
|
||||
],
|
||||
"likes" => %{
|
||||
"id" => "http://mastodon.example.org/users/admin/statuses/2471790073/likes",
|
||||
"totalItems" => 0,
|
||||
"type" => "Collection"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
update_data =
|
||||
create_data
|
||||
|> Map.put("type", "Update")
|
||||
|> Map.put("id", create_data["object"]["id"] <> "/update/1")
|
||||
|> put_in(["object", "content"], "vote now!")
|
||||
|> put_in(["object", "updated"], edit_date)
|
||||
|> put_in(["object", "likes", "totalItems"], 666)
|
||||
|> put_in(["object", "formerRepresentations"], %{
|
||||
"type" => "OrderedCollection",
|
||||
"totalItems" => 1,
|
||||
"orderedItems" => [create_data["object"]]
|
||||
})
|
||||
|
||||
{:ok, %Pleroma.Activity{} = activity} = Transmogrifier.handle_incoming(create_data)
|
||||
%Pleroma.Object{} = object = Object.get_by_ap_id(activity.data["object"])
|
||||
assert object.data["content"] == "vote!"
|
||||
assert object.data["likes"] == []
|
||||
assert object.data["like_count"] == 0
|
||||
|
||||
{:ok, %Pleroma.Activity{} = activity} = Transmogrifier.handle_incoming(update_data)
|
||||
%Pleroma.Object{} = object = Object.get_by_ap_id(activity.data["object"]["id"])
|
||||
assert object.data["content"] == "vote now!"
|
||||
assert object.data["likes"] == []
|
||||
# in the future this should retain remote likes, but for now:
|
||||
assert object.data["like_count"] == 0
|
||||
end
|
||||
end
|
||||
|
||||
describe "prepare outgoing" do
|
||||
|
|
@ -166,7 +433,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
|||
|
||||
{:ok, announce_activity} = CommonAPI.repeat(activity.id, user)
|
||||
|
||||
{:ok, modified} = Transmogrifier.prepare_outgoing(announce_activity.data)
|
||||
{:ok, modified} = Transmogrifier.prepare_activity(announce_activity.data)
|
||||
|
||||
assert modified["object"]["content"] == "hey"
|
||||
assert modified["object"]["actor"] == modified["object"]["attributedTo"]
|
||||
|
|
@ -181,7 +448,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
|||
|
||||
with_mock Pleroma.Notification,
|
||||
get_notified_from_activity: fn _, _ -> [] end do
|
||||
{:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
|
||||
{:ok, modified} = Transmogrifier.prepare_activity(activity.data)
|
||||
|
||||
object = modified["object"]
|
||||
|
||||
|
|
@ -207,7 +474,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
|||
user = insert(:user)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "hey"})
|
||||
{:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
|
||||
{:ok, modified} = Transmogrifier.prepare_activity(activity.data)
|
||||
|
||||
assert modified["@context"] == Utils.make_json_ld_header()["@context"]
|
||||
|
||||
|
|
@ -218,7 +485,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
|||
user = insert(:user)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "hey"})
|
||||
{:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
|
||||
{:ok, modified} = Transmogrifier.prepare_activity(activity.data)
|
||||
|
||||
assert modified["object"]["actor"] == modified["object"]["attributedTo"]
|
||||
end
|
||||
|
|
@ -234,7 +501,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
|||
"name" => "#2hu"
|
||||
}
|
||||
|
||||
{:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
|
||||
{:ok, modified} = Transmogrifier.prepare_activity(activity.data)
|
||||
|
||||
assert modified["object"]["tag"] == [expected_tag]
|
||||
end
|
||||
|
|
@ -257,7 +524,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
|||
url: "https://pleroma.social"
|
||||
} == activity.object.data["generator"]
|
||||
|
||||
{:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
|
||||
{:ok, modified} = Transmogrifier.prepare_activity(activity.data)
|
||||
|
||||
assert length(modified["object"]["tag"]) == 2
|
||||
|
||||
|
|
@ -266,12 +533,15 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
|||
assert is_nil(modified["object"]["announcements"])
|
||||
assert is_nil(modified["object"]["announcement_count"])
|
||||
assert is_nil(modified["object"]["generator"])
|
||||
assert is_nil(modified["object"]["rules"])
|
||||
assert is_nil(modified["object"]["language"])
|
||||
assert is_nil(modified["object"]["voters"])
|
||||
end
|
||||
|
||||
test "it strips internal fields of article" do
|
||||
activity = insert(:article_activity)
|
||||
|
||||
{:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
|
||||
{:ok, modified} = Transmogrifier.prepare_activity(activity.data)
|
||||
|
||||
assert length(modified["object"]["tag"]) == 2
|
||||
|
||||
|
|
@ -288,13 +558,13 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
|||
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "2hu :moominmamma:"})
|
||||
|
||||
{:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
|
||||
{:ok, modified} = Transmogrifier.prepare_activity(activity.data)
|
||||
|
||||
assert modified["directMessage"] == false
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "@#{other_user.nickname} :moominmamma:"})
|
||||
|
||||
{:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
|
||||
{:ok, modified} = Transmogrifier.prepare_activity(activity.data)
|
||||
|
||||
assert modified["directMessage"] == false
|
||||
|
||||
|
|
@ -304,18 +574,18 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
|||
visibility: "direct"
|
||||
})
|
||||
|
||||
{:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
|
||||
{:ok, modified} = Transmogrifier.prepare_activity(activity.data)
|
||||
|
||||
assert modified["directMessage"] == true
|
||||
end
|
||||
|
||||
test "it strips BCC field" do
|
||||
user = insert(:user)
|
||||
{:ok, list} = Pleroma.List.create("foo", user)
|
||||
{:ok, list} = Pleroma.List.create(%{title: "foo"}, user)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})
|
||||
|
||||
{:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
|
||||
{:ok, modified} = Transmogrifier.prepare_activity(activity.data)
|
||||
|
||||
assert is_nil(modified["bcc"])
|
||||
end
|
||||
|
|
@ -323,7 +593,8 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
|||
test "it can handle Listen activities" do
|
||||
listen_activity = insert(:listen)
|
||||
|
||||
{:ok, modified} = Transmogrifier.prepare_outgoing(listen_activity.data)
|
||||
# This has an inlined object as in ObjectView
|
||||
{:ok, modified} = Transmogrifier.prepare_activity(listen_activity.data)
|
||||
|
||||
assert modified["type"] == "Listen"
|
||||
|
||||
|
|
@ -331,7 +602,36 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
|||
|
||||
{:ok, activity} = CommonAPI.listen(user, %{"title" => "lain radio episode 1"})
|
||||
|
||||
{:ok, _modified} = Transmogrifier.prepare_outgoing(activity.data)
|
||||
user_ap_id = user.ap_id
|
||||
activity_ap_id = activity.data["id"]
|
||||
activity_to = activity.data["to"]
|
||||
activity_cc = activity.data["cc"]
|
||||
object_ap_id = activity.data["object"]
|
||||
object_type = activity.object.data["type"]
|
||||
|
||||
# This does not have an inlined object
|
||||
{:ok, modified2} = Transmogrifier.prepare_activity(activity.data)
|
||||
|
||||
assert match?(
|
||||
%{
|
||||
"@context" => [_ | _],
|
||||
"type" => "Listen",
|
||||
"actor" => ^user_ap_id,
|
||||
"to" => ^activity_to,
|
||||
"cc" => ^activity_cc,
|
||||
"context" => "http://localhost" <> _,
|
||||
"id" => ^activity_ap_id,
|
||||
"object" => %{
|
||||
"actor" => ^user_ap_id,
|
||||
"attributedTo" => ^user_ap_id,
|
||||
"id" => ^object_ap_id,
|
||||
"type" => ^object_type,
|
||||
"to" => ^activity_to,
|
||||
"cc" => ^activity_cc
|
||||
}
|
||||
},
|
||||
modified2
|
||||
)
|
||||
end
|
||||
|
||||
test "custom emoji urls are URI encoded" do
|
||||
|
|
@ -340,7 +640,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
|||
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "everybody do the dinosaur :dinosaur:"})
|
||||
|
||||
{:ok, prepared} = Transmogrifier.prepare_outgoing(activity.data)
|
||||
{:ok, prepared} = Transmogrifier.prepare_activity(activity.data)
|
||||
|
||||
assert length(prepared["object"]["tag"]) == 1
|
||||
|
||||
|
|
@ -353,9 +653,9 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
|||
user = insert(:user)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "everybody do the dinosaur :dinosaur:"})
|
||||
{:ok, update} = CommonAPI.update(user, activity, %{status: "mew mew :blank:"})
|
||||
{:ok, update} = CommonAPI.update(activity, user, %{status: "mew mew :blank:"})
|
||||
|
||||
{:ok, prepared} = Transmogrifier.prepare_outgoing(update.data)
|
||||
{:ok, prepared} = Transmogrifier.prepare_activity(update.data)
|
||||
|
||||
assert %{
|
||||
"content" => "mew mew :blank:",
|
||||
|
|
@ -371,19 +671,204 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
|||
} = prepared["object"]
|
||||
end
|
||||
|
||||
test "Updates of Actors are handled" do
|
||||
user = insert(:user, local: true)
|
||||
|
||||
changeset = User.update_changeset(user, %{name: "new name"})
|
||||
{:ok, unpersisted_user} = Ecto.Changeset.apply_action(changeset, :update)
|
||||
|
||||
updated_object =
|
||||
UserView.render("user.json", user: unpersisted_user)
|
||||
|> Map.delete("@context")
|
||||
|
||||
{:ok, update_data, []} = Builder.update(user, updated_object)
|
||||
|
||||
{:ok, activity, _} =
|
||||
Pipeline.common_pipeline(update_data,
|
||||
local: true,
|
||||
user_update_changeset: changeset
|
||||
)
|
||||
|
||||
assert {:ok, prepared} = Transmogrifier.prepare_activity(activity.data)
|
||||
assert prepared["type"] == "Update"
|
||||
assert prepared["@context"]
|
||||
assert prepared["object"]["type"] == user.actor_type
|
||||
end
|
||||
|
||||
test "Correctly handles Undo activities" do
|
||||
blocked = insert(:user)
|
||||
blocker = insert(:user, local: true)
|
||||
|
||||
blocked_ap_id = blocked.ap_id
|
||||
blocker_ap_id = blocker.ap_id
|
||||
|
||||
{:ok, %Activity{} = block_activity} = CommonAPI.block(blocked, blocker)
|
||||
{:ok, %Activity{} = undo_activity} = CommonAPI.unblock(blocked, blocker)
|
||||
{:ok, data} = Transmogrifier.prepare_activity(undo_activity.data)
|
||||
|
||||
block_ap_id = block_activity.data["id"]
|
||||
assert is_binary(block_ap_id)
|
||||
|
||||
assert match?(
|
||||
%{
|
||||
"@context" => [_ | _],
|
||||
"type" => "Undo",
|
||||
"id" => "http://localhost" <> _,
|
||||
"actor" => ^blocker_ap_id,
|
||||
"object" => ^block_ap_id,
|
||||
"to" => [^blocked_ap_id],
|
||||
"cc" => [],
|
||||
"bto" => [],
|
||||
"bcc" => []
|
||||
},
|
||||
data
|
||||
)
|
||||
end
|
||||
|
||||
test "Correctly handles EmojiReact activities" do
|
||||
user = insert(:user, local: true)
|
||||
note_activity = insert(:note_activity)
|
||||
|
||||
user_ap_id = user.ap_id
|
||||
user_followers = user.follower_address
|
||||
note_author = note_activity.data["actor"]
|
||||
note_ap_id = note_activity.data["object"]
|
||||
|
||||
assert is_binary(note_author)
|
||||
assert is_binary(note_ap_id)
|
||||
|
||||
{:ok, react_activity} = CommonAPI.react_with_emoji(note_activity.id, user, "🐈")
|
||||
{:ok, data} = Transmogrifier.prepare_activity(react_activity.data)
|
||||
|
||||
assert match?(
|
||||
%{
|
||||
"@context" => [_ | _],
|
||||
"type" => "EmojiReact",
|
||||
"actor" => ^user_ap_id,
|
||||
"to" => [^user_followers, ^note_author],
|
||||
"cc" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"bto" => [],
|
||||
"bcc" => [],
|
||||
"content" => "🐈",
|
||||
"context" => "2hu",
|
||||
"id" => "http://localhost" <> _,
|
||||
"object" => ^note_ap_id,
|
||||
"tag" => []
|
||||
},
|
||||
data
|
||||
)
|
||||
end
|
||||
|
||||
test "EmojiReact custom emoji urls are URI encoded" do
|
||||
user = insert(:user, local: true)
|
||||
note_activity = insert(:note_activity)
|
||||
|
||||
{:ok, react_activity} = CommonAPI.react_with_emoji(note_activity.id, user, ":dinosaur:")
|
||||
{:ok, data} = Transmogrifier.prepare_activity(react_activity.data)
|
||||
|
||||
assert length(data["tag"]) == 1
|
||||
|
||||
tag = List.first(data["tag"])
|
||||
url = tag["icon"]["url"]
|
||||
|
||||
assert url == "http://localhost:4001/emoji/dino%20walking.gif"
|
||||
assert tag["id"] == "http://localhost:4001/emoji/dino%20walking.gif"
|
||||
end
|
||||
|
||||
test "it prepares a quote post" do
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, quoted_post} = CommonAPI.post(user, %{status: "hey"})
|
||||
{:ok, quote_post} = CommonAPI.post(user, %{status: "hey", quote_id: quoted_post.id})
|
||||
{:ok, quote_post} = CommonAPI.post(user, %{status: "hey", quoted_status_id: quoted_post.id})
|
||||
|
||||
{:ok, modified} = Transmogrifier.prepare_outgoing(quote_post.data)
|
||||
{:ok, modified} = Transmogrifier.prepare_activity(quote_post.data)
|
||||
|
||||
%{data: %{"id" => quote_id}} = Object.normalize(quoted_post)
|
||||
|
||||
assert modified["object"]["quoteUrl"] == quote_id
|
||||
assert modified["object"]["quoteUri"] == quote_id
|
||||
end
|
||||
|
||||
test "it adds language of the object to its json-ld context" do
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "Cześć", language: "pl"})
|
||||
{:ok, modified} = Transmogrifier.prepare_activity(activity.object.data)
|
||||
|
||||
assert [_, _, %{"@language" => "pl"}] = modified["@context"]
|
||||
end
|
||||
|
||||
test "it adds language of the object to Create activity json-ld context" do
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "Cześć", language: "pl"})
|
||||
{:ok, modified} = Transmogrifier.prepare_activity(activity.data)
|
||||
|
||||
assert [_, _, %{"@language" => "pl"}] = modified["@context"]
|
||||
end
|
||||
|
||||
test "it strips report data" do
|
||||
reporter = insert(:user)
|
||||
target_account = insert(:user)
|
||||
content = "foobar"
|
||||
{:ok, reported_activity} = CommonAPI.post(target_account, %{status: content})
|
||||
context = Utils.generate_context_id()
|
||||
|
||||
object_ap_id = reported_activity.object.data["id"]
|
||||
|
||||
assert {:ok, activity} =
|
||||
Pleroma.Web.ActivityPub.ActivityPub.flag(%{
|
||||
actor: reporter,
|
||||
context: context,
|
||||
account: target_account,
|
||||
statuses: [reported_activity],
|
||||
content: content
|
||||
})
|
||||
|
||||
{:ok, data} = Transmogrifier.prepare_activity(activity.data)
|
||||
|
||||
expected_data =
|
||||
activity.data
|
||||
|> put_in(["object"], [target_account.ap_id, object_ap_id])
|
||||
|> Map.put("actor", reporter.ap_id)
|
||||
|> Map.merge(Utils.make_json_ld_header())
|
||||
|
||||
assert data == expected_data
|
||||
end
|
||||
|
||||
test "it strips report data and anonymize" do
|
||||
placeholder = insert(:user)
|
||||
|
||||
reporter = insert(:user)
|
||||
target_account = insert(:user)
|
||||
content = "foobar"
|
||||
{:ok, reported_activity} = CommonAPI.post(target_account, %{status: content})
|
||||
context = Utils.generate_context_id()
|
||||
|
||||
object_ap_id = reported_activity.object.data["id"]
|
||||
|
||||
assert {:ok, activity} =
|
||||
Pleroma.Web.ActivityPub.ActivityPub.flag(%{
|
||||
actor: reporter,
|
||||
context: context,
|
||||
account: target_account,
|
||||
statuses: [reported_activity],
|
||||
content: content
|
||||
})
|
||||
|
||||
clear_config([:activitypub, :anonymize_reporter], true)
|
||||
clear_config([:activitypub, :anonymize_reporter_local_nickname], placeholder.nickname)
|
||||
|
||||
{:ok, data} = Transmogrifier.prepare_activity(activity.data)
|
||||
|
||||
expected_data =
|
||||
activity.data
|
||||
|> put_in(["object"], [target_account.ap_id, object_ap_id])
|
||||
|> Map.put("actor", placeholder.ap_id)
|
||||
|> Map.merge(Utils.make_json_ld_header())
|
||||
|
||||
assert data == expected_data
|
||||
end
|
||||
end
|
||||
|
||||
describe "actor rewriting" do
|
||||
|
|
@ -621,5 +1106,14 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
|||
processed = Transmogrifier.prepare_object(original)
|
||||
assert processed["formerRepresentations"] == original["formerRepresentations"]
|
||||
end
|
||||
|
||||
test "it uses contentMap to specify post language" do
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "Cześć", language: "pl"})
|
||||
object = Transmogrifier.prepare_object(activity.object.data)
|
||||
|
||||
assert %{"contentMap" => %{"pl" => "Cześć"}} = object
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.UtilsTest do
|
||||
use Pleroma.DataCase, async: true
|
||||
use Pleroma.DataCase, async: false
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Repo
|
||||
|
|
@ -173,16 +173,30 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do
|
|||
end
|
||||
end
|
||||
|
||||
test "make_json_ld_header/0" do
|
||||
assert Utils.make_json_ld_header() == %{
|
||||
"@context" => [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"http://localhost:4001/schemas/litepub-0.1.jsonld",
|
||||
%{
|
||||
"@language" => "und"
|
||||
}
|
||||
]
|
||||
}
|
||||
describe "make_json_ld_header/1" do
|
||||
test "makes jsonld header" do
|
||||
assert Utils.make_json_ld_header() == %{
|
||||
"@context" => [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"http://localhost:4001/schemas/litepub-0.1.jsonld",
|
||||
%{
|
||||
"@language" => "und"
|
||||
}
|
||||
]
|
||||
}
|
||||
end
|
||||
|
||||
test "includes language if specified" do
|
||||
assert Utils.make_json_ld_header(%{"language" => "pl"}) == %{
|
||||
"@context" => [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"http://localhost:4001/schemas/litepub-0.1.jsonld",
|
||||
%{
|
||||
"@language" => "pl"
|
||||
}
|
||||
]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
describe "get_existing_votes" do
|
||||
|
|
@ -201,7 +215,7 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do
|
|||
})
|
||||
|
||||
object = Object.normalize(activity, fetch: false)
|
||||
{:ok, votes, object} = CommonAPI.vote(other_user, object, [0, 1])
|
||||
{:ok, votes, object} = CommonAPI.vote(object, other_user, [0, 1])
|
||||
assert Enum.sort(Utils.get_existing_votes(other_user.ap_id, object)) == Enum.sort(votes)
|
||||
end
|
||||
|
||||
|
|
@ -219,8 +233,8 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do
|
|||
})
|
||||
|
||||
object = Object.normalize(activity, fetch: false)
|
||||
{:ok, [vote], object} = CommonAPI.vote(other_user, object, [0])
|
||||
{:ok, _activity} = CommonAPI.favorite(user, activity.id)
|
||||
{:ok, [vote], object} = CommonAPI.vote(object, other_user, [0])
|
||||
{:ok, _activity} = CommonAPI.favorite(activity.id, user)
|
||||
[fetched_vote] = Utils.get_existing_votes(other_user.ap_id, object)
|
||||
assert fetched_vote.id == vote.id
|
||||
end
|
||||
|
|
@ -231,8 +245,8 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do
|
|||
user = insert(:user, is_locked: true)
|
||||
follower = insert(:user)
|
||||
|
||||
{:ok, _, _, follow_activity} = CommonAPI.follow(follower, user)
|
||||
{:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user)
|
||||
{:ok, _, _, follow_activity} = CommonAPI.follow(user, follower)
|
||||
{:ok, _, _, follow_activity_two} = CommonAPI.follow(user, follower)
|
||||
|
||||
data =
|
||||
follow_activity_two.data
|
||||
|
|
@ -253,8 +267,8 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do
|
|||
user = insert(:user)
|
||||
follower = insert(:user)
|
||||
|
||||
{:ok, _, _, follow_activity} = CommonAPI.follow(follower, user)
|
||||
{:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user)
|
||||
{:ok, _, _, follow_activity} = CommonAPI.follow(user, follower)
|
||||
{:ok, _, _, follow_activity_two} = CommonAPI.follow(user, follower)
|
||||
|
||||
{:ok, follow_activity_two} =
|
||||
Utils.update_follow_state_for_all(follow_activity_two, "reject")
|
||||
|
|
@ -269,8 +283,8 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do
|
|||
user = insert(:user, is_locked: true)
|
||||
follower = insert(:user)
|
||||
|
||||
{:ok, _, _, follow_activity} = CommonAPI.follow(follower, user)
|
||||
{:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user)
|
||||
{:ok, _, _, follow_activity} = CommonAPI.follow(user, follower)
|
||||
{:ok, _, _, follow_activity_two} = CommonAPI.follow(user, follower)
|
||||
|
||||
data =
|
||||
follow_activity_two.data
|
||||
|
|
@ -355,7 +369,7 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do
|
|||
|
||||
user = insert(:user)
|
||||
refute Utils.get_existing_like(user.ap_id, object)
|
||||
{:ok, like_activity} = CommonAPI.favorite(user, note_activity.id)
|
||||
{:ok, like_activity} = CommonAPI.favorite(note_activity.id, user)
|
||||
|
||||
assert ^like_activity = Utils.get_existing_like(user.ap_id, object)
|
||||
end
|
||||
|
|
@ -382,9 +396,9 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do
|
|||
user1 = insert(:user)
|
||||
user2 = insert(:user)
|
||||
|
||||
assert {:ok, %Activity{} = _} = CommonAPI.block(user1, user2)
|
||||
assert {:ok, %Activity{} = _} = CommonAPI.block(user1, user2)
|
||||
assert {:ok, %Activity{} = activity} = CommonAPI.block(user1, user2)
|
||||
assert {:ok, %Activity{} = _} = CommonAPI.block(user2, user1)
|
||||
assert {:ok, %Activity{} = _} = CommonAPI.block(user2, user1)
|
||||
assert {:ok, %Activity{} = activity} = CommonAPI.block(user2, user1)
|
||||
|
||||
assert Utils.fetch_latest_block(user1, user2) == activity
|
||||
end
|
||||
|
|
@ -546,7 +560,7 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do
|
|||
target_account = insert(:user)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(posting_account, %{status: "foobar"})
|
||||
{:ok, like} = CommonAPI.favorite(target_account, activity.id)
|
||||
{:ok, like} = CommonAPI.favorite(activity.id, target_account)
|
||||
context = Utils.generate_context_id()
|
||||
content = "foobar"
|
||||
|
||||
|
|
@ -656,4 +670,91 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do
|
|||
)
|
||||
end
|
||||
end
|
||||
|
||||
describe "assign_report_to_account/2" do
|
||||
test "assigns report to an account" do
|
||||
reporter = insert(:user)
|
||||
target_account = insert(:user)
|
||||
%{id: assigned_id} = insert(:user)
|
||||
|
||||
{:ok, report} = CommonAPI.report(reporter, %{account_id: target_account.id})
|
||||
{:ok, report} = Utils.assign_report_to_account(report, assigned_id)
|
||||
|
||||
assert %{data: %{"assigned_account" => ^assigned_id}} = report
|
||||
end
|
||||
end
|
||||
|
||||
describe "maybe_anonymize_reporter/1" do
|
||||
setup do
|
||||
reporter = insert(:user)
|
||||
report = %{"actor" => reporter.ap_id}
|
||||
|
||||
%{
|
||||
placeholder: insert(:user),
|
||||
reporter: reporter,
|
||||
report: report
|
||||
}
|
||||
end
|
||||
|
||||
test "anonymize when configured correctly", %{
|
||||
placeholder: placeholder,
|
||||
report: report
|
||||
} do
|
||||
clear_config([:activitypub, :anonymize_reporter], true)
|
||||
clear_config([:activitypub, :anonymize_reporter_local_nickname], placeholder.nickname)
|
||||
|
||||
assert %{"actor" => placeholder.ap_id} == Utils.maybe_anonymize_reporter(report)
|
||||
end
|
||||
|
||||
test "anonymize Activity", %{
|
||||
placeholder: placeholder,
|
||||
reporter: reporter,
|
||||
report: report
|
||||
} do
|
||||
clear_config([:activitypub, :anonymize_reporter], true)
|
||||
clear_config([:activitypub, :anonymize_reporter_local_nickname], placeholder.nickname)
|
||||
report_activity = %Activity{actor: reporter, data: report}
|
||||
anon_id = placeholder.ap_id
|
||||
|
||||
assert %Activity{actor: ^anon_id, data: %{"actor" => ^anon_id}} =
|
||||
Utils.maybe_anonymize_reporter(report_activity)
|
||||
end
|
||||
|
||||
test "do not anonymize when disabled", %{
|
||||
placeholder: placeholder,
|
||||
reporter: reporter,
|
||||
report: report
|
||||
} do
|
||||
clear_config([:activitypub, :anonymize_reporter], false)
|
||||
clear_config([:activitypub, :anonymize_reporter_local_nickname], placeholder.nickname)
|
||||
|
||||
assert %{"actor" => reporter.ap_id} == Utils.maybe_anonymize_reporter(report)
|
||||
end
|
||||
|
||||
test "do not anonymize when user does not exist", %{
|
||||
placeholder: placeholder,
|
||||
reporter: reporter,
|
||||
report: report
|
||||
} do
|
||||
clear_config([:activitypub, :anonymize_reporter], true)
|
||||
|
||||
clear_config(
|
||||
[:activitypub, :anonymize_reporter_local_nickname],
|
||||
placeholder.nickname <> "MewMew"
|
||||
)
|
||||
|
||||
assert %{"actor" => reporter.ap_id} == Utils.maybe_anonymize_reporter(report)
|
||||
end
|
||||
|
||||
test "do not anonymize when user is not local", %{
|
||||
reporter: reporter,
|
||||
report: report
|
||||
} do
|
||||
placeholder = insert(:user, local: false)
|
||||
clear_config([:activitypub, :anonymize_reporter], true)
|
||||
clear_config([:activitypub, :anonymize_reporter_local_nickname], placeholder.nickname)
|
||||
|
||||
assert %{"actor" => reporter.ap_id} == Utils.maybe_anonymize_reporter(report)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -49,9 +49,39 @@ defmodule Pleroma.Web.ActivityPub.ObjectViewTest do
|
|||
replies_uris = [self_reply1.object.data["id"]]
|
||||
result = ObjectView.render("object.json", %{object: refresh_record(activity)})
|
||||
|
||||
assert %{"type" => "Collection", "items" => ^replies_uris} =
|
||||
assert %{
|
||||
"type" => "OrderedCollection",
|
||||
"id" => _,
|
||||
"first" => %{"orderedItems" => ^replies_uris}
|
||||
} =
|
||||
get_in(result, ["object", "replies"])
|
||||
end
|
||||
|
||||
test "renders a replies collection on its own" do
|
||||
user = insert(:user)
|
||||
activity = insert(:note_activity, user: user)
|
||||
activity = Pleroma.Activity.get_by_id_with_object(activity.id)
|
||||
|
||||
{:ok, r1} =
|
||||
CommonAPI.post(user, %{status: "reply1", in_reply_to_status_id: activity.id})
|
||||
|
||||
{:ok, r2} =
|
||||
CommonAPI.post(user, %{status: "reply1", in_reply_to_status_id: activity.id})
|
||||
|
||||
replies_uris = [r1.object.data["id"], r2.object.data["id"]]
|
||||
|
||||
result =
|
||||
ObjectView.render("object_replies.json", %{
|
||||
render_params: %{object_ap_id: activity.object.data["id"]}
|
||||
})
|
||||
|
||||
%{
|
||||
"type" => "OrderedCollection",
|
||||
"id" => _,
|
||||
"totalItems" => 2,
|
||||
"first" => %{"orderedItems" => ^replies_uris}
|
||||
} = result
|
||||
end
|
||||
end
|
||||
|
||||
test "renders a like activity" do
|
||||
|
|
@ -59,7 +89,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectViewTest do
|
|||
object = Object.normalize(note, fetch: false)
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, like_activity} = CommonAPI.favorite(user, note.id)
|
||||
{:ok, like_activity} = CommonAPI.favorite(note.id, user)
|
||||
|
||||
result = ObjectView.render("object.json", %{object: like_activity})
|
||||
|
||||
|
|
@ -95,4 +125,23 @@ defmodule Pleroma.Web.ActivityPub.ObjectViewTest do
|
|||
assert result["object"] == announce.data["id"]
|
||||
assert result["type"] == "Undo"
|
||||
end
|
||||
|
||||
test "renders a listen activity" do
|
||||
audio = insert(:audio)
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, listen_activity} = CommonAPI.listen(user, audio.data)
|
||||
|
||||
result = ObjectView.render("object.json", %{object: listen_activity})
|
||||
|
||||
assert result["id"] == listen_activity.data["id"]
|
||||
assert result["to"] == listen_activity.data["to"]
|
||||
assert result["type"] == "Listen"
|
||||
assert result["object"]["album"] == listen_activity.data["album"]
|
||||
assert result["object"]["artist"] == listen_activity.data["artist"]
|
||||
assert result["object"]["length"] == listen_activity.data["length"]
|
||||
assert result["object"]["title"] == listen_activity.data["title"]
|
||||
assert result["object"]["type"] == "Audio"
|
||||
assert result["@context"]
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.UserViewTest do
|
||||
use Pleroma.DataCase, async: true
|
||||
use Pleroma.DataCase, async: false
|
||||
import Pleroma.Factory
|
||||
|
||||
alias Pleroma.User
|
||||
|
|
@ -68,6 +68,23 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
|
|||
result = UserView.render("user.json", %{user: user})
|
||||
assert result["icon"]["url"] == "https://someurl"
|
||||
assert result["image"]["url"] == "https://somebanner"
|
||||
|
||||
refute result["icon"]["name"]
|
||||
refute result["image"]["name"]
|
||||
end
|
||||
|
||||
test "Avatar has a description if the user set one" do
|
||||
user =
|
||||
insert(:user,
|
||||
avatar: %{
|
||||
"url" => [%{"href" => "https://someurl"}],
|
||||
"name" => "a drawing of pleroma-tan using pleroma groups"
|
||||
}
|
||||
)
|
||||
|
||||
result = UserView.render("user.json", %{user: user})
|
||||
|
||||
assert result["icon"]["name"] == "a drawing of pleroma-tan using pleroma groups"
|
||||
end
|
||||
|
||||
test "renders an invisible user with the invisible property set to true" do
|
||||
|
|
@ -138,7 +155,7 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
|
|||
test "sets totalItems to zero when followers are hidden" do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
{:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user)
|
||||
{:ok, user, _other_user, _activity} = CommonAPI.follow(user, other_user)
|
||||
assert %{"totalItems" => 1} = UserView.render("followers.json", %{user: user})
|
||||
user = Map.merge(user, %{hide_followers_count: true, hide_followers: true})
|
||||
refute UserView.render("followers.json", %{user: user}) |> Map.has_key?("totalItems")
|
||||
|
|
@ -147,18 +164,30 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
|
|||
test "sets correct totalItems when followers are hidden but the follower counter is not" do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
{:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user)
|
||||
{:ok, user, _other_user, _activity} = CommonAPI.follow(user, other_user)
|
||||
assert %{"totalItems" => 1} = UserView.render("followers.json", %{user: user})
|
||||
user = Map.merge(user, %{hide_followers_count: false, hide_followers: true})
|
||||
assert %{"totalItems" => 1} = UserView.render("followers.json", %{user: user})
|
||||
end
|
||||
|
||||
test "does not hide follower items based on `hide_follows`" do
|
||||
user = insert(:user)
|
||||
follower = insert(:user)
|
||||
{:ok, user, _follower, _activity} = CommonAPI.follow(user, follower)
|
||||
|
||||
user = Map.merge(user, %{hide_followers: false, hide_follows: true})
|
||||
follower_ap_id = follower.ap_id
|
||||
|
||||
assert %{"first" => %{"orderedItems" => [^follower_ap_id]}} =
|
||||
UserView.render("followers.json", %{user: user})
|
||||
end
|
||||
end
|
||||
|
||||
describe "following" do
|
||||
test "sets totalItems to zero when follows are hidden" do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
{:ok, user, _other_user, _activity} = CommonAPI.follow(user, other_user)
|
||||
{:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user)
|
||||
assert %{"totalItems" => 1} = UserView.render("following.json", %{user: user})
|
||||
user = Map.merge(user, %{hide_follows_count: true, hide_follows: true})
|
||||
assert %{"totalItems" => 0} = UserView.render("following.json", %{user: user})
|
||||
|
|
@ -167,7 +196,7 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
|
|||
test "sets correct totalItems when follows are hidden but the follow counter is not" do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
{:ok, user, _other_user, _activity} = CommonAPI.follow(user, other_user)
|
||||
{:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user)
|
||||
assert %{"totalItems" => 1} = UserView.render("following.json", %{user: user})
|
||||
user = Map.merge(user, %{hide_follows_count: false, hide_follows: true})
|
||||
assert %{"totalItems" => 1} = UserView.render("following.json", %{user: user})
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ defmodule Pleroma.Web.ActivityPub.VisibilityTest do
|
|||
following = insert(:user)
|
||||
unrelated = insert(:user)
|
||||
{:ok, following, user} = Pleroma.User.follow(following, user)
|
||||
{:ok, list} = Pleroma.List.create("foo", user)
|
||||
{:ok, list} = Pleroma.List.create(%{title: "foo"}, user)
|
||||
|
||||
Pleroma.List.follow(list, unrelated)
|
||||
|
||||
|
|
|
|||
|
|
@ -321,6 +321,36 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||
"@#{admin.nickname} revoked admin role from @#{user_one.nickname}, @#{user_two.nickname}"
|
||||
end
|
||||
|
||||
test "/:right DELETE, admin cannot revoke their own admin status (single)", %{
|
||||
admin: admin,
|
||||
conn: conn
|
||||
} do
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("accept", "application/json")
|
||||
|> delete("/api/pleroma/admin/users/#{admin.nickname}/permission_group/admin")
|
||||
|
||||
assert json_response(conn, 403) == %{"error" => "You can't revoke your own admin status."}
|
||||
end
|
||||
|
||||
test "/:right DELETE, admin cannot revoke their own admin status (multiple)", %{
|
||||
admin: admin,
|
||||
conn: conn
|
||||
} do
|
||||
user = insert(:user, is_admin: true)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("accept", "application/json")
|
||||
|> delete("/api/pleroma/admin/users/permission_group/admin", %{
|
||||
nicknames: [admin.nickname, user.nickname]
|
||||
})
|
||||
|
||||
assert json_response(conn, 403) == %{
|
||||
"error" => "You can't revoke your own admin/moderator status."
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
describe "/api/pleroma/admin/users/:nickname/password_reset" do
|
||||
|
|
@ -1096,9 +1126,13 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
|
||||
ObanHelpers.perform_all()
|
||||
|
||||
email = Pleroma.Emails.UserEmail.backup_is_ready_email(backup, admin.id)
|
||||
email = Pleroma.Emails.UserEmail.backup_is_ready_email(backup)
|
||||
|
||||
assert String.contains?(
|
||||
email.html_body,
|
||||
"A full backup of your Pleroma account was requested"
|
||||
)
|
||||
|
||||
assert String.contains?(email.html_body, "Admin @#{admin.nickname} requested a full backup")
|
||||
assert_email_sent(to: {user.name, user.email}, html_body: email.html_body)
|
||||
|
||||
log_message = "@#{admin_nickname} requested account backup for @#{user_nickname}"
|
||||
|
|
|
|||
|
|
@ -194,6 +194,16 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do
|
|||
|
||||
setup do: clear_config(:configurable_from_database, true)
|
||||
|
||||
setup do:
|
||||
clear_config(:database_config_whitelist, [
|
||||
{:pleroma},
|
||||
{:http},
|
||||
{:idna},
|
||||
{:oban},
|
||||
{:tesla},
|
||||
{:ueberauth}
|
||||
])
|
||||
|
||||
test "create new config setting in db", %{conn: conn} do
|
||||
ueberauth = Application.get_env(:ueberauth, Ueberauth)
|
||||
on_exit(fn -> Application.put_env(:ueberauth, Ueberauth, ueberauth) end)
|
||||
|
|
@ -807,7 +817,7 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do
|
|||
%{
|
||||
"tuple" => [
|
||||
"/websocket",
|
||||
"Phoenix.Endpoint.CowboyWebSocket",
|
||||
":sth",
|
||||
%{
|
||||
"tuple" => [
|
||||
"Phoenix.Transports.WebSocket",
|
||||
|
|
@ -871,7 +881,7 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do
|
|||
%{
|
||||
"tuple" => [
|
||||
"/websocket",
|
||||
"Phoenix.Endpoint.CowboyWebSocket",
|
||||
":sth",
|
||||
%{
|
||||
"tuple" => [
|
||||
"Phoenix.Transports.WebSocket",
|
||||
|
|
@ -1210,9 +1220,32 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do
|
|||
assert Application.get_env(:not_real, :anything) == "value6"
|
||||
end
|
||||
|
||||
test "args for Pleroma.Upload.Filter.Mogrify with custom tuples", %{conn: conn} do
|
||||
clear_config(Pleroma.Upload.Filter.Mogrify)
|
||||
test "doesn't allow updating the database_config_whitelist itself", %{conn: conn} do
|
||||
original_whitelist = Pleroma.Config.get(:database_config_whitelist)
|
||||
|
||||
refute ConfigDB.get_by_group_and_key(:pleroma, :database_config_whitelist)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/api/pleroma/admin/config", %{
|
||||
configs: [
|
||||
%{
|
||||
group: ":pleroma",
|
||||
key: ":database_config_whitelist",
|
||||
value: [%{"tuple" => [":pleroma", ":key1"]}]
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
%{"configs" => configs} = json_response_and_validate_schema(conn, 200)
|
||||
|
||||
assert configs == []
|
||||
assert Pleroma.Config.get(:database_config_whitelist) == original_whitelist
|
||||
refute ConfigDB.get_by_group_and_key(:pleroma, :database_config_whitelist)
|
||||
end
|
||||
|
||||
test "args for Pleroma.Upload.Filter.Mogrify with custom tuples", %{conn: conn} do
|
||||
assert conn
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/api/pleroma/admin/config", %{
|
||||
|
|
@ -1240,7 +1273,8 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do
|
|||
"need_reboot" => false
|
||||
}
|
||||
|
||||
assert Config.get(Pleroma.Upload.Filter.Mogrify) == [args: ["auto-orient", "strip"]]
|
||||
config = Config.get(Pleroma.Upload.Filter.Mogrify)
|
||||
assert {:args, ["auto-orient", "strip"]} in config
|
||||
|
||||
assert conn
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|
|
@ -1289,9 +1323,9 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do
|
|||
"need_reboot" => false
|
||||
}
|
||||
|
||||
assert Config.get(Pleroma.Upload.Filter.Mogrify) == [
|
||||
args: ["auto-orient", "strip", {"implode", "1"}, {"resize", "3840x1080>"}]
|
||||
]
|
||||
config = Config.get(Pleroma.Upload.Filter.Mogrify)
|
||||
|
||||
assert {:args, ["auto-orient", "strip", {"implode", "1"}, {"resize", "3840x1080>"}]} in config
|
||||
end
|
||||
|
||||
test "enables the welcome messages", %{conn: conn} do
|
||||
|
|
@ -1473,5 +1507,13 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do
|
|||
web_endpoint = Enum.find(children, fn c -> c["key"] == "Pleroma.Upload" end)
|
||||
assert web_endpoint["children"]
|
||||
end
|
||||
|
||||
test "all keys from description are whitelisted", %{conn: conn} do
|
||||
conn = get(conn, "/api/pleroma/admin/config/descriptions")
|
||||
|
||||
assert response = json_response_and_validate_schema(conn, 200)
|
||||
|
||||
assert length(response) == length(Pleroma.Docs.JSON.compiled_descriptions())
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ defmodule Pleroma.Web.AdminAPI.FrontendControllerTest do
|
|||
|
||||
setup do
|
||||
clear_config([:instance, :static_dir], @dir)
|
||||
File.mkdir_p!(Pleroma.Frontend.dir())
|
||||
Pleroma.Backports.mkdir_p!(Pleroma.Frontend.dir())
|
||||
|
||||
on_exit(fn ->
|
||||
File.rm_rf(@dir)
|
||||
|
|
|
|||
|
|
@ -8,8 +8,6 @@ defmodule Pleroma.Web.AdminAPI.InstanceControllerTest do
|
|||
|
||||
import Pleroma.Factory
|
||||
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.Tests.ObanHelpers
|
||||
alias Pleroma.Web.CommonAPI
|
||||
|
||||
setup_all do
|
||||
|
|
@ -69,19 +67,19 @@ defmodule Pleroma.Web.AdminAPI.InstanceControllerTest do
|
|||
|
||||
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)
|
||||
insert(:user, nickname: "lain@lain.com")
|
||||
|
||||
response =
|
||||
conn
|
||||
|> delete("/api/pleroma/admin/instances/lain.com")
|
||||
|> json_response(200)
|
||||
|
||||
[:ok] = ObanHelpers.perform_all()
|
||||
|
||||
assert response == "lain.com"
|
||||
refute Repo.reload(user).is_active
|
||||
refute Repo.reload(post)
|
||||
|
||||
assert_enqueued(
|
||||
worker: Pleroma.Workers.DeleteWorker,
|
||||
args: %{"op" => "delete_instance", "host" => "lain.com"}
|
||||
)
|
||||
|
||||
clear_config([:instance, :admin_privileges], [])
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ defmodule Pleroma.Web.AdminAPI.InstanceDocumentControllerTest do
|
|||
@default_instance_panel ~s(<p>Welcome to <a href="https://pleroma.social" target="_blank">Pleroma!</a></p>)
|
||||
|
||||
setup do
|
||||
File.mkdir_p!(@dir)
|
||||
Pleroma.Backports.mkdir_p!(@dir)
|
||||
on_exit(fn -> File.rm_rf(@dir) end)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -57,6 +57,28 @@ defmodule Pleroma.Web.AdminAPI.OAuthAppControllerTest do
|
|||
} = response
|
||||
end
|
||||
|
||||
test "success with redirect_uris array", %{conn: conn} do
|
||||
base_url = Endpoint.url()
|
||||
app_name = "Trusted app"
|
||||
|
||||
response =
|
||||
conn
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/api/pleroma/admin/oauth_app", %{
|
||||
name: app_name,
|
||||
redirect_uris: [base_url]
|
||||
})
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
assert %{
|
||||
"client_id" => _,
|
||||
"client_secret" => _,
|
||||
"name" => ^app_name,
|
||||
"redirect_uri" => ^base_url,
|
||||
"trusted" => false
|
||||
} = response
|
||||
end
|
||||
|
||||
test "with trusted", %{conn: conn} do
|
||||
base_url = Endpoint.url()
|
||||
app_name = "Trusted app"
|
||||
|
|
|
|||
|
|
@ -388,6 +388,38 @@ defmodule Pleroma.Web.AdminAPI.ReportControllerTest do
|
|||
|> json_response_and_validate_schema(:ok)
|
||||
end
|
||||
|
||||
test "returns reports with specified assigned user", %{conn: conn, admin: admin} do
|
||||
[reporter, target_user] = insert_pair(:user)
|
||||
activity = insert(:note_activity, user: target_user)
|
||||
|
||||
{:ok, _report} =
|
||||
CommonAPI.report(reporter, %{
|
||||
account_id: target_user.id,
|
||||
comment: "I feel offended",
|
||||
status_ids: [activity.id]
|
||||
})
|
||||
|
||||
{:ok, %{id: second_report_id}} =
|
||||
CommonAPI.report(reporter, %{
|
||||
account_id: target_user.id,
|
||||
comment: "I don't like this user"
|
||||
})
|
||||
|
||||
CommonAPI.assign_report_to_account(second_report_id, admin.id)
|
||||
|
||||
response =
|
||||
conn
|
||||
|> get(report_path(conn, :index, %{assigned_account: admin.id}))
|
||||
|> json_response_and_validate_schema(:ok)
|
||||
|
||||
assert [open_report] = response["reports"]
|
||||
|
||||
assert length(response["reports"]) == 1
|
||||
assert open_report["id"] == second_report_id
|
||||
|
||||
assert response["total"] == 1
|
||||
end
|
||||
|
||||
test "renders content correctly", %{conn: conn} do
|
||||
[reporter, target_user] = insert_pair(:user)
|
||||
note = insert(:note, user: target_user, data: %{"content" => "mew 1"})
|
||||
|
|
@ -467,6 +499,66 @@ defmodule Pleroma.Web.AdminAPI.ReportControllerTest do
|
|||
end
|
||||
end
|
||||
|
||||
describe "POST /api/pleroma/admin/reports/assign_account" do
|
||||
test "assigns account to report", %{conn: conn, admin: admin} do
|
||||
[reporter, target_user] = insert_pair(:user)
|
||||
activity = insert(:note_activity, user: target_user)
|
||||
|
||||
{:ok, %{id: report_id}} =
|
||||
CommonAPI.report(reporter, %{
|
||||
account_id: target_user.id,
|
||||
status_ids: [activity.id]
|
||||
})
|
||||
|
||||
conn
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/api/pleroma/admin/reports/assign_account", %{
|
||||
"reports" => [
|
||||
%{"assigned_account" => admin.nickname, "id" => report_id}
|
||||
]
|
||||
})
|
||||
|> json_response_and_validate_schema(:no_content)
|
||||
|
||||
activity = Activity.get_by_id_with_user_actor(report_id)
|
||||
assert activity.data["assigned_account"] == admin.id
|
||||
|
||||
log_entry = Repo.one(ModerationLog)
|
||||
|
||||
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||
"@#{admin.nickname} assigned report ##{report_id} (on user @#{activity.user_actor.nickname}) to user #{admin.nickname}"
|
||||
end
|
||||
|
||||
test "unassigns account from report", %{conn: conn, admin: admin} do
|
||||
[reporter, target_user] = insert_pair(:user)
|
||||
activity = insert(:note_activity, user: target_user)
|
||||
|
||||
{:ok, %{id: report_id}} =
|
||||
CommonAPI.report(reporter, %{
|
||||
account_id: target_user.id,
|
||||
status_ids: [activity.id]
|
||||
})
|
||||
|
||||
CommonAPI.assign_report_to_account(report_id, admin.id)
|
||||
|
||||
conn
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/api/pleroma/admin/reports/assign_account", %{
|
||||
"reports" => [
|
||||
%{"assigned_account" => nil, "id" => report_id}
|
||||
]
|
||||
})
|
||||
|> json_response_and_validate_schema(:no_content)
|
||||
|
||||
activity = Activity.get_by_id_with_user_actor(report_id)
|
||||
assert activity.data["assigned_account"] == nil
|
||||
|
||||
log_entry = Repo.one(ModerationLog)
|
||||
|
||||
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||
"@#{admin.nickname} unassigned report ##{report_id} (on user @#{activity.user_actor.nickname}) from a user"
|
||||
end
|
||||
end
|
||||
|
||||
describe "POST /api/pleroma/admin/reports/:id/notes" do
|
||||
setup %{conn: conn, admin: admin} do
|
||||
clear_config([:instance, :admin_privileges], [:reports_manage_reports])
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do
|
|||
alias Pleroma.Web.MediaProxy
|
||||
|
||||
setup do
|
||||
Mox.stub_with(Pleroma.UnstubbedConfigMock, Pleroma.Config)
|
||||
Mox.stub_with(Pleroma.UnstubbedConfigMock, Pleroma.Test.StaticConfig)
|
||||
:ok
|
||||
end
|
||||
|
||||
|
|
@ -69,8 +69,8 @@ defmodule Pleroma.Web.AdminAPI.UserControllerTest do
|
|||
# Create some activities to check they got deleted later
|
||||
follower = insert(:user)
|
||||
{:ok, _} = CommonAPI.post(user, %{status: "test"})
|
||||
{:ok, _, _, _} = CommonAPI.follow(user, follower)
|
||||
{:ok, _, _, _} = CommonAPI.follow(follower, user)
|
||||
{:ok, _, _, _} = CommonAPI.follow(user, follower)
|
||||
user = Repo.get(User, user.id)
|
||||
assert user.note_count == 1
|
||||
assert user.follower_count == 1
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ defmodule Pleroma.Web.AdminAPI.ReportViewTest do
|
|||
}),
|
||||
AdminAPI.AccountView.render("show.json", %{user: other_user})
|
||||
),
|
||||
assigned_account: nil,
|
||||
statuses: [],
|
||||
notes: [],
|
||||
state: "open",
|
||||
|
|
@ -75,6 +76,7 @@ defmodule Pleroma.Web.AdminAPI.ReportViewTest do
|
|||
}),
|
||||
AdminAPI.AccountView.render("show.json", %{user: other_user})
|
||||
),
|
||||
assigned_account: nil,
|
||||
statuses: [StatusView.render("show.json", %{activity: activity})],
|
||||
state: "open",
|
||||
notes: [],
|
||||
|
|
|
|||
|
|
@ -20,14 +20,20 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraftTest do
|
|||
{: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})
|
||||
{:error, _} = ActivityDraft.create(user, %{status: "nice", quoted_status_id: direct.id})
|
||||
{:ok, _} = ActivityDraft.create(user, %{status: "nice", quoted_status_id: private.id})
|
||||
|
||||
{:error, _} =
|
||||
ActivityDraft.create(another_user, %{status: "nice", quoted_status_id: private.id})
|
||||
|
||||
{:ok, _} = ActivityDraft.create(user, %{status: "nice", quoted_status_id: unlisted.id})
|
||||
|
||||
{:ok, _} =
|
||||
ActivityDraft.create(another_user, %{status: "nice", quoted_status_id: unlisted.id})
|
||||
|
||||
{:ok, _} = ActivityDraft.create(user, %{status: "nice", quoted_status_id: local.id})
|
||||
{:ok, _} = ActivityDraft.create(another_user, %{status: "nice", quoted_status_id: local.id})
|
||||
{:ok, _} = ActivityDraft.create(user, %{status: "nice", quoted_status_id: public.id})
|
||||
{:ok, _} = ActivityDraft.create(another_user, %{status: "nice", quoted_status_id: public.id})
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -647,7 +647,7 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do
|
|||
describe "maybe_add_list_data/3" do
|
||||
test "adds list params when found user list" do
|
||||
user = insert(:user)
|
||||
{:ok, %Pleroma.List{} = list} = Pleroma.List.create("title", user)
|
||||
{:ok, %Pleroma.List{} = list} = Pleroma.List.create(%{title: "title"}, user)
|
||||
|
||||
assert Utils.maybe_add_list_data(%{additional: %{}, object: %{}}, user, {:list, list.id}) ==
|
||||
%{
|
||||
|
|
@ -658,7 +658,7 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do
|
|||
|
||||
test "returns original params when list not found" do
|
||||
user = insert(:user)
|
||||
{:ok, %Pleroma.List{} = list} = Pleroma.List.create("title", insert(:user))
|
||||
{:ok, %Pleroma.List{} = list} = Pleroma.List.create(%{title: "title"}, insert(:user))
|
||||
|
||||
assert Utils.maybe_add_list_data(%{additional: %{}, object: %{}}, user, {:list, list.id}) ==
|
||||
%{additional: %{}, object: %{}}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
alias Pleroma.Object
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.Rule
|
||||
alias Pleroma.Tests.ObanHelpers
|
||||
alias Pleroma.UnstubbedConfigMock, as: ConfigMock
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
|
|
@ -22,7 +23,7 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Workers.PollWorker
|
||||
|
||||
import Ecto.Query, only: [from: 2]
|
||||
import Ecto.Query, only: [from: 2, where: 3]
|
||||
import Mock
|
||||
import Mox
|
||||
import Pleroma.Factory
|
||||
|
|
@ -79,8 +80,8 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
setup do
|
||||
blocker = insert(:user)
|
||||
blocked = insert(:user, local: false)
|
||||
CommonAPI.follow(blocker, blocked)
|
||||
CommonAPI.follow(blocked, blocker)
|
||||
CommonAPI.follow(blocker, blocked)
|
||||
CommonAPI.accept_follow_request(blocker, blocked)
|
||||
CommonAPI.accept_follow_request(blocked, blocked)
|
||||
%{blocker: blocker, blocked: blocked}
|
||||
|
|
@ -94,7 +95,7 @@ defmodule Pleroma.Web.CommonAPITest 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 {:ok, block} = CommonAPI.block(blocked, blocker)
|
||||
|
||||
assert block.local
|
||||
assert User.blocks?(blocker, blocked)
|
||||
|
|
@ -110,6 +111,17 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
end
|
||||
end
|
||||
|
||||
test "add expiring block", %{blocker: blocker, blocked: blocked} do
|
||||
{:ok, _} = CommonAPI.block(blocked, blocker, %{expires_in: 60})
|
||||
assert User.blocks?(blocker, blocked)
|
||||
|
||||
worker = Pleroma.Workers.MuteExpireWorker
|
||||
args = %{"op" => "unblock_user", "blocker_id" => blocker.id, "blocked_id" => blocked.id}
|
||||
|
||||
assert :ok = perform_job(worker, args)
|
||||
refute User.blocks?(blocker, blocked)
|
||||
end
|
||||
|
||||
test "it blocks and does not federate if outgoing blocks are disabled", %{
|
||||
blocker: blocker,
|
||||
blocked: blocked
|
||||
|
|
@ -119,7 +131,7 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
|
||||
with_mock Pleroma.Web.Federator,
|
||||
publish: fn _ -> nil end do
|
||||
assert {:ok, block} = CommonAPI.block(blocker, blocked)
|
||||
assert {:ok, block} = CommonAPI.block(blocked, blocker)
|
||||
|
||||
assert block.local
|
||||
assert User.blocks?(blocker, blocked)
|
||||
|
|
@ -323,7 +335,7 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
User.block(blocker, blocked)
|
||||
|
||||
assert User.blocks?(blocker, blocked)
|
||||
assert {:ok, :no_activity} == CommonAPI.unblock(blocker, blocked)
|
||||
assert {:ok, :no_activity} == CommonAPI.unblock(blocked, blocker)
|
||||
refute User.blocks?(blocker, blocked)
|
||||
end
|
||||
end
|
||||
|
|
@ -453,7 +465,7 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
|
||||
users_serial
|
||||
|> Enum.map(fn user ->
|
||||
CommonAPI.favorite(user, activity.id)
|
||||
CommonAPI.favorite(activity.id, user)
|
||||
end)
|
||||
|
||||
object = Object.get_by_ap_id(activity.data["object"])
|
||||
|
|
@ -462,7 +474,7 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
users
|
||||
|> Enum.map(fn user ->
|
||||
Task.async(fn ->
|
||||
CommonAPI.favorite(user, activity.id)
|
||||
CommonAPI.favorite(activity.id, user)
|
||||
end)
|
||||
end)
|
||||
|> Enum.map(&Task.await/1)
|
||||
|
|
@ -747,7 +759,7 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
|
||||
test "it allows to address a list" do
|
||||
user = insert(:user)
|
||||
{:ok, list} = Pleroma.List.create("foo", user)
|
||||
{:ok, list} = Pleroma.List.create(%{title: "foo"}, user)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})
|
||||
|
||||
|
|
@ -818,7 +830,9 @@ defmodule Pleroma.Web.CommonAPITest 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})
|
||||
|
||||
{:ok, quote_post} =
|
||||
CommonAPI.post(user, %{status: "nice post", quoted_status_id: quoted.id})
|
||||
|
||||
quoted = Object.normalize(quoted)
|
||||
quote_post = Object.normalize(quote_post)
|
||||
|
|
@ -829,13 +843,25 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
refute quoted.data["actor"] in quote_post.data["to"]
|
||||
end
|
||||
|
||||
test "it supports fallback from `quote_id`" 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"]
|
||||
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: []})
|
||||
CommonAPI.post(user, %{status: "nice post", quoted_status_id: quoted.id, to: []})
|
||||
|
||||
assert Object.normalize(quote_post).data["to"] == [Pleroma.Constants.as_public()]
|
||||
end
|
||||
|
|
@ -850,15 +876,15 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
{: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})
|
||||
{:error, _} = CommonAPI.post(user, %{status: "nice", quoted_status_id: direct.id})
|
||||
{:ok, _} = CommonAPI.post(user, %{status: "nice", quoted_status_id: private.id})
|
||||
{:error, _} = CommonAPI.post(another_user, %{status: "nice", quoted_status_id: private.id})
|
||||
{:ok, _} = CommonAPI.post(user, %{status: "nice", quoted_status_id: unlisted.id})
|
||||
{:ok, _} = CommonAPI.post(another_user, %{status: "nice", quoted_status_id: unlisted.id})
|
||||
{:ok, _} = CommonAPI.post(user, %{status: "nice", quoted_status_id: local.id})
|
||||
{:ok, _} = CommonAPI.post(another_user, %{status: "nice", quoted_status_id: local.id})
|
||||
{:ok, _} = CommonAPI.post(user, %{status: "nice", quoted_status_id: public.id})
|
||||
{:ok, _} = CommonAPI.post(another_user, %{status: "nice", quoted_status_id: public.id})
|
||||
end
|
||||
|
||||
test "it properly mentions punycode domain" do
|
||||
|
|
@ -954,7 +980,7 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
test "author can repeat own private statuses" do
|
||||
author = insert(:user)
|
||||
follower = insert(:user)
|
||||
CommonAPI.follow(follower, author)
|
||||
CommonAPI.follow(author, follower)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(author, %{status: "cofe", visibility: "private"})
|
||||
|
||||
|
|
@ -973,7 +999,7 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
|
||||
{:ok, post_activity} = CommonAPI.post(other_user, %{status: "cofe"})
|
||||
|
||||
{:ok, %Activity{data: data}} = CommonAPI.favorite(user, post_activity.id)
|
||||
{:ok, %Activity{data: data}} = CommonAPI.favorite(post_activity.id, user)
|
||||
assert data["type"] == "Like"
|
||||
assert data["actor"] == user.ap_id
|
||||
assert data["object"] == post_activity.data["object"]
|
||||
|
|
@ -993,8 +1019,8 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
other_user = insert(:user)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
|
||||
{:ok, %Activity{}} = CommonAPI.favorite(user, activity.id)
|
||||
assert {:ok, :already_liked} = CommonAPI.favorite(user, activity.id)
|
||||
{:ok, %Activity{}} = CommonAPI.favorite(activity.id, user)
|
||||
assert {:ok, :already_liked} = CommonAPI.favorite(activity.id, user)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -1060,7 +1086,7 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
|
||||
test "only public can be pinned", %{user: user} do
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "private status", visibility: "private"})
|
||||
{:error, :visibility_error} = CommonAPI.pin(activity.id, user)
|
||||
{:error, :non_public_error} = CommonAPI.pin(activity.id, user)
|
||||
end
|
||||
|
||||
test "unpin status", %{user: user, activity: activity} do
|
||||
|
|
@ -1148,7 +1174,7 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
}
|
||||
)
|
||||
|
||||
{:ok, favorite_activity} = CommonAPI.favorite(friend2, activity.id)
|
||||
{:ok, favorite_activity} = CommonAPI.favorite(activity.id, friend2)
|
||||
{:ok, repeat_activity} = CommonAPI.repeat(activity.id, friend1)
|
||||
|
||||
assert Repo.aggregate(
|
||||
|
|
@ -1171,8 +1197,8 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
n.type == "mention" && n.activity_id == reply_activity.id
|
||||
end)
|
||||
|
||||
{:ok, _} = CommonAPI.add_mute(author, activity)
|
||||
assert CommonAPI.thread_muted?(author, activity)
|
||||
{:ok, _} = CommonAPI.add_mute(activity, author)
|
||||
assert CommonAPI.thread_muted?(activity, author)
|
||||
|
||||
assert Repo.aggregate(
|
||||
from(n in Notification, where: n.seen == false and n.user_id == ^friend1.id),
|
||||
|
|
@ -1196,13 +1222,13 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
end
|
||||
|
||||
test "add mute", %{user: user, activity: activity} do
|
||||
{:ok, _} = CommonAPI.add_mute(user, activity)
|
||||
assert CommonAPI.thread_muted?(user, activity)
|
||||
{:ok, _} = CommonAPI.add_mute(activity, user)
|
||||
assert CommonAPI.thread_muted?(activity, user)
|
||||
end
|
||||
|
||||
test "add expiring mute", %{user: user, activity: activity} do
|
||||
{:ok, _} = CommonAPI.add_mute(user, activity, %{expires_in: 60})
|
||||
assert CommonAPI.thread_muted?(user, activity)
|
||||
{:ok, _} = CommonAPI.add_mute(activity, user, %{expires_in: 60})
|
||||
assert CommonAPI.thread_muted?(activity, user)
|
||||
|
||||
worker = Pleroma.Workers.MuteExpireWorker
|
||||
args = %{"op" => "unmute_conversation", "user_id" => user.id, "activity_id" => activity.id}
|
||||
|
|
@ -1213,24 +1239,24 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
)
|
||||
|
||||
assert :ok = perform_job(worker, args)
|
||||
refute CommonAPI.thread_muted?(user, activity)
|
||||
refute CommonAPI.thread_muted?(activity, user)
|
||||
end
|
||||
|
||||
test "remove mute", %{user: user, activity: activity} do
|
||||
CommonAPI.add_mute(user, activity)
|
||||
{:ok, _} = CommonAPI.remove_mute(user, activity)
|
||||
refute CommonAPI.thread_muted?(user, activity)
|
||||
CommonAPI.add_mute(activity, user)
|
||||
{:ok, _} = CommonAPI.remove_mute(activity, user)
|
||||
refute CommonAPI.thread_muted?(activity, user)
|
||||
end
|
||||
|
||||
test "remove mute by ids", %{user: user, activity: activity} do
|
||||
CommonAPI.add_mute(user, activity)
|
||||
{:ok, _} = CommonAPI.remove_mute(user.id, activity.id)
|
||||
refute CommonAPI.thread_muted?(user, activity)
|
||||
CommonAPI.add_mute(activity, user)
|
||||
{:ok, _} = CommonAPI.remove_mute(activity.id, user.id)
|
||||
refute CommonAPI.thread_muted?(activity, user)
|
||||
end
|
||||
|
||||
test "check that mutes can't be duplicate", %{user: user, activity: activity} do
|
||||
CommonAPI.add_mute(user, activity)
|
||||
{:error, _} = CommonAPI.add_mute(user, activity)
|
||||
CommonAPI.add_mute(activity, user)
|
||||
{:error, _} = CommonAPI.add_mute(activity, user)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -1274,6 +1300,47 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
} = flag_activity
|
||||
end
|
||||
|
||||
test "doesn't create a report when post is not visible to user" do
|
||||
reporter = insert(:user)
|
||||
target_user = insert(:user)
|
||||
{:ok, post} = CommonAPI.post(target_user, %{status: "Eric", visibility: "private"})
|
||||
|
||||
assert Pleroma.Web.ActivityPub.Visibility.private?(post)
|
||||
refute Pleroma.Web.ActivityPub.Visibility.visible_for_user?(post, reporter)
|
||||
|
||||
# Fails when all status are invisible
|
||||
report_data = %{
|
||||
account_id: target_user.id,
|
||||
comment: "foobar",
|
||||
status_ids: [post.id]
|
||||
}
|
||||
|
||||
assert {:error, :visibility_error} = CommonAPI.report(reporter, report_data)
|
||||
end
|
||||
|
||||
test "doesn't create a report when some posts are not visible to user" do
|
||||
reporter = insert(:user)
|
||||
target_user = insert(:user)
|
||||
|
||||
{:ok, visible_activity} = CommonAPI.post(target_user, %{status: "cofe"})
|
||||
|
||||
{:ok, invisibile_activity} =
|
||||
CommonAPI.post(target_user, %{status: "cawfee", visibility: "private"})
|
||||
|
||||
assert Pleroma.Web.ActivityPub.Visibility.private?(invisibile_activity)
|
||||
assert Pleroma.Web.ActivityPub.Visibility.public?(visible_activity)
|
||||
refute Pleroma.Web.ActivityPub.Visibility.visible_for_user?(invisibile_activity, reporter)
|
||||
|
||||
# Fails when some statuses are invisible
|
||||
report_data_partial = %{
|
||||
account_id: target_user.id,
|
||||
comment: "foobar",
|
||||
status_ids: [visible_activity.id, invisibile_activity.id]
|
||||
}
|
||||
|
||||
assert {:error, :visibility_error} = CommonAPI.report(reporter, report_data_partial)
|
||||
end
|
||||
|
||||
test "updates report state" do
|
||||
[reporter, target_user] = insert_pair(:user)
|
||||
activity = insert(:note_activity, user: target_user)
|
||||
|
|
@ -1391,6 +1458,29 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
}
|
||||
} = flag_activity
|
||||
end
|
||||
|
||||
test "assigns report to an account" do
|
||||
[reporter, target_user] = insert_pair(:user)
|
||||
%{id: assigned} = insert(:user)
|
||||
|
||||
{:ok, %Activity{id: report_id}} = CommonAPI.report(reporter, %{account_id: target_user.id})
|
||||
|
||||
{:ok, activity} = CommonAPI.assign_report_to_account(report_id, assigned)
|
||||
|
||||
assert %{data: %{"assigned_account" => ^assigned}} = activity
|
||||
end
|
||||
|
||||
test "unassigns report from account" do
|
||||
[reporter, target_user] = insert_pair(:user)
|
||||
%{id: assigned} = insert(:user)
|
||||
|
||||
{:ok, %Activity{id: report_id}} = CommonAPI.report(reporter, %{account_id: target_user.id})
|
||||
|
||||
CommonAPI.assign_report_to_account(report_id, assigned)
|
||||
{:ok, activity} = CommonAPI.assign_report_to_account(report_id, nil)
|
||||
|
||||
refute Map.has_key?(activity.data, "assigned_account")
|
||||
end
|
||||
end
|
||||
|
||||
describe "reblog muting" do
|
||||
|
|
@ -1403,14 +1493,14 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
end
|
||||
|
||||
test "add a reblog mute", %{muter: muter, muted: muted} do
|
||||
{:ok, _reblog_mute} = CommonAPI.hide_reblogs(muter, muted)
|
||||
{:ok, _reblog_mute} = CommonAPI.hide_reblogs(muted, muter)
|
||||
|
||||
assert User.showing_reblogs?(muter, muted) == false
|
||||
end
|
||||
|
||||
test "remove a reblog mute", %{muter: muter, muted: muted} do
|
||||
{:ok, _reblog_mute} = CommonAPI.hide_reblogs(muter, muted)
|
||||
{:ok, _reblog_mute} = CommonAPI.show_reblogs(muter, muted)
|
||||
{:ok, _reblog_mute} = CommonAPI.hide_reblogs(muted, muter)
|
||||
{:ok, _reblog_mute} = CommonAPI.show_reblogs(muted, muter)
|
||||
|
||||
assert User.showing_reblogs?(muter, muted) == true
|
||||
end
|
||||
|
|
@ -1419,7 +1509,7 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
describe "follow/2" do
|
||||
test "directly follows a non-locked local user" do
|
||||
[follower, followed] = insert_pair(:user)
|
||||
{:ok, follower, followed, _} = CommonAPI.follow(follower, followed)
|
||||
{:ok, followed, follower, _} = CommonAPI.follow(followed, follower)
|
||||
|
||||
assert User.following?(follower, followed)
|
||||
end
|
||||
|
|
@ -1428,24 +1518,24 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
describe "unfollow/2" do
|
||||
test "also unsubscribes a user" do
|
||||
[follower, followed] = insert_pair(:user)
|
||||
{:ok, follower, followed, _} = CommonAPI.follow(follower, followed)
|
||||
{:ok, followed, follower, _} = CommonAPI.follow(followed, follower)
|
||||
{:ok, _subscription} = User.subscribe(follower, followed)
|
||||
|
||||
assert User.subscribed_to?(follower, followed)
|
||||
|
||||
{:ok, follower} = CommonAPI.unfollow(follower, followed)
|
||||
{:ok, follower} = CommonAPI.unfollow(followed, follower)
|
||||
|
||||
refute User.subscribed_to?(follower, followed)
|
||||
end
|
||||
|
||||
test "also unpins a user" do
|
||||
[follower, followed] = insert_pair(:user)
|
||||
{:ok, follower, followed, _} = CommonAPI.follow(follower, followed)
|
||||
{:ok, followed, follower, _} = CommonAPI.follow(followed, follower)
|
||||
{:ok, _endorsement} = User.endorse(follower, followed)
|
||||
|
||||
assert User.endorses?(follower, followed)
|
||||
|
||||
{:ok, follower} = CommonAPI.unfollow(follower, followed)
|
||||
{:ok, follower} = CommonAPI.unfollow(followed, follower)
|
||||
|
||||
refute User.endorses?(follower, followed)
|
||||
end
|
||||
|
|
@ -1454,11 +1544,11 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
follower = insert(:user)
|
||||
followed = insert(:user, is_locked: true)
|
||||
|
||||
assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} =
|
||||
CommonAPI.follow(follower, followed)
|
||||
assert {:ok, followed, follower, %{id: activity_id, data: %{"state" => "pending"}}} =
|
||||
CommonAPI.follow(followed, follower)
|
||||
|
||||
assert User.get_follow_state(follower, followed) == :follow_pending
|
||||
assert {:ok, follower} = CommonAPI.unfollow(follower, followed)
|
||||
assert {:ok, follower} = CommonAPI.unfollow(followed, follower)
|
||||
assert User.get_follow_state(follower, followed) == nil
|
||||
|
||||
assert %{id: ^activity_id, data: %{"state" => "cancelled"}} =
|
||||
|
|
@ -1476,11 +1566,11 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
follower = insert(:user)
|
||||
followed = insert(:user, is_locked: true, local: false)
|
||||
|
||||
assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} =
|
||||
CommonAPI.follow(follower, followed)
|
||||
assert {:ok, followed, follower, %{id: activity_id, data: %{"state" => "pending"}}} =
|
||||
CommonAPI.follow(followed, follower)
|
||||
|
||||
assert User.get_follow_state(follower, followed) == :follow_pending
|
||||
assert {:ok, follower} = CommonAPI.unfollow(follower, followed)
|
||||
assert {:ok, follower} = CommonAPI.unfollow(followed, follower)
|
||||
assert User.get_follow_state(follower, followed) == nil
|
||||
|
||||
assert %{id: ^activity_id, data: %{"state" => "cancelled"}} =
|
||||
|
|
@ -1501,9 +1591,9 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
follower = insert(:user)
|
||||
follower_two = insert(:user)
|
||||
|
||||
{:ok, _, _, follow_activity} = CommonAPI.follow(follower, user)
|
||||
{:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user)
|
||||
{:ok, _, _, follow_activity_three} = CommonAPI.follow(follower_two, user)
|
||||
{:ok, _, _, follow_activity} = CommonAPI.follow(user, follower)
|
||||
{:ok, _, _, follow_activity_two} = CommonAPI.follow(user, follower)
|
||||
{:ok, _, _, follow_activity_three} = CommonAPI.follow(user, follower_two)
|
||||
|
||||
assert follow_activity.data["state"] == "pending"
|
||||
assert follow_activity_two.data["state"] == "pending"
|
||||
|
|
@ -1521,9 +1611,9 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
follower = insert(:user)
|
||||
follower_two = insert(:user)
|
||||
|
||||
{:ok, _, _, follow_activity} = CommonAPI.follow(follower, user)
|
||||
{:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user)
|
||||
{:ok, _, _, follow_activity_three} = CommonAPI.follow(follower_two, user)
|
||||
{:ok, _, _, follow_activity} = CommonAPI.follow(user, follower)
|
||||
{:ok, _, _, follow_activity_two} = CommonAPI.follow(user, follower)
|
||||
{:ok, _, _, follow_activity_three} = CommonAPI.follow(user, follower_two)
|
||||
|
||||
assert follow_activity.data["state"] == "pending"
|
||||
assert follow_activity_two.data["state"] == "pending"
|
||||
|
|
@ -1558,9 +1648,9 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
|
||||
object = Object.normalize(activity, fetch: false)
|
||||
|
||||
{:ok, _, object} = CommonAPI.vote(other_user, object, [0])
|
||||
{:ok, _, object} = CommonAPI.vote(object, other_user, [0])
|
||||
|
||||
assert {:error, "Already voted"} == CommonAPI.vote(other_user, object, [1])
|
||||
assert {:error, "Already voted"} == CommonAPI.vote(object, other_user, [1])
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -1694,7 +1784,7 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
|
||||
with_mock Pleroma.Web.Federator, publish: fn _ -> :ok end do
|
||||
assert {:ok, %Activity{data: %{"type" => "Like"}} = activity} =
|
||||
CommonAPI.favorite(user, activity.id)
|
||||
CommonAPI.favorite(activity.id, user)
|
||||
|
||||
assert Visibility.local_public?(activity)
|
||||
refute called(Pleroma.Web.Federator.publish(activity))
|
||||
|
|
@ -1707,7 +1797,7 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
|
||||
{:ok, activity} = CommonAPI.post(other_user, %{status: "cofe", visibility: "local"})
|
||||
|
||||
{:ok, %Activity{}} = CommonAPI.favorite(user, activity.id)
|
||||
{:ok, %Activity{}} = CommonAPI.favorite(activity.id, user)
|
||||
|
||||
with_mock Pleroma.Web.Federator, publish: fn _ -> :ok end do
|
||||
assert {:ok, activity} = CommonAPI.unfavorite(activity.id, user)
|
||||
|
|
@ -1752,7 +1842,7 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
user = insert(:user)
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "foo1", spoiler_text: "title 1"})
|
||||
|
||||
{:ok, updated} = CommonAPI.update(user, activity, %{status: "updated 2"})
|
||||
{:ok, updated} = CommonAPI.update(activity, user, %{status: "updated 2"})
|
||||
|
||||
updated_object = Object.normalize(updated)
|
||||
assert updated_object.data["content"] == "updated 2"
|
||||
|
|
@ -1766,7 +1856,7 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
{:ok, activity} =
|
||||
CommonAPI.post(user, %{status: "foo1", spoiler_text: "title 1", visibility: "private"})
|
||||
|
||||
{:ok, updated} = CommonAPI.update(user, activity, %{status: "updated 2"})
|
||||
{:ok, updated} = CommonAPI.update(activity, user, %{status: "updated 2"})
|
||||
|
||||
updated_object = Object.normalize(updated)
|
||||
assert updated_object.data["content"] == "updated 2"
|
||||
|
|
@ -1783,7 +1873,7 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
{:ok, activity} =
|
||||
CommonAPI.post(user, %{status: "foo1", spoiler_text: "title 1 :#{emoji1}:"})
|
||||
|
||||
{:ok, updated} = CommonAPI.update(user, activity, %{status: "updated 2 :#{emoji2}:"})
|
||||
{:ok, updated} = CommonAPI.update(activity, user, %{status: "updated 2 :#{emoji2}:"})
|
||||
|
||||
updated_object = Object.normalize(updated)
|
||||
assert updated_object.data["content"] == "updated 2 :#{emoji2}:"
|
||||
|
|
@ -1802,7 +1892,7 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
|
||||
with_mock Pleroma.Web.Federator,
|
||||
publish: fn _p -> nil end do
|
||||
{:ok, updated} = CommonAPI.update(user, activity, %{status: "updated 2 :#{emoji2}:"})
|
||||
{:ok, updated} = CommonAPI.update(activity, user, %{status: "updated 2 :#{emoji2}:"})
|
||||
|
||||
assert updated.data["object"]["content"] == "updated 2 :#{emoji2}:"
|
||||
assert %{^emoji2 => _} = updated.data["object"]["emoji"]
|
||||
|
|
@ -1846,7 +1936,7 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
assert reply.object.data["emoji"]["remoteemoji"] == remote_emoji_uri
|
||||
|
||||
{:ok, edit} =
|
||||
CommonAPI.update(user, reply, %{status: "reply mew mew", spoiler_text: ":remoteemoji:"})
|
||||
CommonAPI.update(reply, user, %{status: "reply mew mew", spoiler_text: ":remoteemoji:"})
|
||||
|
||||
edited_note = Pleroma.Object.normalize(edit)
|
||||
|
||||
|
|
@ -1862,7 +1952,7 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
{: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"})
|
||||
{:ok, updated} = CommonAPI.update(activity, user, %{status: "updated 2"})
|
||||
|
||||
updated_object = Object.normalize(updated)
|
||||
assert updated_object.data["content"] == "mewmew 2"
|
||||
|
|
@ -1913,11 +2003,224 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
end
|
||||
|
||||
test "it does not boost if group is blocking poster", %{poster: poster, group: group} do
|
||||
{:ok, _} = CommonAPI.block(group, poster)
|
||||
{:ok, _} = CommonAPI.block(poster, group)
|
||||
{:ok, post} = CommonAPI.post(poster, %{status: "hey @#{group.nickname}"})
|
||||
|
||||
announces = get_announces_of_object(post.object)
|
||||
assert [] = announces
|
||||
end
|
||||
end
|
||||
|
||||
describe "Oban jobs are cancelled" do
|
||||
setup do
|
||||
clear_config([:instance, :federating], true)
|
||||
|
||||
local_user = insert(:user)
|
||||
|
||||
remote_one =
|
||||
insert(:user, %{
|
||||
local: false,
|
||||
nickname: "nick1@domain.com",
|
||||
ap_id: "https://domain.com/users/nick1",
|
||||
inbox: "https://domain.com/users/nick1/inbox",
|
||||
shared_inbox: "https://domain.com/inbox"
|
||||
})
|
||||
|
||||
remote_two =
|
||||
insert(:user, %{
|
||||
local: false,
|
||||
nickname: "nick2@example.com",
|
||||
ap_id: "https://example.com/users/nick2",
|
||||
inbox: "https://example.com/users/nick2/inbox",
|
||||
shared_inbox: "https://example.com/inbox"
|
||||
})
|
||||
|
||||
%{local_user: local_user, remote_one: remote_one, remote_two: remote_two}
|
||||
end
|
||||
|
||||
test "when deleting posts", %{
|
||||
local_user: local_user,
|
||||
remote_one: remote_one,
|
||||
remote_two: remote_two
|
||||
} do
|
||||
{:ok, _, _} = Pleroma.User.follow(remote_one, local_user)
|
||||
{:ok, _, _} = Pleroma.User.follow(remote_two, local_user)
|
||||
|
||||
{:ok, %{id: activity_id} = _activity} =
|
||||
CommonAPI.post(local_user, %{status: "Happy Friday everyone!"})
|
||||
|
||||
# Generate the publish_one jobs
|
||||
ObanHelpers.perform_all()
|
||||
|
||||
publish_one_jobs =
|
||||
all_enqueued()
|
||||
|> Enum.filter(fn job ->
|
||||
match?(
|
||||
%{
|
||||
state: "available",
|
||||
queue: "federator_outgoing",
|
||||
worker: "Pleroma.Workers.PublisherWorker",
|
||||
args: %{"op" => "publish_one", "params" => %{"activity_id" => ^activity_id}}
|
||||
},
|
||||
job
|
||||
)
|
||||
end)
|
||||
|
||||
assert length(publish_one_jobs) == 2
|
||||
|
||||
# The delete should have triggered cancelling the publish_one jobs
|
||||
assert {:ok, _delete} = CommonAPI.delete(activity_id, local_user)
|
||||
|
||||
# all_enqueued/1 will not return cancelled jobs
|
||||
cancelled_jobs =
|
||||
Oban.Job
|
||||
|> where([j], j.worker == "Pleroma.Workers.PublisherWorker")
|
||||
|> where([j], j.state == "cancelled")
|
||||
|> where([j], j.args["op"] == "publish_one")
|
||||
|> where([j], j.args["params"]["activity_id"] == ^activity_id)
|
||||
|> Pleroma.Repo.all()
|
||||
|
||||
assert length(cancelled_jobs) == 2
|
||||
end
|
||||
|
||||
test "when unfavoriting posts", %{
|
||||
local_user: local_user,
|
||||
remote_one: remote_user
|
||||
} do
|
||||
{:ok, activity} =
|
||||
CommonAPI.post(remote_user, %{status: "I like turtles!"})
|
||||
|
||||
{:ok, %{id: favorite_id} = _favorite} =
|
||||
CommonAPI.favorite(activity.id, local_user)
|
||||
|
||||
# Generate the publish_one jobs
|
||||
ObanHelpers.perform_all()
|
||||
|
||||
publish_one_jobs =
|
||||
all_enqueued()
|
||||
|> Enum.filter(fn job ->
|
||||
match?(
|
||||
%{
|
||||
state: "available",
|
||||
queue: "federator_outgoing",
|
||||
worker: "Pleroma.Workers.PublisherWorker",
|
||||
args: %{"op" => "publish_one", "params" => %{"activity_id" => ^favorite_id}}
|
||||
},
|
||||
job
|
||||
)
|
||||
end)
|
||||
|
||||
assert length(publish_one_jobs) == 1
|
||||
|
||||
# The unfavorite should have triggered cancelling the publish_one jobs
|
||||
assert {:ok, _unfavorite} = CommonAPI.unfavorite(activity.id, local_user)
|
||||
|
||||
# all_enqueued/1 will not return cancelled jobs
|
||||
cancelled_jobs =
|
||||
Oban.Job
|
||||
|> where([j], j.worker == "Pleroma.Workers.PublisherWorker")
|
||||
|> where([j], j.state == "cancelled")
|
||||
|> where([j], j.args["op"] == "publish_one")
|
||||
|> where([j], j.args["params"]["activity_id"] == ^favorite_id)
|
||||
|> Pleroma.Repo.all()
|
||||
|
||||
assert length(cancelled_jobs) == 1
|
||||
end
|
||||
|
||||
test "when unboosting posts", %{
|
||||
local_user: local_user,
|
||||
remote_one: remote_one,
|
||||
remote_two: remote_two
|
||||
} do
|
||||
{:ok, _, _} = Pleroma.User.follow(remote_one, local_user)
|
||||
{:ok, _, _} = Pleroma.User.follow(remote_two, local_user)
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.post(remote_one, %{status: "This is an unpleasant post"})
|
||||
|
||||
{:ok, %{id: repeat_id} = _repeat} =
|
||||
CommonAPI.repeat(activity.id, local_user)
|
||||
|
||||
# Generate the publish_one jobs
|
||||
ObanHelpers.perform_all()
|
||||
|
||||
publish_one_jobs =
|
||||
all_enqueued()
|
||||
|> Enum.filter(fn job ->
|
||||
match?(
|
||||
%{
|
||||
state: "available",
|
||||
queue: "federator_outgoing",
|
||||
worker: "Pleroma.Workers.PublisherWorker",
|
||||
args: %{"op" => "publish_one", "params" => %{"activity_id" => ^repeat_id}}
|
||||
},
|
||||
job
|
||||
)
|
||||
end)
|
||||
|
||||
assert length(publish_one_jobs) == 2
|
||||
|
||||
# The unrepeat should have triggered cancelling the publish_one jobs
|
||||
assert {:ok, _unfavorite} = CommonAPI.unrepeat(activity.id, local_user)
|
||||
|
||||
# all_enqueued/1 will not return cancelled jobs
|
||||
cancelled_jobs =
|
||||
Oban.Job
|
||||
|> where([j], j.worker == "Pleroma.Workers.PublisherWorker")
|
||||
|> where([j], j.state == "cancelled")
|
||||
|> where([j], j.args["op"] == "publish_one")
|
||||
|> where([j], j.args["params"]["activity_id"] == ^repeat_id)
|
||||
|> Pleroma.Repo.all()
|
||||
|
||||
assert length(cancelled_jobs) == 2
|
||||
end
|
||||
|
||||
test "when unreacting to posts", %{
|
||||
local_user: local_user,
|
||||
remote_one: remote_one,
|
||||
remote_two: remote_two
|
||||
} do
|
||||
{:ok, _, _} = Pleroma.User.follow(remote_one, local_user)
|
||||
{:ok, _, _} = Pleroma.User.follow(remote_two, local_user)
|
||||
|
||||
{:ok, %{id: activity_id}} =
|
||||
CommonAPI.post(remote_one, %{status: "Gang gang!!!!"})
|
||||
|
||||
{:ok, %{id: react_id} = _react} =
|
||||
CommonAPI.react_with_emoji(activity_id, local_user, "👍")
|
||||
|
||||
# Generate the publish_one jobs
|
||||
ObanHelpers.perform_all()
|
||||
|
||||
publish_one_jobs =
|
||||
all_enqueued()
|
||||
|> Enum.filter(fn job ->
|
||||
match?(
|
||||
%{
|
||||
state: "available",
|
||||
queue: "federator_outgoing",
|
||||
worker: "Pleroma.Workers.PublisherWorker",
|
||||
args: %{"op" => "publish_one", "params" => %{"activity_id" => ^react_id}}
|
||||
},
|
||||
job
|
||||
)
|
||||
end)
|
||||
|
||||
assert length(publish_one_jobs) == 2
|
||||
|
||||
# The unreact should have triggered cancelling the publish_one jobs
|
||||
assert {:ok, _unreact} = CommonAPI.unreact_with_emoji(activity_id, local_user, "👍")
|
||||
|
||||
# all_enqueued/1 will not return cancelled jobs
|
||||
cancelled_jobs =
|
||||
Oban.Job
|
||||
|> where([j], j.worker == "Pleroma.Workers.PublisherWorker")
|
||||
|> where([j], j.state == "cancelled")
|
||||
|> where([j], j.args["op"] == "publish_one")
|
||||
|> where([j], j.args["params"]["activity_id"] == ^react_id)
|
||||
|> Pleroma.Repo.all()
|
||||
|
||||
assert length(cancelled_jobs) == 2
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ defmodule Pleroma.Web.FallbackTest do
|
|||
resp = get(conn, "/foo")
|
||||
|
||||
assert html_response(resp, 200) =~ "<title>a cool title</title>"
|
||||
refute html_response(resp, 200) =~ "initial-results"
|
||||
assert html_response(resp, 200) =~ "<meta content=\"noindex, noarchive\" name=\"robots\">"
|
||||
end
|
||||
|
||||
test "GET /*path", %{conn: conn} do
|
||||
|
|
@ -77,6 +77,10 @@ defmodule Pleroma.Web.FallbackTest do
|
|||
assert redirected_to(get(conn, "/pleroma/admin")) =~ "/pleroma/admin/"
|
||||
end
|
||||
|
||||
test "GET /phoenix/live_dashboard -> /pleroma/live_dashboard", %{conn: conn} do
|
||||
assert redirected_to(get(conn, "/phoenix/live_dashboard")) =~ "/pleroma/live_dashboard"
|
||||
end
|
||||
|
||||
test "OPTIONS /*path", %{conn: conn} do
|
||||
assert conn
|
||||
|> options("/foo")
|
||||
|
|
|
|||
|
|
@ -126,22 +126,17 @@ defmodule Pleroma.Web.FederatorTest do
|
|||
inbox: inbox2
|
||||
})
|
||||
|
||||
dt = NaiveDateTime.utc_now()
|
||||
Instances.set_unreachable(inbox1, dt)
|
||||
|
||||
Instances.set_consistently_unreachable(URI.parse(inbox2).host)
|
||||
Instances.set_unreachable(URI.parse(inbox2).host)
|
||||
|
||||
{:ok, _activity} =
|
||||
CommonAPI.post(user, %{status: "HI @nick1@domain.com, @nick2@domain2.com!"})
|
||||
|
||||
expected_dt = NaiveDateTime.to_iso8601(dt)
|
||||
|
||||
ObanHelpers.perform(all_enqueued(worker: PublisherWorker))
|
||||
|
||||
assert ObanHelpers.member?(
|
||||
%{
|
||||
"op" => "publish_one",
|
||||
"params" => %{"inbox" => inbox1, "unreachable_since" => expected_dt}
|
||||
"params" => %{"inbox" => inbox1}
|
||||
},
|
||||
all_enqueued(worker: PublisherWorker)
|
||||
)
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue