Merge branch 'develop' into 'endorsements-api'

# Conflicts:
#   test/pleroma/web/pleroma_api/controllers/account_controller_test.exs
This commit is contained in:
nicole mikołajczyk 2025-10-08 05:04:55 +02:00
commit 5ce3c12c28
641 changed files with 5697 additions and 1718 deletions

View file

@ -0,0 +1,76 @@
{
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
{
"Hashtag": "as:Hashtag",
"PropertyValue": "schema:PropertyValue",
"conversation": "ostatus:conversation",
"dfrn": "http://purl.org/macgirvin/dfrn/1.0/",
"diaspora": "https://diasporafoundation.org/ns/",
"directMessage": "litepub:directMessage",
"discoverable": "toot:discoverable",
"featured": {
"@id": "toot:featured",
"@type": "@id"
},
"litepub": "http://litepub.social/ns#",
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
"ostatus": "http://ostatus.org#",
"quoteUrl": "as:quoteUrl",
"schema": "http://schema.org#",
"sensitive": "as:sensitive",
"toot": "http://joinmastodon.org/ns#",
"value": "schema:value",
"vcard": "http://www.w3.org/2006/vcard/ns#"
}
],
"actor": "https://my-place.social/profile/vaartis",
"cc": [
"https://my-place.social/followers/vaartis"
],
"id": "https://my-place.social/objects/e599373b-1368-4b20-cd24-837166957182/Undo",
"instrument": {
"id": "https://my-place.social/friendica",
"name": "Friendica 'Interrupted Fern' 2024.12-1576",
"type": "Application",
"url": "https://my-place.social"
},
"object": {
"actor": "https://my-place.social/profile/vaartis",
"cc": [
"https://my-place.social/followers/vaartis"
],
"diaspora:guid": "e599373b-1968-4b20-cd24-80d340160302",
"diaspora:like": "{\"author\":\"vaartis@my-place.social\",\"guid\":\"e599373b-1968-4b20-cd24-80d340160302\",\"parent_guid\":\"cd36feba-c31f3ed3fd5c064a-17c31593\",\"parent_type\":\"Post\",\"positive\":\"false\",\"author_signature\":\"xR2zLJNfc9Nhx1n8LLMWM1kde12my4cqamIsrH\\/UntKzuDwO4DuHBL0fkFhgC\\/dylxm4HqsHD45MQbtwQCVGq6WhC96TrbMuYEK61HIO23dTr3m+qJVtfdH4wyhUNHgiiYPhZpkLDfnR1JiRWmFTlmZC8q8JEkOB5IQsrWia2eOR6IsqDcdKO\\/Urgns9\\/BdQi8KnchBKSEFc1iUtcOEruvhunKGyW5zI\\/Rltfdz3xGH8tlw+YlMXeWXPnqgOJ9GzNA0lwG4U421L6yylYagW7oxIznnBLB4bO46vYZbgXZV1hiI9ZyveHOinLMY1QkmTj5CNvnx3\\/VJwLovd0v+0Nr2vu\\/3ftbpBXc6L1bsNjlRqtsfwJlcgl+tH1DC4W8tKf+Y3tdtzVw0CHXCuacxHLyq5wZd\\/5YfYR9SJQ\\/jInU4PHA5+hIE3PGqNUp5QfFE0umq56H7MQKsIPgM5mMV4fPAA8OpltuMVDvQYUxalrnvoTf00k90x1wCTK71\\/jQGh7r7PmGvSdfPr+65eVTjITD8\\/lTGIb8850v1fl3\\/i2R8Dv17jQIRyX5o9MXPSO6jHo4Swma5RzPA\\/0bRj6qRTyPkM1L9qEIr+2H2I7KKhT2ZE5GhAU7yI9A3VLBWzpTrUPMGbfpd1OjVTEqXAdMjpLDYI3Mh5zQ58p8xCzt+W+t0=\"}",
"id": "https://my-place.social/objects/e599373b-1368-4b20-cd24-837166957182",
"instrument": {
"id": "https://my-place.social/friendica",
"name": "Friendica 'Interrupted Fern' 2024.12-1576",
"type": "Application",
"url": "https://my-place.social"
},
"object": "https://pl.kotobank.ch/objects/301bce65-8a1b-4c49-a65c-fe2ce861a213",
"published": "2025-06-12T18:47:41Z",
"to": [
"https://pl.kotobank.ch/users/vaartis",
"https://mitra.social/users/silverpill",
"https://www.w3.org/ns/activitystreams#Public"
],
"type": "Dislike"
},
"published": "2025-06-12T18:41:25Z",
"signature": {
"created": "2025-06-12T18:44:16Z",
"creator": "https://my-place.social/profile/vaartis#main-key",
"nonce": "2d67847d4bd4b1b83a30d61eac6cdc7ad6b980df06a8b9b97217e1d8f7b6cf20",
"signatureValue": "LnoRMZuQGDvTICkShGBq28ynaj2lF1bViJFGS6n4gKn3IbxPWATHxao43gxWRc+HCTrHNg7quzgaW4+PYM7UVUz3jO+bjNKsN845nijOVdyFrPOXbuaij3KQh2OoHhFJWoV/ZQQTFF0kRK1qT4BwG+P8NqOOKAMv+Cw7ruQH+f2w7uDgcNIbCD1gLcwb6cw7WVe5qu8yMkKqp2kBdqW3RCsI85RmmFgwehDgH5nrX7ER1qbeLWrqy7echwD9/fO3rqAu13xDNyiGZHDT7JB3RUt0AyMm0XCfjbwSQ0n+MkYXgE4asvFz81+iiPCLt+6gePWAFc5odF1FxdySBpSuUOs4p92NzP9OhQ0c0qrqrzYI7aYklY7oMfxjkva+X+0bm3up+2IRJdnZa/pXlmwdcqTpyMr1sgzaexMUNBp3dq7zA51eEaakLDX3i2onXJowfmze3+6XgPAFHYamR+pRNtuEoY4uyYEK3fj5GgwJ4RtFJMYVoEs/Q8h3OgYRcK1FE9UlDjSqbQ7QIRn2Ib4wjgmkeM0vrHIwh/1CtqA/M/6WuYFzCaJBc8O9ykpK9ZMbw64ToQXKf2SqhZsDoyTWRWTO1PXOk1XCAAElUh8/WCyeghvgqLXn0LHov4lmBsHA5iMUcLqBKD3GJIHd+ExrOFxMZs4mBLLGyz0p5joJ3NY=",
"type": "RsaSignature2017"
},
"to": [
"https://pl.kotobank.ch/users/vaartis",
"https://mitra.social/users/silverpill",
"https://www.w3.org/ns/activitystreams#Public"
],
"type": "Undo"
}

56
test/fixtures/friendica-dislike.json vendored Normal file
View file

@ -0,0 +1,56 @@
{
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
{
"Hashtag": "as:Hashtag",
"PropertyValue": "schema:PropertyValue",
"conversation": "ostatus:conversation",
"dfrn": "http://purl.org/macgirvin/dfrn/1.0/",
"diaspora": "https://diasporafoundation.org/ns/",
"directMessage": "litepub:directMessage",
"discoverable": "toot:discoverable",
"featured": {
"@id": "toot:featured",
"@type": "@id"
},
"litepub": "http://litepub.social/ns#",
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
"ostatus": "http://ostatus.org#",
"quoteUrl": "as:quoteUrl",
"schema": "http://schema.org#",
"sensitive": "as:sensitive",
"toot": "http://joinmastodon.org/ns#",
"value": "schema:value",
"vcard": "http://www.w3.org/2006/vcard/ns#"
}
],
"actor": "https://my-place.social/profile/vaartis",
"cc": [
"https://my-place.social/followers/vaartis"
],
"diaspora:guid": "e599373b-1968-4b20-cd24-80d340160302",
"diaspora:like": "{\"author\":\"vaartis@my-place.social\",\"guid\":\"e599373b-1968-4b20-cd24-80d340160302\",\"parent_guid\":\"cd36feba-c31f3ed3fd5c064a-17c31593\",\"parent_type\":\"Post\",\"positive\":\"false\",\"author_signature\":\"xR2zLJNfc9Nhx1n8LLMWM1kde12my4cqamIsrH\\/UntKzuDwO4DuHBL0fkFhgC\\/dylxm4HqsHD45MQbtwQCVGq6WhC96TrbMuYEK61HIO23dTr3m+qJVtfdH4wyhUNHgiiYPhZpkLDfnR1JiRWmFTlmZC8q8JEkOB5IQsrWia2eOR6IsqDcdKO\\/Urgns9\\/BdQi8KnchBKSEFc1iUtcOEruvhunKGyW5zI\\/Rltfdz3xGH8tlw+YlMXeWXPnqgOJ9GzNA0lwG4U421L6yylYagW7oxIznnBLB4bO46vYZbgXZV1hiI9ZyveHOinLMY1QkmTj5CNvnx3\\/VJwLovd0v+0Nr2vu\\/3ftbpBXc6L1bsNjlRqtsfwJlcgl+tH1DC4W8tKf+Y3tdtzVw0CHXCuacxHLyq5wZd\\/5YfYR9SJQ\\/jInU4PHA5+hIE3PGqNUp5QfFE0umq56H7MQKsIPgM5mMV4fPAA8OpltuMVDvQYUxalrnvoTf00k90x1wCTK71\\/jQGh7r7PmGvSdfPr+65eVTjITD8\\/lTGIb8850v1fl3\\/i2R8Dv17jQIRyX5o9MXPSO6jHo4Swma5RzPA\\/0bRj6qRTyPkM1L9qEIr+2H2I7KKhT2ZE5GhAU7yI9A3VLBWzpTrUPMGbfpd1OjVTEqXAdMjpLDYI3Mh5zQ58p8xCzt+W+t0=\"}",
"id": "https://my-place.social/objects/e599373b-1368-4b20-cd24-837166957182",
"instrument": {
"id": "https://my-place.social/friendica",
"name": "Friendica 'Interrupted Fern' 2024.12-1576",
"type": "Application",
"url": "https://my-place.social"
},
"object": "https://pl.kotobank.ch/objects/301bce65-8a1b-4c49-a65c-fe2ce861a213",
"published": "2025-06-12T18:47:41Z",
"signature": {
"created": "2025-06-12T18:47:42Z",
"creator": "https://my-place.social/profile/vaartis#main-key",
"nonce": "84e496f80b09d7a299c5cc89e8cadd13abf621b3a0a321684fa74278b68a6dd8",
"signatureValue": "qe2WxY+j7daIYLRadCctgal6A1s9XgoiMfM/8KjJm15w0sSizYYqruyQ5gS44e+cj5GHc9v5gP2ieod5v7eHAPzlcDI4bfkcyHVapAXTqU67ZebW+v6Q+21IMDgqrkYCv5TbV7LTxltW59dlqovpHE4TEe/M7xLKWJ3vVchRUcWqH9kDmak0nacoqYVAb5E9jYnQhUWPTCfPV82qQpeWQPOZ4iIvPw6rDkSSY5jL6bCogBZblHGpUjXfe/FPlacaCWiTQdoga3yOBXB1RYPw9nh5FI5Xkv/oi+52WmJrECinlD6AL8/BpiYvKz236zy7p/TR4BXlCx9RR/msjOnSabkQ4kmYFrRr80UDCGF+CdkdzLl8K9rSE3PbF1+nEqD7X0GOWn/DdtixqXJw6IR4bh32YW2SlcrSRBvI1p82Mv68BeqRaYqL6FAhKFwLhX5JpXngZ3k0g7rWWxc498voPWnFZDyCTRNxO9VIIUavDDEQ0BdFk6WDb8zx9tsAg8JoK57eVDcFly7tfVQffYiHpve06d8ag1DtzipqguRsURmuqpGNMq28XBTxwtrP2LnXXHKxoYN/YQ9cDnCKclbx7/uKmOVMLkLZlM0wAVoZpm5z2fG4voKqFiGZ1PoiFY2sN4URMArJtygV3PlTX4ASAQrak0ksvEo9msrBUD0Su9c=",
"type": "RsaSignature2017"
},
"to": [
"https://pl.kotobank.ch/users/vaartis",
"https://mitra.social/users/silverpill",
"https://www.w3.org/ns/activitystreams#Public"
],
"type": "Dislike"
}

View file

@ -1 +0,0 @@
21.1

View file

@ -1 +0,0 @@
22.1

View file

@ -1 +0,0 @@
22.4

View file

@ -1 +0,0 @@
23.0

View file

@ -42,9 +42,10 @@ defmodule Mix.Tasks.Pleroma.AppTest do
test "with errors" do
Mix.Tasks.Pleroma.App.run(["create"])
{:mix_shell, :error, ["Creating failed:"]}
{:mix_shell, :error, ["name: can't be blank"]}
{:mix_shell, :error, ["redirect_uris: can't be blank"]}
assert_receive {:mix_shell, :error, ["Creating failed:"]}
assert_receive {:mix_shell, :error, ["name: can't be blank"]}
assert_receive {:mix_shell, :error, ["redirect_uris: can't be blank"]}
end
defp assert_app(name, redirect, scopes) do

View file

@ -11,7 +11,7 @@ defmodule Mix.Tasks.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 ->
@ -50,7 +50,7 @@ defmodule Mix.Tasks.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)

View file

@ -7,7 +7,7 @@ defmodule Mix.Tasks.Pleroma.InstanceTest do
use Pleroma.DataCase
setup do
File.mkdir_p!(tmp_path())
Pleroma.Backports.mkdir_p!(tmp_path())
on_exit(fn ->
File.rm_rf(tmp_path())

View file

@ -62,7 +62,7 @@ defmodule Mix.Tasks.Pleroma.UploadsTest do
upload_dir = Config.get([Pleroma.Uploaders.Local, :uploads])
if not File.exists?(upload_dir) || File.ls!(upload_dir) == [] do
File.mkdir_p(upload_dir)
Pleroma.Backports.mkdir_p(upload_dir)
Path.join([upload_dir, "file.txt"])
|> File.touch()

View file

@ -13,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)
@ -58,7 +61,7 @@ defmodule Pleroma.Emoji.PackTest do
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")
File.mkdir_p(test_pack_path)
Pleroma.Backports.mkdir_p(test_pack_path)
# Create a pack.json file
File.write!(Path.join(test_pack_path, "pack.json"), """

View file

@ -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)

View file

@ -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

View file

@ -25,6 +25,9 @@ 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"}
end)
:ok
@ -67,4 +70,20 @@ 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
end
end

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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
@ -250,17 +249,6 @@ defmodule Pleroma.Object.FetcherTest do
result = Fetcher.fetch_object_from_id("https://example.com/objects/no-content-type")
assert {:fetch, {:error, nil}} = result
end
test "it resets instance reachability on successful fetch" do
id = "http://mastodon.example.org/@admin/99541947525187367"
Instances.set_consistently_unreachable(id)
refute Instances.reachable?(id)
{:ok, _object} =
Fetcher.fetch_object_from_id("http://mastodon.example.org/@admin/99541947525187367")
assert Instances.reachable?(id)
end
end
describe "implementation quirks" do

View file

@ -156,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",

View file

@ -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

View file

@ -395,4 +395,40 @@ defmodule Pleroma.ReverseProxyTest do
assert Conn.get_resp_header(conn, "content-type") == ["application/octet-stream"]
end
end
# Hackey 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!"}
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
end
end

View file

@ -9,12 +9,12 @@ defmodule Pleroma.SafeZipTest do
setup do
# Ensure tmp directory exists
File.mkdir_p!(@tmp_dir)
Pleroma.Backports.mkdir_p!(@tmp_dir)
on_exit(fn ->
# Clean up any files created during tests
File.rm_rf!(@tmp_dir)
File.mkdir_p!(@tmp_dir)
Pleroma.Backports.mkdir_p!(@tmp_dir)
end)
:ok
@ -89,7 +89,7 @@ defmodule Pleroma.SafeZipTest do
# 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")
File.mkdir_p!(extract_dir)
Pleroma.Backports.mkdir_p!(extract_dir)
{:ok, files} = SafeZip.unzip_file(zip_path, extract_dir)
# Verify the root file was extracted
@ -145,7 +145,7 @@ defmodule Pleroma.SafeZipTest do
test "can create zip with directories" do
# Create a directory structure
dir_path = Path.join(@tmp_dir, "test_dir")
File.mkdir_p!(dir_path)
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")
@ -428,7 +428,7 @@ defmodule Pleroma.SafeZipTest do
# Create a directory and a file in it
dir_path = Path.join(@tmp_dir, "file_in_dir")
File.mkdir_p!(dir_path)
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")

View file

@ -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

View file

@ -2669,8 +2669,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()
@ -2682,10 +2686,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
@ -2783,6 +2792,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

View file

@ -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
@ -601,23 +600,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()
@ -941,23 +923,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)
@ -1108,24 +1073,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)
@ -1205,9 +1152,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",
@ -1341,6 +1287,50 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
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

View file

@ -1270,6 +1270,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)
@ -1681,32 +1691,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,

View 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

View file

@ -6,16 +6,15 @@ 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"
@ -167,122 +166,10 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do
})
|> Publisher.publish_one()
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"
activity = insert(:note_activity)
assert {:ok, _} =
Publisher.prepare_one(%{
inbox: inbox,
activity_id: activity.id,
unreachable_since: NaiveDateTime.utc_now() |> NaiveDateTime.to_string()
})
|> Publisher.publish_one()
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"
activity = insert(:note_activity)
assert {:ok, _} =
Publisher.prepare_one(%{
inbox: inbox,
activity_id: activity.id,
unreachable_since: nil
})
|> Publisher.publish_one()
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"
activity = insert(:note_activity)
assert {:cancel, _} =
Publisher.prepare_one(%{inbox: inbox, activity_id: activity.id})
|> Publisher.publish_one()
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"
activity = insert(:note_activity)
assert capture_log(fn ->
assert {:error, _} =
Publisher.prepare_one(%{
inbox: inbox,
activity_id: activity.id
})
|> Publisher.publish_one()
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"
activity = insert(:note_activity)
assert {:ok, _} =
Publisher.prepare_one(%{inbox: inbox, activity_id: activity.id})
|> Publisher.publish_one()
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"
activity = insert(:note_activity)
assert capture_log(fn ->
assert {:error, _} =
Publisher.prepare_one(%{
inbox: inbox,
activity_id: activity.id,
unreachable_since: NaiveDateTime.utc_now() |> NaiveDateTime.to_string()
})
|> Publisher.publish_one()
end) =~ "connrefused"
refute called(Instances.set_unreachable(inbox))
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 =
@ -317,10 +204,7 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do
)
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 =
@ -356,10 +240,7 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do
)
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"])
@ -368,21 +249,58 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do
assert res == :ok
assert called(
Publisher.enqueue_one(
%{
inbox: :_,
activity_id: note_activity.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_outgoing 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_outgoing, 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,
@ -414,10 +332,7 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do
)
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 "publishes a delete activity to peers who signed fetch requests to the create acitvity/object." do
fetcher =
insert(:user,
local: false,
@ -520,4 +435,144 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do
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

View file

@ -143,4 +143,40 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.LikeHandlingTest do
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

View file

@ -642,6 +642,69 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
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_outgoing(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_outgoing(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

View file

@ -670,4 +670,78 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do
)
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

View file

@ -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

View file

@ -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)

View file

@ -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], [])

View file

@ -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

View file

@ -111,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

View file

@ -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)
)

View file

@ -469,6 +469,17 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
assert [%{"id" => ^post_id}] = json_response_and_validate_schema(conn, 200)
end
test "gets only a user's reblogs", %{user: user, conn: conn} do
{:ok, %{id: post_id}} = CommonAPI.post(user, %{status: "HI!!!"})
{:ok, %{id: reblog_id}} = CommonAPI.repeat(post_id, user)
conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?only_reblogs=true")
assert [%{"id" => ^reblog_id}] = json_response_and_validate_schema(conn, 200)
conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?only_reblogs=1")
assert [%{"id" => ^reblog_id}] = json_response_and_validate_schema(conn, 200)
end
test "filters user's statuses by a hashtag", %{user: user, conn: conn} do
{:ok, %{id: post_id}} = CommonAPI.post(user, %{status: "#hashtag"})
{:ok, _post} = CommonAPI.post(user, %{status: "hashtag"})

View file

@ -7,7 +7,6 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do
alias Pleroma.Object
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.Endpoint
import Pleroma.Factory
import ExUnit.CaptureLog
import Tesla.Mock
@ -66,9 +65,7 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do
[account | _] = results["accounts"]
assert account["id"] == to_string(user_three.id)
assert results["hashtags"] == [
%{"name" => "private", "url" => "#{Endpoint.url()}/tag/private"}
]
assert results["hashtags"] == []
[status] = results["statuses"]
assert status["id"] == to_string(activity.id)
@ -77,9 +74,7 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do
get(conn, "/api/v2/search?q=天子")
|> json_response_and_validate_schema(200)
assert results["hashtags"] == [
%{"name" => "天子", "url" => "#{Endpoint.url()}/tag/天子"}
]
assert results["hashtags"] == []
[status] = results["statuses"]
assert status["id"] == to_string(activity.id)
@ -130,84 +125,97 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do
assert [] = results["statuses"]
end
test "constructs hashtags from search query", %{conn: conn} do
test "returns empty results when no hashtags match", %{conn: conn} do
results =
conn
|> get("/api/v2/search?#{URI.encode_query(%{q: "some text with #explicit #hashtags"})}")
|> get("/api/v2/search?#{URI.encode_query(%{q: "nonexistent"})}")
|> json_response_and_validate_schema(200)
assert results["hashtags"] == [
%{"name" => "explicit", "url" => "#{Endpoint.url()}/tag/explicit"},
%{"name" => "hashtags", "url" => "#{Endpoint.url()}/tag/hashtags"}
]
assert results["hashtags"] == []
end
test "searches hashtags by multiple words in query", %{conn: conn} do
user = insert(:user)
{:ok, _activity1} = CommonAPI.post(user, %{status: "This is my new #computer"})
{:ok, _activity2} = CommonAPI.post(user, %{status: "Check out this #laptop"})
{:ok, _activity3} = CommonAPI.post(user, %{status: "My #desktop setup"})
{:ok, _activity4} = CommonAPI.post(user, %{status: "New #phone arrived"})
results =
conn
|> get("/api/v2/search?#{URI.encode_query(%{q: "john doe JOHN DOE"})}")
|> get("/api/v2/search?#{URI.encode_query(%{q: "new computer"})}")
|> json_response_and_validate_schema(200)
assert results["hashtags"] == [
%{"name" => "john", "url" => "#{Endpoint.url()}/tag/john"},
%{"name" => "doe", "url" => "#{Endpoint.url()}/tag/doe"},
%{"name" => "JohnDoe", "url" => "#{Endpoint.url()}/tag/JohnDoe"}
]
hashtag_names = Enum.map(results["hashtags"], & &1["name"])
assert "computer" in hashtag_names
refute "laptop" in hashtag_names
refute "desktop" in hashtag_names
refute "phone" in hashtag_names
results =
conn
|> get("/api/v2/search?#{URI.encode_query(%{q: "accident-prone"})}")
|> get("/api/v2/search?#{URI.encode_query(%{q: "computer laptop"})}")
|> json_response_and_validate_schema(200)
assert results["hashtags"] == [
%{"name" => "accident", "url" => "#{Endpoint.url()}/tag/accident"},
%{"name" => "prone", "url" => "#{Endpoint.url()}/tag/prone"},
%{"name" => "AccidentProne", "url" => "#{Endpoint.url()}/tag/AccidentProne"}
]
results =
conn
|> get("/api/v2/search?#{URI.encode_query(%{q: "https://shpposter.club/users/shpuld"})}")
|> json_response_and_validate_schema(200)
assert results["hashtags"] == [
%{"name" => "shpuld", "url" => "#{Endpoint.url()}/tag/shpuld"}
]
results =
conn
|> get(
"/api/v2/search?#{URI.encode_query(%{q: "https://www.washingtonpost.com/sports/2020/06/10/" <> "nascar-ban-display-confederate-flag-all-events-properties/"})}"
)
|> json_response_and_validate_schema(200)
assert results["hashtags"] == [
%{"name" => "nascar", "url" => "#{Endpoint.url()}/tag/nascar"},
%{"name" => "ban", "url" => "#{Endpoint.url()}/tag/ban"},
%{"name" => "display", "url" => "#{Endpoint.url()}/tag/display"},
%{"name" => "confederate", "url" => "#{Endpoint.url()}/tag/confederate"},
%{"name" => "flag", "url" => "#{Endpoint.url()}/tag/flag"},
%{"name" => "all", "url" => "#{Endpoint.url()}/tag/all"},
%{"name" => "events", "url" => "#{Endpoint.url()}/tag/events"},
%{"name" => "properties", "url" => "#{Endpoint.url()}/tag/properties"},
%{
"name" => "NascarBanDisplayConfederateFlagAllEventsProperties",
"url" =>
"#{Endpoint.url()}/tag/NascarBanDisplayConfederateFlagAllEventsProperties"
}
]
hashtag_names = Enum.map(results["hashtags"], & &1["name"])
assert "computer" in hashtag_names
assert "laptop" in hashtag_names
refute "desktop" in hashtag_names
refute "phone" in hashtag_names
end
test "supports pagination of hashtags search results", %{conn: conn} do
user = insert(:user)
{:ok, _activity1} = CommonAPI.post(user, %{status: "First #alpha hashtag"})
{:ok, _activity2} = CommonAPI.post(user, %{status: "Second #beta hashtag"})
{:ok, _activity3} = CommonAPI.post(user, %{status: "Third #gamma hashtag"})
{:ok, _activity4} = CommonAPI.post(user, %{status: "Fourth #delta hashtag"})
results =
conn
|> get(
"/api/v2/search?#{URI.encode_query(%{q: "#some #text #with #hashtags", limit: 2, offset: 1})}"
)
|> get("/api/v2/search?#{URI.encode_query(%{q: "a", limit: 2, offset: 1})}")
|> json_response_and_validate_schema(200)
assert results["hashtags"] == [
%{"name" => "text", "url" => "#{Endpoint.url()}/tag/text"},
%{"name" => "with", "url" => "#{Endpoint.url()}/tag/with"}
]
hashtag_names = Enum.map(results["hashtags"], & &1["name"])
# Should return 2 hashtags (alpha, beta, gamma, delta all contain 'a')
# With offset 1, we skip the first one, so we get 2 of the remaining 3
assert length(hashtag_names) == 2
assert Enum.all?(hashtag_names, &String.contains?(&1, "a"))
end
test "searches real hashtags from database", %{conn: conn} do
user = insert(:user)
{:ok, _activity1} = CommonAPI.post(user, %{status: "Check out this #car"})
{:ok, _activity2} = CommonAPI.post(user, %{status: "Fast #racecar on the track"})
{:ok, _activity3} = CommonAPI.post(user, %{status: "NASCAR #nascar racing"})
results =
conn
|> get("/api/v2/search?#{URI.encode_query(%{q: "car"})}")
|> json_response_and_validate_schema(200)
hashtag_names = Enum.map(results["hashtags"], & &1["name"])
# Should return car, racecar, and nascar since they all contain "car"
assert "car" in hashtag_names
assert "racecar" in hashtag_names
assert "nascar" in hashtag_names
# Search for "race" - should return racecar
results =
conn
|> get("/api/v2/search?#{URI.encode_query(%{q: "race"})}")
|> json_response_and_validate_schema(200)
hashtag_names = Enum.map(results["hashtags"], & &1["name"])
assert "racecar" in hashtag_names
refute "car" in hashtag_names
refute "nascar" in hashtag_names
end
test "excludes a blocked users from search results", %{conn: conn} do
@ -314,7 +322,7 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do
[account | _] = results["accounts"]
assert account["id"] == to_string(user_three.id)
assert results["hashtags"] == ["2hu"]
assert results["hashtags"] == []
[status] = results["statuses"]
assert status["id"] == to_string(activity.id)

View file

@ -0,0 +1,371 @@
# Pleroma: A lightweight social networking server
# Copyright © Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerDownloadZipTest do
use Pleroma.Web.ConnCase, async: false
import Tesla.Mock
import Pleroma.Factory
setup_all do
# Create a base temp directory for this test module
base_temp_dir = Path.join(System.tmp_dir!(), "emoji_test_#{Ecto.UUID.generate()}")
# Clean up when all tests in module are done
on_exit(fn ->
File.rm_rf!(base_temp_dir)
end)
{:ok, %{base_temp_dir: base_temp_dir}}
end
setup %{base_temp_dir: base_temp_dir} do
# Create a unique subdirectory for each test
test_id = Ecto.UUID.generate()
temp_dir = Path.join(base_temp_dir, test_id)
emoji_dir = Path.join(temp_dir, "emoji")
# Create the directory structure
File.mkdir_p!(emoji_dir)
# Configure this test to use the temp directory
clear_config([:instance, :static_dir], temp_dir)
admin = insert(:user, is_admin: true)
token = insert(:oauth_admin_token, user: admin)
admin_conn =
build_conn()
|> assign(:user, admin)
|> assign(:token, token)
Pleroma.Emoji.reload()
{:ok, %{admin_conn: admin_conn, emoji_path: emoji_dir}}
end
describe "POST /api/pleroma/emoji/packs/download_zip" do
setup do
clear_config([:instance, :admin_privileges], [:emoji_manage_emoji])
end
test "creates pack from uploaded ZIP file", %{admin_conn: admin_conn, emoji_path: emoji_path} do
# Create a test ZIP file with emojis
{:ok, zip_path} = create_test_emoji_zip()
upload = %Plug.Upload{
content_type: "application/zip",
path: zip_path,
filename: "test_pack.zip"
}
assert admin_conn
|> put_req_header("content-type", "multipart/form-data")
|> post("/api/pleroma/emoji/packs/download_zip", %{
name: "test_zip_pack",
file: upload
})
|> json_response_and_validate_schema(200) == "ok"
# Verify pack was created
assert File.exists?("#{emoji_path}/test_zip_pack/pack.json")
assert File.exists?("#{emoji_path}/test_zip_pack/test_emoji.png")
# Verify pack.json contents
{:ok, pack_json} = File.read("#{emoji_path}/test_zip_pack/pack.json")
pack_data = Jason.decode!(pack_json)
assert pack_data["files"]["test_emoji"] == "test_emoji.png"
assert pack_data["pack"]["src_sha256"] != nil
# Clean up
File.rm!(zip_path)
end
test "creates pack from URL", %{admin_conn: admin_conn, emoji_path: emoji_path} do
# Mock HTTP request to download ZIP
{:ok, zip_path} = create_test_emoji_zip()
{:ok, zip_data} = File.read(zip_path)
mock(fn
%{method: :get, url: "https://example.com/emoji_pack.zip"} ->
%Tesla.Env{status: 200, body: zip_data}
end)
assert admin_conn
|> put_req_header("content-type", "multipart/form-data")
|> post("/api/pleroma/emoji/packs/download_zip", %{
name: "test_zip_pack_url",
url: "https://example.com/emoji_pack.zip"
})
|> json_response_and_validate_schema(200) == "ok"
# Verify pack was created
assert File.exists?("#{emoji_path}/test_zip_pack_url/pack.json")
assert File.exists?("#{emoji_path}/test_zip_pack_url/test_emoji.png")
# Verify pack.json has URL as source
{:ok, pack_json} = File.read("#{emoji_path}/test_zip_pack_url/pack.json")
pack_data = Jason.decode!(pack_json)
assert pack_data["pack"]["src"] == "https://example.com/emoji_pack.zip"
assert pack_data["pack"]["src_sha256"] != nil
# Clean up
File.rm!(zip_path)
end
test "refuses to overwrite existing pack", %{admin_conn: admin_conn, emoji_path: emoji_path} do
# Create existing pack
pack_path = Path.join(emoji_path, "test_zip_pack")
File.mkdir_p!(pack_path)
File.write!(Path.join(pack_path, "pack.json"), Jason.encode!(%{files: %{}}))
{:ok, zip_path} = create_test_emoji_zip()
upload = %Plug.Upload{
content_type: "application/zip",
path: zip_path,
filename: "test_pack.zip"
}
assert admin_conn
|> put_req_header("content-type", "multipart/form-data")
|> post("/api/pleroma/emoji/packs/download_zip", %{
name: "test_zip_pack",
file: upload
})
|> json_response_and_validate_schema(400) == %{
"error" => "Pack already exists, refusing to import test_zip_pack"
}
# Clean up
File.rm!(zip_path)
end
test "handles invalid ZIP file", %{admin_conn: admin_conn} do
# Create invalid ZIP file
invalid_zip_path = Path.join(System.tmp_dir!(), "invalid.zip")
File.write!(invalid_zip_path, "not a zip file")
upload = %Plug.Upload{
content_type: "application/zip",
path: invalid_zip_path,
filename: "invalid.zip"
}
assert admin_conn
|> put_req_header("content-type", "multipart/form-data")
|> post("/api/pleroma/emoji/packs/download_zip", %{
name: "test_invalid_pack",
file: upload
})
|> json_response_and_validate_schema(400) == %{
"error" => "Could not unzip pack"
}
# Clean up
File.rm!(invalid_zip_path)
end
test "handles URL download failure", %{admin_conn: admin_conn} do
mock(fn
%{method: :get, url: "https://example.com/bad_pack.zip"} ->
%Tesla.Env{status: 404, body: "Not found"}
end)
assert admin_conn
|> put_req_header("content-type", "multipart/form-data")
|> post("/api/pleroma/emoji/packs/download_zip", %{
name: "test_bad_url_pack",
url: "https://example.com/bad_pack.zip"
})
|> json_response_and_validate_schema(400) == %{
"error" => "Could not download pack"
}
end
test "requires either file or URL parameter", %{admin_conn: admin_conn} do
assert admin_conn
|> put_req_header("content-type", "multipart/form-data")
|> post("/api/pleroma/emoji/packs/download_zip", %{
name: "test_no_source_pack"
})
|> json_response_and_validate_schema(400) == %{
"error" => "Neither file nor URL was present in the request"
}
end
test "returns error when pack name is empty", %{admin_conn: admin_conn} do
{:ok, zip_path} = create_test_emoji_zip()
upload = %Plug.Upload{
content_type: "application/zip",
path: zip_path,
filename: "test_pack.zip"
}
assert admin_conn
|> put_req_header("content-type", "multipart/form-data")
|> post("/api/pleroma/emoji/packs/download_zip", %{
name: "",
file: upload
})
|> json_response_and_validate_schema(400) == %{
"error" => "Pack name cannot be empty"
}
# Clean up
File.rm!(zip_path)
end
test "returns error when unable to create pack directory", %{
admin_conn: admin_conn,
emoji_path: emoji_path
} do
# Make the emoji directory read-only to trigger mkdir_p failure
# Save original permissions
{:ok, %{mode: original_mode}} = File.stat(emoji_path)
# Make emoji directory read-only (no write permission)
File.chmod!(emoji_path, 0o555)
{:ok, zip_path} = create_test_emoji_zip()
upload = %Plug.Upload{
content_type: "application/zip",
path: zip_path,
filename: "test_pack.zip"
}
# Try to create a pack in the read-only emoji directory
assert admin_conn
|> put_req_header("content-type", "multipart/form-data")
|> post("/api/pleroma/emoji/packs/download_zip", %{
name: "test_readonly_pack",
file: upload
})
|> json_response_and_validate_schema(400) == %{
"error" => "Could not create the pack directory"
}
# Clean up - restore original permissions
File.chmod!(emoji_path, original_mode)
File.rm!(zip_path)
end
test "preserves existing pack.json if present in ZIP", %{
admin_conn: admin_conn,
emoji_path: emoji_path
} do
# Create ZIP with pack.json
{:ok, zip_path} = create_test_emoji_zip_with_pack_json()
upload = %Plug.Upload{
content_type: "application/zip",
path: zip_path,
filename: "test_pack_with_json.zip"
}
assert admin_conn
|> put_req_header("content-type", "multipart/form-data")
|> post("/api/pleroma/emoji/packs/download_zip", %{
name: "test_zip_pack_with_json",
file: upload
})
|> json_response_and_validate_schema(200) == "ok"
# Verify original pack.json was preserved
{:ok, pack_json} = File.read("#{emoji_path}/test_zip_pack_with_json/pack.json")
pack_data = Jason.decode!(pack_json)
assert pack_data["pack"]["description"] == "Test pack from ZIP"
assert pack_data["pack"]["license"] == "Test License"
# Clean up
File.rm!(zip_path)
end
test "rejects malicious pack names", %{admin_conn: admin_conn} do
{:ok, zip_path} = create_test_emoji_zip()
upload = %Plug.Upload{
content_type: "application/zip",
path: zip_path,
filename: "test_pack.zip"
}
# Test path traversal attempts
malicious_names = ["../evil", "../../evil", ".", "..", "evil/../../../etc"]
Enum.each(malicious_names, fn name ->
assert_raise RuntimeError, ~r/Invalid or malicious pack name/, fn ->
admin_conn
|> put_req_header("content-type", "multipart/form-data")
|> post("/api/pleroma/emoji/packs/download_zip", %{
name: name,
file: upload
})
end
end)
# Clean up
File.rm!(zip_path)
end
end
defp create_test_emoji_zip do
tmp_dir = System.tmp_dir!()
zip_path = Path.join(tmp_dir, "test_emoji_pack_#{:rand.uniform(10000)}.zip")
# 1x1 pixel PNG
png_data =
Base.decode64!(
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=="
)
files = [
{~c"test_emoji.png", png_data},
# Will be treated as GIF based on extension
{~c"another_emoji.gif", png_data}
]
{:ok, {_name, zip_binary}} = :zip.zip(~c"test_pack.zip", files, [:memory])
File.write!(zip_path, zip_binary)
{:ok, zip_path}
end
defp create_test_emoji_zip_with_pack_json do
tmp_dir = System.tmp_dir!()
zip_path = Path.join(tmp_dir, "test_emoji_pack_json_#{:rand.uniform(10000)}.zip")
png_data =
Base.decode64!(
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=="
)
pack_json =
Jason.encode!(%{
pack: %{
description: "Test pack from ZIP",
license: "Test License"
},
files: %{
"test_emoji" => "test_emoji.png"
}
})
files = [
{~c"test_emoji.png", png_data},
{~c"pack.json", pack_json}
]
{:ok, {_name, zip_binary}} = :zip.zip(~c"test_pack.zip", files, [:memory])
File.write!(zip_path, zip_binary)
{:ok, zip_path}
end
end

View file

@ -7,16 +7,11 @@ defmodule Pleroma.Web.PleromaApi.InstancesControllerTest do
alias Pleroma.Instances
setup_all do: clear_config([:instance, :federation_reachability_timeout_days], 1)
setup do
constant = "http://consistently-unreachable.name/"
eventual = "http://eventually-unreachable.com/path"
{:ok, %Pleroma.Instances.Instance{unreachable_since: constant_unreachable}} =
Instances.set_consistently_unreachable(constant)
_eventual_unreachable = Instances.set_unreachable(eventual)
Instances.set_unreachable(constant)
%{constant_unreachable: constant_unreachable, constant: constant}
end

View file

@ -19,10 +19,33 @@ defmodule Pleroma.Web.PleromaAPI.ScrobbleControllerTest do
"artist" => "lain",
"album" => "lain radio",
"length" => "180000",
"externalLink" => "https://www.last.fm/music/lain/lain+radio/lain+radio+episode+1"
"external_link" => "https://www.last.fm/music/lain/lain+radio/lain+radio+episode+1"
})
assert %{"title" => "lain radio episode 1"} = json_response_and_validate_schema(conn, 200)
assert %{
"title" => "lain radio episode 1",
"external_link" => "https://www.last.fm/music/lain/lain+radio/lain+radio+episode+1"
} = json_response_and_validate_schema(conn, 200)
end
test "external_link fallback" do
%{conn: conn} = oauth_access(["write"])
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/pleroma/scrobble", %{
"title" => "lain radio episode 2",
"artist" => "lain",
"album" => "lain radio",
"length" => "180000",
"externalLink" => "https://www.last.fm/music/lain/lain+radio/lain+radio+episode+2"
})
assert %{
"title" => "lain radio episode 2",
"external_link" => "https://www.last.fm/music/lain/lain+radio/lain+radio+episode+2"
} = json_response_and_validate_schema(conn, 200)
end
end
@ -35,7 +58,7 @@ defmodule Pleroma.Web.PleromaAPI.ScrobbleControllerTest do
title: "lain radio episode 1",
artist: "lain",
album: "lain radio",
externalLink: "https://www.last.fm/music/lain/lain+radio/lain+radio+episode+1"
external_link: "https://www.last.fm/music/lain/lain+radio/lain+radio+episode+1"
})
{:ok, _activity} =
@ -43,7 +66,7 @@ defmodule Pleroma.Web.PleromaAPI.ScrobbleControllerTest do
title: "lain radio episode 2",
artist: "lain",
album: "lain radio",
externalLink: "https://www.last.fm/music/lain/lain+radio/lain+radio+episode+2"
external_link: "https://www.last.fm/music/lain/lain+radio/lain+radio+episode+2"
})
{:ok, _activity} =
@ -51,7 +74,7 @@ defmodule Pleroma.Web.PleromaAPI.ScrobbleControllerTest do
title: "lain radio episode 3",
artist: "lain",
album: "lain radio",
externalLink: "https://www.last.fm/music/lain/lain+radio/lain+radio+episode+3"
external_link: "https://www.last.fm/music/lain/lain+radio/lain+radio+episode+3"
})
conn = get(conn, "/api/v1/pleroma/accounts/#{user.id}/scrobbles")

View file

@ -5,7 +5,8 @@
defmodule Pleroma.Web.Plugs.CacheTest do
# Relies on Cachex, has to stay synchronous
use Pleroma.DataCase
use Plug.Test
import Plug.Conn
import Plug.Test
alias Pleroma.Web.Plugs.Cache

View file

@ -4,7 +4,8 @@
defmodule Pleroma.Web.Plugs.DigestPlugTest do
use ExUnit.Case, async: true
use Plug.Test
import Plug.Conn
import Plug.Test
test "digest algorithm is taken from digest header" do
body = "{\"hello\": \"world\"}"

View file

@ -13,7 +13,7 @@ defmodule Pleroma.Web.Plugs.FrontendStaticPlugTest do
@dir "test/tmp/instance_static"
setup do
File.mkdir_p!(@dir)
Pleroma.Backports.mkdir_p!(@dir)
on_exit(fn -> File.rm_rf(@dir) end)
end
@ -38,7 +38,7 @@ defmodule Pleroma.Web.Plugs.FrontendStaticPlugTest do
clear_config([:frontends, :primary], %{"name" => name, "ref" => ref})
path = "#{@dir}/frontends/#{name}/#{ref}"
File.mkdir_p!(path)
Pleroma.Backports.mkdir_p!(path)
File.write!("#{path}/index.html", "from frontend plug")
index = get(conn, "/")
@ -52,7 +52,7 @@ defmodule Pleroma.Web.Plugs.FrontendStaticPlugTest do
clear_config([:frontends, :admin], %{"name" => name, "ref" => ref})
path = "#{@dir}/frontends/#{name}/#{ref}"
File.mkdir_p!(path)
Pleroma.Backports.mkdir_p!(path)
File.write!("#{path}/index.html", "from frontend plug")
index = get(conn, "/pleroma/admin/")
@ -67,7 +67,7 @@ defmodule Pleroma.Web.Plugs.FrontendStaticPlugTest do
clear_config([:frontends, :primary], %{"name" => name, "ref" => ref})
path = "#{@dir}/frontends/#{name}/#{ref}"
File.mkdir_p!("#{path}/proxy/rr/ss")
Pleroma.Backports.mkdir_p!("#{path}/proxy/rr/ss")
File.write!("#{path}/proxy/rr/ss/Ek7w8WPVcAApOvN.jpg:large", "FB image")
ConfigMock

View file

@ -5,7 +5,8 @@
defmodule Pleroma.Web.Plugs.IdempotencyPlugTest do
# Relies on Cachex, has to stay synchronous
use Pleroma.DataCase
use Plug.Test
import Plug.Conn
import Plug.Test
alias Pleroma.Web.Plugs.IdempotencyPlug
alias Plug.Conn

View file

@ -8,7 +8,7 @@ defmodule Pleroma.Web.Plugs.InstanceStaticTest do
@dir "test/tmp/instance_static"
setup do
File.mkdir_p!(@dir)
Pleroma.Backports.mkdir_p!(@dir)
on_exit(fn -> File.rm_rf(@dir) end)
end
@ -34,7 +34,7 @@ defmodule Pleroma.Web.Plugs.InstanceStaticTest do
refute html_response(bundled_index, 200) == "from frontend plug"
path = "#{@dir}/frontends/#{name}/#{ref}"
File.mkdir_p!(path)
Pleroma.Backports.mkdir_p!(path)
File.write!("#{path}/index.html", "from frontend plug")
index = get(conn, "/")

View file

@ -4,7 +4,8 @@
defmodule Pleroma.Web.Plugs.RemoteIpTest do
use ExUnit.Case
use Plug.Test
import Plug.Conn
import Plug.Test
alias Pleroma.Web.Plugs.RemoteIp

View file

@ -4,7 +4,8 @@
defmodule Pleroma.Web.Plugs.SetFormatPlugTest do
use ExUnit.Case, async: true
use Plug.Test
import Plug.Conn
import Plug.Test
alias Pleroma.Web.Plugs.SetFormatPlug

View file

@ -4,7 +4,7 @@
defmodule Pleroma.Web.Plugs.SetLocalePlugTest do
use ExUnit.Case, async: true
use Plug.Test
import Plug.Test
alias Pleroma.Web.Plugs.SetLocalePlug
alias Plug.Conn

View file

@ -21,8 +21,12 @@ defmodule Pleroma.Web.Plugs.UserTrackingPlugTest do
|> assign(:user, user)
|> UserTrackingPlug.call(%{})
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 "doesn't update last_active_at if it was updated recently", %{conn: conn} do
@ -38,7 +42,7 @@ defmodule Pleroma.Web.Plugs.UserTrackingPlugTest do
|> assign(:user, user)
|> UserTrackingPlug.call(%{})
assert user.last_active_at == last_active_at
assert NaiveDateTime.compare(user.last_active_at, last_active_at) == :eq
end
test "skips updating last_active_at if user ID is nil", %{conn: conn} do

View file

@ -92,7 +92,7 @@ defmodule Pleroma.Web.RichMedia.Parsers.TwitterCardTest do
}
end
test "takes first founded title in html head if there is html markup error" do
test "takes first found title in html head if there is html markup error" do
html =
File.read!("test/fixtures/nypd-facial-recognition-children-teenagers4.html")
|> Floki.parse_document!()

View file

@ -5,7 +5,6 @@
defmodule Pleroma.Web.WebFinger.WebFingerControllerTest do
use Pleroma.Web.ConnCase
import ExUnit.CaptureLog
import Pleroma.Factory
import Tesla.Mock
@ -34,27 +33,122 @@ defmodule Pleroma.Web.WebFinger.WebFingerControllerTest do
assert match?(^response_xml, expected_xml)
end
test "Webfinger JRD" do
describe "Webfinger" do
test "JRD" do
clear_config([Pleroma.Web.Endpoint, :url, :host], "hyrule.world")
clear_config([Pleroma.Web.WebFinger, :domain], "hyrule.world")
user =
insert(:user,
ap_id: "https://hyrule.world/users/zelda"
)
response =
build_conn()
|> put_req_header("accept", "application/jrd+json")
|> get("/.well-known/webfinger?resource=acct:#{user.nickname}@hyrule.world")
|> json_response(200)
assert response["subject"] == "acct:#{user.nickname}@hyrule.world"
assert response["aliases"] == [
"https://hyrule.world/users/zelda"
]
end
test "XML" do
clear_config([Pleroma.Web.Endpoint, :url, :host], "hyrule.world")
clear_config([Pleroma.Web.WebFinger, :domain], "hyrule.world")
user =
insert(:user,
ap_id: "https://hyrule.world/users/zelda"
)
response =
build_conn()
|> put_req_header("accept", "application/xrd+xml")
|> get("/.well-known/webfinger?resource=acct:#{user.nickname}@localhost")
|> response(200)
assert response =~ "<Alias>https://hyrule.world/users/zelda</Alias>"
end
end
test "Webfinger defaults to JSON when no Accept header is provided" do
clear_config([Pleroma.Web.Endpoint, :url, :host], "hyrule.world")
clear_config([Pleroma.Web.WebFinger, :domain], "hyrule.world")
user =
insert(:user,
ap_id: "https://hyrule.world/users/zelda",
also_known_as: ["https://mushroom.kingdom/users/toad"]
ap_id: "https://hyrule.world/users/zelda"
)
response =
build_conn()
|> put_req_header("accept", "application/jrd+json")
|> get("/.well-known/webfinger?resource=acct:#{user.nickname}@localhost")
|> get("/.well-known/webfinger?resource=acct:#{user.nickname}@hyrule.world")
|> json_response(200)
assert response["subject"] == "acct:#{user.nickname}@localhost"
assert response["subject"] == "acct:#{user.nickname}@hyrule.world"
assert response["aliases"] == [
"https://hyrule.world/users/zelda",
"https://mushroom.kingdom/users/toad"
"https://hyrule.world/users/zelda"
]
end
describe "Webfinger returns also_known_as / aliases in the response" do
test "JSON" do
clear_config([Pleroma.Web.Endpoint, :url, :host], "hyrule.world")
clear_config([Pleroma.Web.WebFinger, :domain], "hyrule.world")
user =
insert(:user,
ap_id: "https://hyrule.world/users/zelda",
also_known_as: [
"https://mushroom.kingdom/users/toad",
"https://luigi.mansion/users/kingboo"
]
)
response =
build_conn()
|> get("/.well-known/webfinger?resource=acct:#{user.nickname}@hyrule.world")
|> json_response(200)
assert response["subject"] == "acct:#{user.nickname}@hyrule.world"
assert response["aliases"] == [
"https://hyrule.world/users/zelda",
"https://mushroom.kingdom/users/toad",
"https://luigi.mansion/users/kingboo"
]
end
test "XML" do
clear_config([Pleroma.Web.Endpoint, :url, :host], "hyrule.world")
clear_config([Pleroma.Web.WebFinger, :domain], "hyrule.world")
user =
insert(:user,
ap_id: "https://hyrule.world/users/zelda",
also_known_as: [
"https://mushroom.kingdom/users/toad",
"https://luigi.mansion/users/kingboo"
]
)
response =
build_conn()
|> put_req_header("accept", "application/xrd+xml")
|> get("/.well-known/webfinger?resource=acct:#{user.nickname}@localhost")
|> response(200)
assert response =~ "<Alias>https://hyrule.world/users/zelda</Alias>"
assert response =~ "<Alias>https://mushroom.kingdom/users/toad</Alias>"
assert response =~ "<Alias>https://luigi.mansion/users/kingboo</Alias>"
end
end
test "reach user on tld, while pleroma is running on subdomain" do
clear_config([Pleroma.Web.Endpoint, :url, :host], "sub.example.com")
@ -72,17 +166,32 @@ defmodule Pleroma.Web.WebFinger.WebFingerControllerTest do
assert response["aliases"] == ["https://sub.example.com/users/#{user.nickname}"]
end
test "it returns 404 when user isn't found (JSON)" do
result =
build_conn()
|> put_req_header("accept", "application/jrd+json")
|> get("/.well-known/webfinger?resource=acct:jimm@localhost")
|> json_response(404)
describe "it returns 404 when user isn't found" do
test "JSON" do
result =
build_conn()
|> put_req_header("accept", "application/jrd+json")
|> get("/.well-known/webfinger?resource=acct:jimm@localhost")
|> json_response(404)
assert result == "Couldn't find user"
assert result == "Couldn't find user"
end
test "XML" do
result =
build_conn()
|> put_req_header("accept", "application/xrd+xml")
|> get("/.well-known/webfinger?resource=acct:jimm@localhost")
|> response(404)
assert result == "Couldn't find user"
end
end
test "Webfinger XML" do
test "Returns JSON when format is not supported" do
clear_config([Pleroma.Web.Endpoint, :url, :host], "hyrule.world")
clear_config([Pleroma.Web.WebFinger, :domain], "hyrule.world")
user =
insert(:user,
ap_id: "https://hyrule.world/users/zelda",
@ -91,34 +200,16 @@ defmodule Pleroma.Web.WebFinger.WebFingerControllerTest do
response =
build_conn()
|> put_req_header("accept", "application/xrd+xml")
|> get("/.well-known/webfinger?resource=acct:#{user.nickname}@localhost")
|> response(200)
|> put_req_header("accept", "text/html")
|> get("/.well-known/webfinger?resource=acct:#{user.nickname}@hyrule.world")
|> json_response(200)
assert response =~ "<Alias>https://hyrule.world/users/zelda</Alias>"
assert response =~ "<Alias>https://mushroom.kingdom/users/toad</Alias>"
end
assert response["subject"] == "acct:#{user.nickname}@hyrule.world"
test "it returns 404 when user isn't found (XML)" do
result =
build_conn()
|> put_req_header("accept", "application/xrd+xml")
|> get("/.well-known/webfinger?resource=acct:jimm@localhost")
|> response(404)
assert result == "Couldn't find user"
end
test "Sends a 404 when invalid format" do
user = insert(:user)
assert capture_log(fn ->
assert_raise Phoenix.NotAcceptableError, fn ->
build_conn()
|> put_req_header("accept", "text/html")
|> get("/.well-known/webfinger?resource=acct:#{user.nickname}@localhost")
end
end) =~ "no supported media type in accept header"
assert response["aliases"] == [
"https://hyrule.world/users/zelda",
"https://mushroom.kingdom/users/toad"
]
end
test "Sends a 400 when resource param is missing" do

View file

@ -39,6 +39,23 @@ defmodule Pleroma.Web.WebFingerTest do
end
end
test "requires exact match for Endpoint host or WebFinger domain" do
clear_config([Pleroma.Web.WebFinger, :domain], "pleroma.dev")
user = insert(:user)
assert {:error, "Couldn't find user"} ==
WebFinger.webfinger("#{user.nickname}@#{Pleroma.Web.Endpoint.host()}xxxx", "JSON")
assert {:error, "Couldn't find user"} ==
WebFinger.webfinger("#{user.nickname}@pleroma.devxxxx", "JSON")
assert {:ok, _} =
WebFinger.webfinger("#{user.nickname}@#{Pleroma.Web.Endpoint.host()}", "JSON")
assert {:ok, _} =
WebFinger.webfinger("#{user.nickname}@pleroma.dev", "JSON")
end
describe "fingering" do
test "returns error for nonsensical input" do
assert {:error, _} = WebFinger.finger("bliblablu")

View file

@ -0,0 +1,39 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Workers.DeleteWorkerTest do
use Pleroma.DataCase, async: true
use Oban.Testing, repo: Pleroma.Repo
import Pleroma.Factory
alias Pleroma.Instances.Instance
alias Pleroma.Tests.ObanHelpers
alias Pleroma.Workers.DeleteWorker
describe "instance deletion" do
test "creates individual Oban jobs for each user when deleting an instance" do
user1 = insert(:user, nickname: "alice@example.com", name: "Alice")
user2 = insert(:user, nickname: "bob@example.com", name: "Bob")
{:ok, job} = Instance.delete("example.com")
assert_enqueued(
worker: DeleteWorker,
args: %{"op" => "delete_instance", "host" => "example.com"}
)
{:ok, :ok} = ObanHelpers.perform(job)
delete_user_jobs = all_enqueued(worker: DeleteWorker, args: %{"op" => "delete_user"})
assert length(delete_user_jobs) == 2
user_ids = [user1.id, user2.id]
job_user_ids = Enum.map(delete_user_jobs, fn job -> job.args["user_id"] end)
assert Enum.sort(user_ids) == Enum.sort(job_user_ids)
end
end
end

View file

@ -7,7 +7,9 @@ defmodule Pleroma.Workers.PublisherWorkerTest do
use Oban.Testing, repo: Pleroma.Repo
import Pleroma.Factory
import Mock
alias Pleroma.Instances
alias Pleroma.Object
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Builder
@ -37,4 +39,85 @@ defmodule Pleroma.Workers.PublisherWorkerTest do
assert {:ok, %Oban.Job{priority: 0}} = Federator.publish(post)
end
end
describe "Server reachability:" do
setup do
user = insert(:user)
remote_user = insert(:user, local: false, inbox: "https://example.com/inbox")
{:ok, _, _} = Pleroma.User.follow(remote_user, user)
{:ok, activity} = CommonAPI.post(user, %{status: "Test post"})
%{
user: user,
remote_user: remote_user,
activity: activity
}
end
test "marks server as unreachable only on final failure", %{activity: activity} do
with_mock Pleroma.Web.Federator,
perform: fn :publish_one, _params -> {:error, :connection_error} end do
# First attempt
job = %Oban.Job{
args: %{
"op" => "publish_one",
"params" => %{
"inbox" => "https://example.com/inbox",
"activity_id" => activity.id
}
},
attempt: 1,
max_attempts: 5
}
assert {:error, :connection_error} = Pleroma.Workers.PublisherWorker.perform(job)
assert Instances.reachable?("https://example.com/inbox")
# Final attempt
job = %{job | attempt: 5}
assert {:error, :connection_error} = Pleroma.Workers.PublisherWorker.perform(job)
refute Instances.reachable?("https://example.com/inbox")
end
end
test "does not mark server as unreachable on successful publish", %{activity: activity} do
with_mock Pleroma.Web.Federator,
perform: fn :publish_one, _params -> {:ok, %{status: 200}} end do
job = %Oban.Job{
args: %{
"op" => "publish_one",
"params" => %{
"inbox" => "https://example.com/inbox",
"activity_id" => activity.id
}
},
attempt: 1,
max_attempts: 5
}
assert :ok = Pleroma.Workers.PublisherWorker.perform(job)
assert Instances.reachable?("https://example.com/inbox")
end
end
test "cancels job if server is unreachable", %{activity: activity} do
# First mark the server as unreachable
Instances.set_unreachable("https://example.com/inbox")
refute Instances.reachable?("https://example.com/inbox")
job = %Oban.Job{
args: %{
"op" => "publish_one",
"params" => %{
"inbox" => "https://example.com/inbox",
"activity_id" => activity.id
}
},
attempt: 1,
max_attempts: 5
}
assert {:cancel, :unreachable} = Pleroma.Workers.PublisherWorker.perform(job)
end
end
end

View file

@ -0,0 +1,226 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Workers.ReachabilityWorkerTest do
use Pleroma.DataCase, async: true
use Oban.Testing, repo: Pleroma.Repo
import Mock
alias Pleroma.Tests.ObanHelpers
alias Pleroma.Workers.ReachabilityWorker
setup do
ObanHelpers.wipe_all()
:ok
end
describe "progressive backoff phases" do
test "starts with phase_1min and progresses through phases on failure" do
domain = "example.com"
with_mocks([
{Pleroma.HTTP, [], [get: fn _ -> {:error, :timeout} end]},
{Pleroma.Instances, [], [set_reachable: fn _ -> :ok end]}
]) do
# Start with phase_1min
job = %Oban.Job{
args: %{"domain" => domain, "phase" => "phase_1min", "attempt" => 1}
}
# First attempt fails
assert {:error, :timeout} = ReachabilityWorker.perform(job)
# Should schedule retry for phase_1min (attempt 2)
retry_jobs = all_enqueued(worker: ReachabilityWorker)
assert length(retry_jobs) == 1
[retry_job] = retry_jobs
assert retry_job.args["phase"] == "phase_1min"
assert retry_job.args["attempt"] == 2
# Clear jobs and simulate second attempt failure
ObanHelpers.wipe_all()
retry_job = %Oban.Job{
args: %{"domain" => domain, "phase" => "phase_1min", "attempt" => 2}
}
assert {:error, :timeout} = ReachabilityWorker.perform(retry_job)
# Should schedule retry for phase_1min (attempt 3)
retry_jobs = all_enqueued(worker: ReachabilityWorker)
assert length(retry_jobs) == 1
[retry_job] = retry_jobs
assert retry_job.args["phase"] == "phase_1min"
assert retry_job.args["attempt"] == 3
# Clear jobs and simulate third attempt failure (final attempt for phase_1min)
ObanHelpers.wipe_all()
retry_job = %Oban.Job{
args: %{"domain" => domain, "phase" => "phase_1min", "attempt" => 3}
}
assert {:error, :timeout} = ReachabilityWorker.perform(retry_job)
# Should schedule retry for phase_1min (attempt 4)
retry_jobs = all_enqueued(worker: ReachabilityWorker)
assert length(retry_jobs) == 1
[retry_job] = retry_jobs
assert retry_job.args["phase"] == "phase_1min"
assert retry_job.args["attempt"] == 4
# Clear jobs and simulate fourth attempt failure (final attempt for phase_1min)
ObanHelpers.wipe_all()
retry_job = %Oban.Job{
args: %{"domain" => domain, "phase" => "phase_1min", "attempt" => 4}
}
assert {:error, :timeout} = ReachabilityWorker.perform(retry_job)
# Should schedule next phase (phase_15min)
next_phase_jobs = all_enqueued(worker: ReachabilityWorker)
assert length(next_phase_jobs) == 1
[next_phase_job] = next_phase_jobs
assert next_phase_job.args["phase"] == "phase_15min"
assert next_phase_job.args["attempt"] == 1
end
end
test "progresses through all phases correctly" do
domain = "example.com"
with_mocks([
{Pleroma.HTTP, [], [get: fn _ -> {:error, :timeout} end]},
{Pleroma.Instances, [], [set_reachable: fn _ -> :ok end]}
]) do
# Simulate all phases failing
phases = ["phase_1min", "phase_15min", "phase_1hour", "phase_8hour", "phase_24hour"]
Enum.each(phases, fn phase ->
{_interval, max_attempts, next_phase} = get_phase_config(phase)
# Simulate all attempts failing for this phase
Enum.each(1..max_attempts, fn attempt ->
job = %Oban.Job{args: %{"domain" => domain, "phase" => phase, "attempt" => attempt}}
assert {:error, :timeout} = ReachabilityWorker.perform(job)
if attempt < max_attempts do
# Should schedule retry for same phase
retry_jobs = all_enqueued(worker: ReachabilityWorker)
assert length(retry_jobs) == 1
[retry_job] = retry_jobs
assert retry_job.args["phase"] == phase
assert retry_job.args["attempt"] == attempt + 1
ObanHelpers.wipe_all()
else
# Should schedule next phase (except for final phase)
if next_phase != "final" do
next_phase_jobs = all_enqueued(worker: ReachabilityWorker)
assert length(next_phase_jobs) == 1
[next_phase_job] = next_phase_jobs
assert next_phase_job.args["phase"] == next_phase
assert next_phase_job.args["attempt"] == 1
ObanHelpers.wipe_all()
else
# Final phase - no more jobs should be scheduled
next_phase_jobs = all_enqueued(worker: ReachabilityWorker)
assert length(next_phase_jobs) == 0
end
end
end)
end)
end
end
test "succeeds and stops progression when instance becomes reachable" do
domain = "example.com"
with_mocks([
{Pleroma.HTTP, [], [get: fn _ -> {:ok, %{status: 200}} end]},
{Pleroma.Instances, [], [set_reachable: fn _ -> :ok end]}
]) do
job = %Oban.Job{args: %{"domain" => domain, "phase" => "phase_1hour", "attempt" => 2}}
# Should succeed and not schedule any more jobs
assert :ok = ReachabilityWorker.perform(job)
# Verify set_reachable was called
assert_called(Pleroma.Instances.set_reachable("https://#{domain}"))
# No more jobs should be scheduled
next_jobs = all_enqueued(worker: ReachabilityWorker)
assert length(next_jobs) == 0
end
end
test "enforces uniqueness per domain using Oban's conflict detection" do
domain = "example.com"
# Insert first job for the domain
job1 =
%{
"domain" => domain,
"phase" => "phase_1min",
"attempt" => 1
}
|> ReachabilityWorker.new()
|> Oban.insert()
assert {:ok, _} = job1
# Try to insert a second job for the same domain with different phase/attempt
job2 =
%{
"domain" => domain,
"phase" => "phase_15min",
"attempt" => 1
}
|> ReachabilityWorker.new()
|> Oban.insert()
# Should fail due to uniqueness constraint (conflict)
assert {:ok, %Oban.Job{conflict?: true}} = job2
# Verify only one job exists for this domain
jobs = all_enqueued(worker: ReachabilityWorker)
assert length(jobs) == 1
[existing_job] = jobs
assert existing_job.args["domain"] == domain
assert existing_job.args["phase"] == "phase_1min"
end
test "handles new jobs with only domain argument and transitions them to the first phase" do
domain = "legacy.example.com"
with_mocks([
{Pleroma.Instances, [], [set_reachable: fn _ -> :ok end]}
]) do
# Create a job with only domain (legacy format)
job = %Oban.Job{
args: %{"domain" => domain}
}
# Should reschedule with phase_1min and attempt 1
assert :ok = ReachabilityWorker.perform(job)
# Check that a new job was scheduled with the correct format
scheduled_jobs = all_enqueued(worker: ReachabilityWorker)
assert length(scheduled_jobs) == 1
[scheduled_job] = scheduled_jobs
assert scheduled_job.args["domain"] == domain
assert scheduled_job.args["phase"] == "phase_1min"
assert scheduled_job.args["attempt"] == 1
end
end
end
defp get_phase_config("phase_1min"), do: {1, 4, "phase_15min"}
defp get_phase_config("phase_15min"), do: {15, 4, "phase_1hour"}
defp get_phase_config("phase_1hour"), do: {60, 4, "phase_8hour"}
defp get_phase_config("phase_8hour"), do: {480, 4, "phase_24hour"}
defp get_phase_config("phase_24hour"), do: {1440, 4, "final"}
defp get_phase_config("final"), do: {nil, 0, nil}
end

View file

@ -3,13 +3,14 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Workers.ReceiverWorkerTest do
use Pleroma.DataCase
use Pleroma.DataCase, async: true
use Oban.Testing, repo: Pleroma.Repo
import Mock
import Pleroma.Factory
alias Pleroma.User
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.Federator
alias Pleroma.Workers.ReceiverWorker
@ -243,4 +244,62 @@ defmodule Pleroma.Workers.ReceiverWorkerTest do
assert {:cancel, _} = ReceiverWorker.perform(oban_job)
end
describe "Server reachability:" do
setup do
user = insert(:user)
remote_user = insert(:user, local: false, ap_id: "https://example.com/users/remote")
{:ok, _, _} = Pleroma.User.follow(user, remote_user)
{:ok, activity} = CommonAPI.post(remote_user, %{status: "Test post"})
%{
user: user,
remote_user: remote_user,
activity: activity
}
end
test "schedules ReachabilityWorker if host is unreachable", %{activity: activity} do
with_mocks [
{Pleroma.Web.ActivityPub.Transmogrifier, [],
[handle_incoming: fn _ -> {:ok, activity} end]},
{Pleroma.Instances, [], [reachable?: fn _ -> false end]},
{Pleroma.Web.Federator, [], [perform: fn :incoming_ap_doc, _params -> {:ok, nil} end]}
] do
job = %Oban.Job{
args: %{
"op" => "incoming_ap_doc",
"params" => activity.data
}
}
Pleroma.Workers.ReceiverWorker.perform(job)
assert_enqueued(
worker: Pleroma.Workers.ReachabilityWorker,
args: %{"domain" => "example.com"}
)
end
end
test "does not schedule ReachabilityWorker if host is reachable", %{activity: activity} do
with_mocks [
{Pleroma.Web.ActivityPub.Transmogrifier, [],
[handle_incoming: fn _ -> {:ok, activity} end]},
{Pleroma.Instances, [], [reachable?: fn _ -> true end]},
{Pleroma.Web.Federator, [], [perform: fn :incoming_ap_doc, _params -> {:ok, nil} end]}
] do
job = %Oban.Job{
args: %{
"op" => "incoming_ap_doc",
"params" => activity.data
}
}
Pleroma.Workers.ReceiverWorker.perform(job)
refute_enqueued(worker: Pleroma.Workers.ReachabilityWorker)
end
end
end
end

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Workers.RemoteFetcherWorkerTest do
use Pleroma.DataCase
use Pleroma.DataCase, async: true
use Oban.Testing, repo: Pleroma.Repo
alias Pleroma.Workers.RemoteFetcherWorker

View file

@ -119,6 +119,12 @@ defmodule Pleroma.DataCase do
Mox.stub_with(Pleroma.StubbedHTTPSignaturesMock, Pleroma.Test.HTTPSignaturesProxy)
Mox.stub_with(Pleroma.DateTimeMock, Pleroma.DateTime.Impl)
Mox.stub_with(Pleroma.SignatureMock, Pleroma.Signature)
Mox.stub_with(
Pleroma.Web.ActivityPub.TransmogrifierMock,
Pleroma.Web.ActivityPub.Transmogrifier
)
end
def ensure_local_uploader(context) do

View file

@ -40,3 +40,9 @@ Mox.defmock(Pleroma.Language.LanguageDetectorMock,
Mox.defmock(Pleroma.DateTimeMock, for: Pleroma.DateTime)
Mox.defmock(Pleroma.MogrifyMock, for: Pleroma.MogrifyBehaviour)
Mox.defmock(Pleroma.SignatureMock, for: Pleroma.Signature.API)
Mox.defmock(Pleroma.Web.ActivityPub.TransmogrifierMock,
for: Pleroma.Web.ActivityPub.Transmogrifier.API
)

View file

@ -2,8 +2,6 @@
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
Code.put_compiler_option(:warnings_as_errors, true)
ExUnit.configure(capture_log: true, max_cases: System.schedulers_online())
ExUnit.start(exclude: [:federated])