Merge branch 'develop' of git.pleroma.social:pleroma/pleroma into pleroma-from/upstream-develop/tusooa/report-anon
This commit is contained in:
commit
5bf1a384c7
116 changed files with 2748 additions and 752 deletions
76
test/fixtures/friendica-dislike-undo.json
vendored
Normal file
76
test/fixtures/friendica-dislike-undo.json
vendored
Normal 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
56
test/fixtures/friendica-dislike.json
vendored
Normal 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"
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -3,19 +3,15 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Instances.InstanceTest do
|
||||
alias Pleroma.Instances
|
||||
alias Pleroma.Instances.Instance
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.Tests.ObanHelpers
|
||||
alias Pleroma.Web.CommonAPI
|
||||
|
||||
use Oban.Testing, repo: Pleroma.Repo
|
||||
use Pleroma.DataCase
|
||||
|
||||
import ExUnit.CaptureLog
|
||||
import Pleroma.Factory
|
||||
|
||||
setup_all do: clear_config([:instance, :federation_reachability_timeout_days], 1)
|
||||
|
||||
describe "set_reachable/1" do
|
||||
test "clears `unreachable_since` of existing matching Instance record having non-nil `unreachable_since`" do
|
||||
unreachable_since = NaiveDateTime.to_iso8601(NaiveDateTime.utc_now())
|
||||
|
|
@ -31,6 +27,32 @@ defmodule Pleroma.Instances.InstanceTest do
|
|||
assert {:ok, instance} = Instance.set_reachable(instance.host)
|
||||
refute instance.unreachable_since
|
||||
end
|
||||
|
||||
test "cancels all ReachabilityWorker jobs for the domain" do
|
||||
domain = "cancelme.example.org"
|
||||
insert(:instance, host: domain, unreachable_since: NaiveDateTime.utc_now())
|
||||
|
||||
# Insert a ReachabilityWorker job for this domain, scheduled 5 minutes in the future
|
||||
scheduled_at = DateTime.add(DateTime.utc_now(), 300, :second)
|
||||
|
||||
{:ok, job} =
|
||||
Pleroma.Workers.ReachabilityWorker.new(
|
||||
%{"domain" => domain, "phase" => "phase_1min", "attempt" => 1},
|
||||
scheduled_at: scheduled_at
|
||||
)
|
||||
|> Oban.insert()
|
||||
|
||||
# Ensure the job is present
|
||||
job = Pleroma.Repo.get(Oban.Job, job.id)
|
||||
assert job
|
||||
|
||||
# Call set_reachable, which should delete the job
|
||||
assert {:ok, _} = Instance.set_reachable(domain)
|
||||
|
||||
# Reload the job and assert it is deleted
|
||||
job = Pleroma.Repo.get(Oban.Job, job.id)
|
||||
refute job
|
||||
end
|
||||
end
|
||||
|
||||
describe "set_unreachable/1" do
|
||||
|
|
@ -145,7 +167,11 @@ defmodule Pleroma.Instances.InstanceTest do
|
|||
end
|
||||
|
||||
test "Doesn't scrapes unreachable instances" do
|
||||
instance = insert(:instance, unreachable_since: Instances.reachability_datetime_threshold())
|
||||
instance =
|
||||
insert(:instance,
|
||||
unreachable_since: NaiveDateTime.utc_now() |> NaiveDateTime.add(-:timer.hours(24))
|
||||
)
|
||||
|
||||
url = "https://" <> instance.host
|
||||
|
||||
assert capture_log(fn -> assert nil == Instance.get_or_update_favicon(URI.parse(url)) end) =~
|
||||
|
|
@ -213,32 +239,44 @@ defmodule Pleroma.Instances.InstanceTest do
|
|||
end
|
||||
end
|
||||
|
||||
test "delete_users_and_activities/1 deletes remote instance users and activities" do
|
||||
[mario, luigi, _peach, wario] =
|
||||
users = [
|
||||
insert(:user, nickname: "mario@mushroom.kingdom", name: "Mario"),
|
||||
insert(:user, nickname: "luigi@mushroom.kingdom", name: "Luigi"),
|
||||
insert(:user, nickname: "peach@mushroom.kingdom", name: "Peach"),
|
||||
insert(:user, nickname: "wario@greedville.biz", name: "Wario")
|
||||
]
|
||||
test "delete/1 schedules a job to delete the instance and users" do
|
||||
insert(:user, nickname: "mario@mushroom.kingdom", name: "Mario")
|
||||
|
||||
{:ok, post1} = CommonAPI.post(mario, %{status: "letsa go!"})
|
||||
{:ok, post2} = CommonAPI.post(luigi, %{status: "itsa me... luigi"})
|
||||
{:ok, post3} = CommonAPI.post(wario, %{status: "WHA-HA-HA!"})
|
||||
{:ok, _job} = Instance.delete("mushroom.kingdom")
|
||||
|
||||
{:ok, job} = Instance.delete_users_and_activities("mushroom.kingdom")
|
||||
:ok = ObanHelpers.perform(job)
|
||||
assert_enqueued(
|
||||
worker: Pleroma.Workers.DeleteWorker,
|
||||
args: %{"op" => "delete_instance", "host" => "mushroom.kingdom"}
|
||||
)
|
||||
end
|
||||
|
||||
[mario, luigi, peach, wario] = Repo.reload(users)
|
||||
describe "check_unreachable/1" do
|
||||
test "schedules a ReachabilityWorker job for the given domain" do
|
||||
domain = "test.example.com"
|
||||
|
||||
refute mario.is_active
|
||||
refute luigi.is_active
|
||||
refute peach.is_active
|
||||
refute peach.name == "Peach"
|
||||
# Call check_unreachable
|
||||
assert {:ok, _job} = Instance.check_unreachable(domain)
|
||||
|
||||
assert wario.is_active
|
||||
assert wario.name == "Wario"
|
||||
# Verify that a ReachabilityWorker job was scheduled
|
||||
jobs = all_enqueued(worker: Pleroma.Workers.ReachabilityWorker)
|
||||
assert length(jobs) == 1
|
||||
[job] = jobs
|
||||
assert job.args["domain"] == domain
|
||||
end
|
||||
|
||||
assert [nil, nil, %{}] = Repo.reload([post1, post2, post3])
|
||||
test "handles multiple calls for the same domain (uniqueness enforced)" do
|
||||
domain = "duplicate.example.com"
|
||||
|
||||
assert {:ok, _job1} = Instance.check_unreachable(domain)
|
||||
|
||||
# Second call for the same domain
|
||||
assert {:ok, %Oban.Job{conflict?: true}} = Instance.check_unreachable(domain)
|
||||
|
||||
# Should only have one job due to uniqueness
|
||||
jobs = all_enqueued(worker: Pleroma.Workers.ReachabilityWorker)
|
||||
assert length(jobs) == 1
|
||||
[job] = jobs
|
||||
assert job.args["domain"] == domain
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -6,74 +6,42 @@ defmodule Pleroma.InstancesTest do
|
|||
alias Pleroma.Instances
|
||||
|
||||
use Pleroma.DataCase
|
||||
|
||||
setup_all do: clear_config([:instance, :federation_reachability_timeout_days], 1)
|
||||
use Oban.Testing, repo: Pleroma.Repo
|
||||
|
||||
describe "reachable?/1" do
|
||||
test "returns `true` for host / url with unknown reachability status" do
|
||||
assert Instances.reachable?("unknown.site")
|
||||
assert Instances.reachable?("http://unknown.site")
|
||||
end
|
||||
|
||||
test "returns `false` for host / url marked unreachable for at least `reachability_datetime_threshold()`" do
|
||||
host = "consistently-unreachable.name"
|
||||
Instances.set_consistently_unreachable(host)
|
||||
|
||||
refute Instances.reachable?(host)
|
||||
refute Instances.reachable?("http://#{host}/path")
|
||||
end
|
||||
|
||||
test "returns `true` for host / url marked unreachable for less than `reachability_datetime_threshold()`" do
|
||||
url = "http://eventually-unreachable.name/path"
|
||||
|
||||
Instances.set_unreachable(url)
|
||||
|
||||
assert Instances.reachable?(url)
|
||||
assert Instances.reachable?(URI.parse(url).host)
|
||||
end
|
||||
|
||||
test "raises FunctionClauseError exception on non-binary input" do
|
||||
assert_raise FunctionClauseError, fn -> Instances.reachable?(nil) end
|
||||
assert_raise FunctionClauseError, fn -> Instances.reachable?(1) end
|
||||
end
|
||||
end
|
||||
|
||||
describe "filter_reachable/1" do
|
||||
setup do
|
||||
host = "consistently-unreachable.name"
|
||||
url1 = "http://eventually-unreachable.com/path"
|
||||
url2 = "http://domain.com/path"
|
||||
unreachable_host = "consistently-unreachable.name"
|
||||
reachable_host = "http://domain.com/path"
|
||||
|
||||
Instances.set_consistently_unreachable(host)
|
||||
Instances.set_unreachable(url1)
|
||||
Instances.set_unreachable(unreachable_host)
|
||||
|
||||
result = Instances.filter_reachable([host, url1, url2, nil])
|
||||
%{result: result, url1: url1, url2: url2}
|
||||
result = Instances.filter_reachable([unreachable_host, reachable_host, nil])
|
||||
%{result: result, reachable_host: reachable_host, unreachable_host: unreachable_host}
|
||||
end
|
||||
|
||||
test "returns a map with keys containing 'not marked consistently unreachable' elements of supplied list",
|
||||
%{result: result, url1: url1, url2: url2} do
|
||||
assert is_map(result)
|
||||
assert Enum.sort([url1, url2]) == result |> Map.keys() |> Enum.sort()
|
||||
test "returns a list of only reachable elements",
|
||||
%{result: result, reachable_host: reachable_host} do
|
||||
assert is_list(result)
|
||||
assert [reachable_host] == result
|
||||
end
|
||||
|
||||
test "returns a map with `unreachable_since` values for keys",
|
||||
%{result: result, url1: url1, url2: url2} do
|
||||
assert is_map(result)
|
||||
assert %NaiveDateTime{} = result[url1]
|
||||
assert is_nil(result[url2])
|
||||
end
|
||||
|
||||
test "returns an empty map for empty list or list containing no hosts / url" do
|
||||
assert %{} == Instances.filter_reachable([])
|
||||
assert %{} == Instances.filter_reachable([nil])
|
||||
test "returns an empty list when provided no data" do
|
||||
assert [] == Instances.filter_reachable([])
|
||||
assert [] == Instances.filter_reachable([nil])
|
||||
end
|
||||
end
|
||||
|
||||
describe "set_reachable/1" do
|
||||
test "sets unreachable url or host reachable" do
|
||||
host = "domain.com"
|
||||
Instances.set_consistently_unreachable(host)
|
||||
Instances.set_unreachable(host)
|
||||
refute Instances.reachable?(host)
|
||||
|
||||
Instances.set_reachable(host)
|
||||
|
|
@ -103,22 +71,68 @@ defmodule Pleroma.InstancesTest do
|
|||
end
|
||||
end
|
||||
|
||||
describe "set_consistently_unreachable/1" do
|
||||
test "sets reachable url or host unreachable" do
|
||||
url = "http://domain.com?q="
|
||||
assert Instances.reachable?(url)
|
||||
describe "check_all_unreachable/0" do
|
||||
test "schedules ReachabilityWorker jobs for all unreachable instances" do
|
||||
domain1 = "unreachable1.example.com"
|
||||
domain2 = "unreachable2.example.com"
|
||||
domain3 = "unreachable3.example.com"
|
||||
|
||||
Instances.set_consistently_unreachable(url)
|
||||
refute Instances.reachable?(url)
|
||||
Instances.set_unreachable(domain1)
|
||||
Instances.set_unreachable(domain2)
|
||||
Instances.set_unreachable(domain3)
|
||||
|
||||
Instances.check_all_unreachable()
|
||||
|
||||
# Verify that ReachabilityWorker jobs were scheduled for all unreachable domains
|
||||
jobs = all_enqueued(worker: Pleroma.Workers.ReachabilityWorker)
|
||||
assert length(jobs) == 3
|
||||
|
||||
domains = Enum.map(jobs, & &1.args["domain"])
|
||||
assert domain1 in domains
|
||||
assert domain2 in domains
|
||||
assert domain3 in domains
|
||||
end
|
||||
|
||||
test "keeps unreachable url or host unreachable" do
|
||||
host = "site.name"
|
||||
Instances.set_consistently_unreachable(host)
|
||||
refute Instances.reachable?(host)
|
||||
test "does not schedule jobs for reachable instances" do
|
||||
unreachable_domain = "unreachable.example.com"
|
||||
reachable_domain = "reachable.example.com"
|
||||
|
||||
Instances.set_consistently_unreachable(host)
|
||||
refute Instances.reachable?(host)
|
||||
Instances.set_unreachable(unreachable_domain)
|
||||
Instances.set_reachable(reachable_domain)
|
||||
|
||||
Instances.check_all_unreachable()
|
||||
|
||||
# Verify that only one job was scheduled (for the unreachable domain)
|
||||
jobs = all_enqueued(worker: Pleroma.Workers.ReachabilityWorker)
|
||||
assert length(jobs) == 1
|
||||
[job] = jobs
|
||||
assert job.args["domain"] == unreachable_domain
|
||||
end
|
||||
end
|
||||
|
||||
test "delete_all_unreachable/0 schedules DeleteWorker jobs for all unreachable instances" do
|
||||
domain1 = "unreachable1.example.com"
|
||||
domain2 = "unreachable2.example.com"
|
||||
domain3 = "unreachable3.example.com"
|
||||
|
||||
Instances.set_unreachable(domain1)
|
||||
Instances.set_unreachable(domain2)
|
||||
Instances.set_unreachable(domain3)
|
||||
|
||||
Instances.delete_all_unreachable()
|
||||
|
||||
# Verify that DeleteWorker jobs were scheduled for all unreachable domains
|
||||
jobs = all_enqueued(worker: Pleroma.Workers.DeleteWorker)
|
||||
assert length(jobs) == 3
|
||||
|
||||
domains = Enum.map(jobs, & &1.args["host"])
|
||||
assert domain1 in domains
|
||||
assert domain2 in domains
|
||||
assert domain3 in domains
|
||||
|
||||
# Verify all jobs are delete_instance operations
|
||||
Enum.each(jobs, fn job ->
|
||||
assert job.args["op"] == "delete_instance"
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
@ -1340,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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
139
test/pleroma/web/activity_pub/mrf/quiet_reply_test.exs
Normal file
139
test/pleroma/web/activity_pub/mrf/quiet_reply_test.exs
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.MRF.QuietReplyTest do
|
||||
use Pleroma.DataCase
|
||||
import Pleroma.Factory
|
||||
|
||||
require Pleroma.Constants
|
||||
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Web.ActivityPub.MRF.QuietReply
|
||||
alias Pleroma.Web.CommonAPI
|
||||
|
||||
test "replying to public post is forced to be quiet" do
|
||||
batman = insert(:user, nickname: "batman")
|
||||
robin = insert(:user, nickname: "robin")
|
||||
|
||||
{:ok, post} = CommonAPI.post(batman, %{status: "To the Batmobile!"})
|
||||
|
||||
reply = %{
|
||||
"type" => "Create",
|
||||
"actor" => robin.ap_id,
|
||||
"to" => [
|
||||
batman.ap_id,
|
||||
Pleroma.Constants.as_public()
|
||||
],
|
||||
"cc" => [robin.follower_address],
|
||||
"object" => %{
|
||||
"type" => "Note",
|
||||
"actor" => robin.ap_id,
|
||||
"content" => "@batman Wait up, I forgot my spandex!",
|
||||
"to" => [
|
||||
batman.ap_id,
|
||||
Pleroma.Constants.as_public()
|
||||
],
|
||||
"cc" => [robin.follower_address],
|
||||
"inReplyTo" => Object.normalize(post).data["id"]
|
||||
}
|
||||
}
|
||||
|
||||
assert {:ok, filtered} = QuietReply.filter(reply)
|
||||
|
||||
assert batman.ap_id in filtered["to"]
|
||||
assert batman.ap_id in filtered["object"]["to"]
|
||||
assert robin.follower_address in filtered["to"]
|
||||
assert robin.follower_address in filtered["object"]["to"]
|
||||
assert Pleroma.Constants.as_public() in filtered["cc"]
|
||||
assert Pleroma.Constants.as_public() in filtered["object"]["cc"]
|
||||
end
|
||||
|
||||
test "replying to unlisted post is unmodified" do
|
||||
batman = insert(:user, nickname: "batman")
|
||||
robin = insert(:user, nickname: "robin")
|
||||
|
||||
{:ok, post} = CommonAPI.post(batman, %{status: "To the Batmobile!", visibility: "private"})
|
||||
|
||||
reply = %{
|
||||
"type" => "Create",
|
||||
"actor" => robin.ap_id,
|
||||
"to" => [batman.ap_id],
|
||||
"cc" => [],
|
||||
"object" => %{
|
||||
"type" => "Note",
|
||||
"actor" => robin.ap_id,
|
||||
"content" => "@batman Wait up, I forgot my spandex!",
|
||||
"to" => [batman.ap_id],
|
||||
"cc" => [],
|
||||
"inReplyTo" => Object.normalize(post).data["id"]
|
||||
}
|
||||
}
|
||||
|
||||
assert {:ok, filtered} = QuietReply.filter(reply)
|
||||
|
||||
assert match?(^filtered, reply)
|
||||
end
|
||||
|
||||
test "replying direct is unmodified" do
|
||||
batman = insert(:user, nickname: "batman")
|
||||
robin = insert(:user, nickname: "robin")
|
||||
|
||||
{:ok, post} = CommonAPI.post(batman, %{status: "To the Batmobile!"})
|
||||
|
||||
reply = %{
|
||||
"type" => "Create",
|
||||
"actor" => robin.ap_id,
|
||||
"to" => [batman.ap_id],
|
||||
"cc" => [],
|
||||
"object" => %{
|
||||
"type" => "Note",
|
||||
"actor" => robin.ap_id,
|
||||
"content" => "@batman Wait up, I forgot my spandex!",
|
||||
"to" => [batman.ap_id],
|
||||
"cc" => [],
|
||||
"inReplyTo" => Object.normalize(post).data["id"]
|
||||
}
|
||||
}
|
||||
|
||||
assert {:ok, filtered} = QuietReply.filter(reply)
|
||||
|
||||
assert match?(^filtered, reply)
|
||||
end
|
||||
|
||||
test "replying followers-only is unmodified" do
|
||||
batman = insert(:user, nickname: "batman")
|
||||
robin = insert(:user, nickname: "robin")
|
||||
|
||||
{:ok, post} = CommonAPI.post(batman, %{status: "To the Batmobile!"})
|
||||
|
||||
reply = %{
|
||||
"type" => "Create",
|
||||
"actor" => robin.ap_id,
|
||||
"to" => [batman.ap_id, robin.follower_address],
|
||||
"cc" => [],
|
||||
"object" => %{
|
||||
"type" => "Note",
|
||||
"actor" => robin.ap_id,
|
||||
"content" => "@batman Wait up, I forgot my spandex!",
|
||||
"to" => [batman.ap_id, robin.follower_address],
|
||||
"cc" => [],
|
||||
"inReplyTo" => Object.normalize(post).data["id"]
|
||||
}
|
||||
}
|
||||
|
||||
assert {:ok, filtered} = QuietReply.filter(reply)
|
||||
|
||||
assert match?(^filtered, reply)
|
||||
end
|
||||
|
||||
test "non-reply posts are unmodified" do
|
||||
batman = insert(:user, nickname: "batman")
|
||||
|
||||
{:ok, post} = CommonAPI.post(batman, %{status: "To the Batmobile!"})
|
||||
|
||||
assert {:ok, filtered} = QuietReply.filter(post)
|
||||
|
||||
assert match?(^filtered, post)
|
||||
end
|
||||
end
|
||||
|
|
@ -6,13 +6,11 @@ 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.Publisher
|
||||
|
|
@ -167,115 +165,6 @@ 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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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], [])
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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"})
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -292,10 +292,14 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
|
|||
User.endorse(user1, user2)
|
||||
User.endorse(user1, user3)
|
||||
|
||||
[%{"id" => ^id2}, %{"id" => ^id3}] =
|
||||
response =
|
||||
conn
|
||||
|> get("/api/v1/pleroma/accounts/#{id1}/endorsements")
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
assert length(response) == 2
|
||||
assert Enum.any?(response, fn user -> user["id"] == id2 end)
|
||||
assert Enum.any?(response, fn user -> user["id"] == id3 end)
|
||||
end
|
||||
|
||||
test "returns 404 error when specified user is not exist", %{conn: conn} do
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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\"}"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -33,47 +33,122 @@ defmodule Pleroma.Web.WebFinger.WebFingerControllerTest do
|
|||
assert match?(^response_xml, expected_xml)
|
||||
end
|
||||
|
||||
test "Webfinger JRD" do
|
||||
user =
|
||||
insert(:user,
|
||||
ap_id: "https://hyrule.world/users/zelda",
|
||||
also_known_as: ["https://mushroom.kingdom/users/toad"]
|
||||
)
|
||||
describe "Webfinger" do
|
||||
test "JRD" do
|
||||
clear_config([Pleroma.Web.Endpoint, :url, :host], "hyrule.world")
|
||||
clear_config([Pleroma.Web.WebFinger, :domain], "hyrule.world")
|
||||
|
||||
response =
|
||||
build_conn()
|
||||
|> put_req_header("accept", "application/jrd+json")
|
||||
|> get("/.well-known/webfinger?resource=acct:#{user.nickname}@localhost")
|
||||
|> json_response(200)
|
||||
user =
|
||||
insert(:user,
|
||||
ap_id: "https://hyrule.world/users/zelda"
|
||||
)
|
||||
|
||||
assert response["subject"] == "acct:#{user.nickname}@localhost"
|
||||
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["aliases"] == [
|
||||
"https://hyrule.world/users/zelda",
|
||||
"https://mushroom.kingdom/users/toad"
|
||||
]
|
||||
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()
|
||||
|> 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")
|
||||
|
||||
|
|
@ -91,44 +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"
|
||||
end
|
||||
assert result == "Couldn't find user"
|
||||
end
|
||||
|
||||
test "Webfinger XML" do
|
||||
user =
|
||||
insert(:user,
|
||||
ap_id: "https://hyrule.world/users/zelda",
|
||||
also_known_as: ["https://mushroom.kingdom/users/toad"]
|
||||
)
|
||||
test "XML" do
|
||||
result =
|
||||
build_conn()
|
||||
|> put_req_header("accept", "application/xrd+xml")
|
||||
|> get("/.well-known/webfinger?resource=acct:jimm@localhost")
|
||||
|> response(404)
|
||||
|
||||
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>"
|
||||
end
|
||||
|
||||
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"
|
||||
assert result == "Couldn't find user"
|
||||
end
|
||||
end
|
||||
|
||||
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",
|
||||
|
|
@ -138,10 +201,10 @@ defmodule Pleroma.Web.WebFinger.WebFingerControllerTest do
|
|||
response =
|
||||
build_conn()
|
||||
|> put_req_header("accept", "text/html")
|
||||
|> 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",
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
39
test/pleroma/workers/delete_worker_test.exs
Normal file
39
test/pleroma/workers/delete_worker_test.exs
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
226
test/pleroma/workers/reachability_worker_test.exs
Normal file
226
test/pleroma/workers/reachability_worker_test.exs
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue