summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/fixtures/break_analyze.pngbin0 -> 368176 bytes
-rw-r--r--test/fixtures/mastodon-update-with-likes.json90
-rw-r--r--test/mix/tasks/pleroma/database_test.exs2
-rw-r--r--test/pleroma/ecto_type/activity_pub/object_validators/content_language_map_test.exs56
-rw-r--r--test/pleroma/ecto_type/activity_pub/object_validators/language_code_test.exs29
-rw-r--r--test/pleroma/emoji/pack_test.exs58
-rw-r--r--test/pleroma/integration/mastodon_websocket_test.exs11
-rw-r--r--test/pleroma/object_test.exs152
-rw-r--r--test/pleroma/release_task_test.exs19
-rw-r--r--test/pleroma/safe_zip_test.exs496
-rw-r--r--test/pleroma/upload/filter/analyze_metadata_test.exs14
-rw-r--r--test/pleroma/upload/filter/dedupe_test.exs8
-rw-r--r--test/pleroma/upload_test.exs6
-rw-r--r--test/pleroma/user/backup_test.exs6
-rw-r--r--test/pleroma/user_test.exs70
-rw-r--r--test/pleroma/web/activity_pub/activity_pub_controller_test.exs43
-rw-r--r--test/pleroma/web/activity_pub/activity_pub_test.exs62
-rw-r--r--test/pleroma/web/activity_pub/mrf/remote_report_policy_test.exs155
-rw-r--r--test/pleroma/web/activity_pub/mrf/simple_policy_test.exs15
-rw-r--r--test/pleroma/web/activity_pub/object_validators/article_note_page_validator_test.exs78
-rw-r--r--test/pleroma/web/activity_pub/transmogrifier/note_handling_test.exs30
-rw-r--r--test/pleroma/web/activity_pub/transmogrifier_test.exs27
-rw-r--r--test/pleroma/web/activity_pub/utils_test.exs34
-rw-r--r--test/pleroma/web/activity_pub/views/user_view_test.exs17
-rw-r--r--test/pleroma/web/fallback_test.exs2
-rw-r--r--test/pleroma/web/feed/tag_controller_test.exs56
-rw-r--r--test/pleroma/web/feed/user_controller_test.exs9
-rw-r--r--test/pleroma/web/mastodon_api/controllers/media_controller_test.exs4
-rw-r--r--test/pleroma/web/mastodon_api/controllers/poll_controller_test.exs28
-rw-r--r--test/pleroma/web/mastodon_api/controllers/tag_controller_test.exs159
-rw-r--r--test/pleroma/web/mastodon_api/views/status_view_test.exs20
-rw-r--r--test/pleroma/web/media_proxy/media_proxy_controller_test.exs35
-rw-r--r--test/pleroma/web/metadata/providers/activity_pub_test.exs40
-rw-r--r--test/pleroma/web/metadata/providers/feed_test.exs6
-rw-r--r--test/pleroma/web/o_auth/ldap_authorization_test.exs19
-rw-r--r--test/pleroma/web/rich_media/card_test.exs19
-rw-r--r--test/pleroma/web/streamer_test.exs30
-rw-r--r--test/pleroma/web/twitter_api/controller_test.exs2
-rw-r--r--test/pleroma/workers/poll_worker_test.exs63
-rw-r--r--test/support/factory.ex13
40 files changed, 1787 insertions, 196 deletions
diff --git a/test/fixtures/break_analyze.png b/test/fixtures/break_analyze.png
new file mode 100644
index 000000000..b5e91b08a
--- /dev/null
+++ b/test/fixtures/break_analyze.png
Binary files differ
diff --git a/test/fixtures/mastodon-update-with-likes.json b/test/fixtures/mastodon-update-with-likes.json
new file mode 100644
index 000000000..3bdb3ba3d
--- /dev/null
+++ b/test/fixtures/mastodon-update-with-likes.json
@@ -0,0 +1,90 @@
+{
+ "@context": [
+ "https://www.w3.org/ns/activitystreams",
+ {
+ "atomUri": "ostatus:atomUri",
+ "conversation": "ostatus:conversation",
+ "inReplyToAtomUri": "ostatus:inReplyToAtomUri",
+ "ostatus": "http://ostatus.org#",
+ "sensitive": "as:sensitive",
+ "toot": "http://joinmastodon.org/ns#",
+ "votersCount": "toot:votersCount"
+ },
+ "https://w3id.org/security/v1"
+ ],
+ "actor": "https://pol.social/users/mkljczk",
+ "cc": ["https://www.w3.org/ns/activitystreams#Public",
+ "https://pol.social/users/aemstuz", "https://gts.mkljczk.pl/users/mkljczk",
+ "https://pl.fediverse.pl/users/mkljczk",
+ "https://fedi.kutno.pl/users/mkljczk"],
+ "id": "https://pol.social/users/mkljczk/statuses/113907871635572263#updates/1738096776",
+ "object": {
+ "atomUri": "https://pol.social/users/mkljczk/statuses/113907871635572263",
+ "attachment": [],
+ "attributedTo": "https://pol.social/users/mkljczk",
+ "cc": ["https://www.w3.org/ns/activitystreams#Public",
+ "https://pol.social/users/aemstuz", "https://gts.mkljczk.pl/users/mkljczk",
+ "https://pl.fediverse.pl/users/mkljczk",
+ "https://fedi.kutno.pl/users/mkljczk"],
+ "content": "<p>test</p>",
+ "contentMap": {
+ "pl": "<p>test</p>"
+ },
+ "conversation": "https://fedi.kutno.pl/contexts/43c14c70-d3fb-42b4-a36d-4eacfab9695a",
+ "id": "https://pol.social/users/mkljczk/statuses/113907871635572263",
+ "inReplyTo": "https://pol.social/users/aemstuz/statuses/113907854282654767",
+ "inReplyToAtomUri": "https://pol.social/users/aemstuz/statuses/113907854282654767",
+ "likes": {
+ "id": "https://pol.social/users/mkljczk/statuses/113907871635572263/likes",
+ "totalItems": 1,
+ "type": "Collection"
+ },
+ "published": "2025-01-28T20:29:45Z",
+ "replies": {
+ "first": {
+ "items": [],
+ "next": "https://pol.social/users/mkljczk/statuses/113907871635572263/replies?only_other_accounts=true&page=true",
+ "partOf": "https://pol.social/users/mkljczk/statuses/113907871635572263/replies",
+ "type": "CollectionPage"
+ },
+ "id": "https://pol.social/users/mkljczk/statuses/113907871635572263/replies",
+ "type": "Collection"
+ },
+ "sensitive": false,
+ "shares": {
+ "id": "https://pol.social/users/mkljczk/statuses/113907871635572263/shares",
+ "totalItems": 0,
+ "type": "Collection"
+ },
+ "summary": null,
+ "tag": [
+ {
+ "href": "https://pol.social/users/aemstuz",
+ "name": "@aemstuz",
+ "type": "Mention"
+ },
+ {
+ "href": "https://gts.mkljczk.pl/users/mkljczk",
+ "name": "@mkljczk@gts.mkljczk.pl",
+ "type": "Mention"
+ },
+ {
+ "href": "https://pl.fediverse.pl/users/mkljczk",
+ "name": "@mkljczk@fediverse.pl",
+ "type": "Mention"
+ },
+ {
+ "href": "https://fedi.kutno.pl/users/mkljczk",
+ "name": "@mkljczk@fedi.kutno.pl",
+ "type": "Mention"
+ }
+ ],
+ "to": ["https://pol.social/users/mkljczk/followers"],
+ "type": "Note",
+ "updated": "2025-01-28T20:39:36Z",
+ "url": "https://pol.social/@mkljczk/113907871635572263"
+ },
+ "published": "2025-01-28T20:39:36Z",
+ "to": ["https://pol.social/users/mkljczk/followers"],
+ "type": "Update"
+}
diff --git a/test/mix/tasks/pleroma/database_test.exs b/test/mix/tasks/pleroma/database_test.exs
index 96a925528..38ed096ae 100644
--- a/test/mix/tasks/pleroma/database_test.exs
+++ b/test/mix/tasks/pleroma/database_test.exs
@@ -411,7 +411,7 @@ defmodule Mix.Tasks.Pleroma.DatabaseTest do
["scheduled_activities"],
["schema_migrations"],
["thread_mutes"],
- # ["user_follows_hashtag"], # not in pleroma
+ ["user_follows_hashtag"],
# ["user_frontend_setting_profiles"], # not in pleroma
["user_invite_tokens"],
["user_notes"],
diff --git a/test/pleroma/ecto_type/activity_pub/object_validators/content_language_map_test.exs b/test/pleroma/ecto_type/activity_pub/object_validators/content_language_map_test.exs
new file mode 100644
index 000000000..a05871a6f
--- /dev/null
+++ b/test/pleroma/ecto_type/activity_pub/object_validators/content_language_map_test.exs
@@ -0,0 +1,56 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2023 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.ContentLanguageMapTest do
+ use Pleroma.DataCase, async: true
+
+ alias Pleroma.EctoType.ActivityPub.ObjectValidators.ContentLanguageMap
+
+ test "it validates" do
+ data = %{
+ "en-US" => "mew mew",
+ "en-GB" => "meow meow"
+ }
+
+ assert {:ok, ^data} = ContentLanguageMap.cast(data)
+ end
+
+ test "it validates empty strings" do
+ data = %{
+ "en-US" => "mew mew",
+ "en-GB" => ""
+ }
+
+ assert {:ok, ^data} = ContentLanguageMap.cast(data)
+ end
+
+ test "it ignores non-strings within the map" do
+ data = %{
+ "en-US" => "mew mew",
+ "en-GB" => 123
+ }
+
+ assert {:ok, validated_data} = ContentLanguageMap.cast(data)
+
+ assert validated_data == %{"en-US" => "mew mew"}
+ end
+
+ test "it ignores bad locale codes" do
+ data = %{
+ "en-US" => "mew mew",
+ "en_GB" => "meow meow",
+ "en<<#@!$#!@%!GB" => "meow meow"
+ }
+
+ assert {:ok, validated_data} = ContentLanguageMap.cast(data)
+
+ assert validated_data == %{"en-US" => "mew mew"}
+ end
+
+ test "it complains with non-map data" do
+ assert :error = ContentLanguageMap.cast("mew")
+ assert :error = ContentLanguageMap.cast(["mew"])
+ assert :error = ContentLanguageMap.cast([%{"en-US" => "mew"}])
+ end
+end
diff --git a/test/pleroma/ecto_type/activity_pub/object_validators/language_code_test.exs b/test/pleroma/ecto_type/activity_pub/object_validators/language_code_test.exs
new file mode 100644
index 000000000..086bb3e97
--- /dev/null
+++ b/test/pleroma/ecto_type/activity_pub/object_validators/language_code_test.exs
@@ -0,0 +1,29 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2023 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.LanguageCodeTest do
+ use Pleroma.DataCase, async: true
+
+ alias Pleroma.EctoType.ActivityPub.ObjectValidators.LanguageCode
+
+ test "it accepts language code" do
+ text = "pl"
+ assert {:ok, ^text} = LanguageCode.cast(text)
+ end
+
+ test "it accepts language code with region" do
+ text = "pl-PL"
+ assert {:ok, ^text} = LanguageCode.cast(text)
+ end
+
+ test "errors for invalid language code" do
+ assert {:error, :invalid_language} = LanguageCode.cast("ru_RU")
+ assert {:error, :invalid_language} = LanguageCode.cast(" ")
+ assert {:error, :invalid_language} = LanguageCode.cast("en-US\n")
+ end
+
+ test "errors for non-text" do
+ assert :error == LanguageCode.cast(42)
+ end
+end
diff --git a/test/pleroma/emoji/pack_test.exs b/test/pleroma/emoji/pack_test.exs
index 00001abfc..1943ad1b5 100644
--- a/test/pleroma/emoji/pack_test.exs
+++ b/test/pleroma/emoji/pack_test.exs
@@ -5,6 +5,7 @@
defmodule Pleroma.Emoji.PackTest do
use Pleroma.DataCase
alias Pleroma.Emoji.Pack
+ alias Pleroma.Emoji
@emoji_path Path.join(
Pleroma.Config.get!([:instance, :static_dir]),
@@ -53,6 +54,63 @@ defmodule Pleroma.Emoji.PackTest do
assert updated_pack.files_count == 5
end
+
+ test "skips existing emojis when adding from zip file", %{pack: pack} do
+ # First, let's create a test pack with a "bear" emoji
+ test_pack_path = Path.join(@emoji_path, "test_bear_pack")
+ File.mkdir_p(test_pack_path)
+
+ # Create a pack.json file
+ File.write!(Path.join(test_pack_path, "pack.json"), """
+ {
+ "files": { "bear": "bear.png" },
+ "pack": {
+ "description": "Bear Pack", "homepage": "https://pleroma.social",
+ "license": "Test license", "share-files": true
+ }}
+ """)
+
+ # Copy a test image to use as the bear emoji
+ File.cp!(
+ Path.absname("test/instance_static/emoji/test_pack/blank.png"),
+ Path.join(test_pack_path, "bear.png")
+ )
+
+ # Load the pack to register the "bear" emoji in the global registry
+ {:ok, _bear_pack} = Pleroma.Emoji.Pack.load_pack("test_bear_pack")
+
+ # Reload emoji to make sure the bear emoji is in the global registry
+ Emoji.reload()
+
+ # Verify that the bear emoji exists in the global registry
+ assert Emoji.exist?("bear")
+
+ # Now try to add a zip file that contains an emoji with the same shortcode
+ file = %Plug.Upload{
+ content_type: "application/zip",
+ filename: "emojis.zip",
+ path: Path.absname("test/fixtures/emojis.zip")
+ }
+
+ {:ok, updated_pack} = Pack.add_file(pack, nil, nil, file)
+
+ # Verify that the "bear" emoji was skipped
+ refute Map.has_key?(updated_pack.files, "bear")
+
+ # Other emojis should be added
+ assert Map.has_key?(updated_pack.files, "a_trusted_friend-128")
+ assert Map.has_key?(updated_pack.files, "auroraborealis")
+ assert Map.has_key?(updated_pack.files, "baby_in_a_box")
+ assert Map.has_key?(updated_pack.files, "bear-128")
+
+ # Total count should be 4 (all emojis except "bear")
+ assert updated_pack.files_count == 4
+
+ # Clean up the test pack
+ on_exit(fn ->
+ File.rm_rf!(test_pack_path)
+ end)
+ end
end
test "returns error when zip file is bad", %{pack: pack} do
diff --git a/test/pleroma/integration/mastodon_websocket_test.exs b/test/pleroma/integration/mastodon_websocket_test.exs
index f499f54ad..88f32762d 100644
--- a/test/pleroma/integration/mastodon_websocket_test.exs
+++ b/test/pleroma/integration/mastodon_websocket_test.exs
@@ -268,6 +268,17 @@ defmodule Pleroma.Integration.MastodonWebsocketTest do
end)
end
+ test "accepts valid token on Sec-WebSocket-Protocol header", %{token: token} do
+ assert {:ok, _} = start_socket("?stream=user", [{"Sec-WebSocket-Protocol", token.token}])
+
+ capture_log(fn ->
+ assert {:error, %WebSockex.RequestError{code: 401}} =
+ start_socket("?stream=user", [{"Sec-WebSocket-Protocol", "I am a friend"}])
+
+ Process.sleep(30)
+ end)
+ end
+
test "accepts valid token on client-sent event", %{token: token} do
assert {:ok, pid} = start_socket()
diff --git a/test/pleroma/object_test.exs b/test/pleroma/object_test.exs
index 48d4d86eb..ed5c2b6c8 100644
--- a/test/pleroma/object_test.exs
+++ b/test/pleroma/object_test.exs
@@ -6,12 +6,10 @@ defmodule Pleroma.ObjectTest do
use Pleroma.DataCase
use Oban.Testing, repo: Pleroma.Repo
- import ExUnit.CaptureLog
import Mox
import Pleroma.Factory
import Tesla.Mock
- alias Pleroma.Activity
alias Pleroma.Hashtag
alias Pleroma.Object
alias Pleroma.Repo
@@ -176,8 +174,9 @@ defmodule Pleroma.ObjectTest do
filename = Path.basename(href)
- assert {:ok, files} = File.ls(uploads_dir)
- assert filename in files
+ expected_path = Path.join([uploads_dir, Pleroma.Upload.Filter.Dedupe.shard_path(filename)])
+
+ assert File.exists?(expected_path)
Object.delete(note)
@@ -185,8 +184,7 @@ defmodule Pleroma.ObjectTest do
assert Object.get_by_id(note.id).data["deleted"]
assert Object.get_by_id(attachment.id) == nil
- assert {:ok, files} = File.ls(uploads_dir)
- refute filename in files
+ refute File.exists?(expected_path)
end
test "with objects that have legacy data.url attribute" do
@@ -282,148 +280,6 @@ defmodule Pleroma.ObjectTest do
end
end
- describe "get_by_id_and_maybe_refetch" do
- setup do
- mock(fn
- %{method: :get, url: "https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d"} ->
- %Tesla.Env{
- status: 200,
- body: File.read!("test/fixtures/tesla_mock/poll_original.json"),
- headers: HttpRequestMock.activitypub_object_headers()
- }
-
- env ->
- apply(HttpRequestMock, :request, [env])
- end)
-
- mock_modified = fn resp ->
- mock(fn
- %{method: :get, url: "https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d"} ->
- resp
-
- env ->
- apply(HttpRequestMock, :request, [env])
- end)
- end
-
- on_exit(fn -> mock(fn env -> apply(HttpRequestMock, :request, [env]) end) end)
-
- [mock_modified: mock_modified]
- end
-
- test "refetches if the time since the last refetch is greater than the interval", %{
- mock_modified: mock_modified
- } do
- %Object{} =
- object =
- Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d",
- fetch: true
- )
-
- Object.set_cache(object)
-
- assert Enum.at(object.data["oneOf"], 0)["replies"]["totalItems"] == 4
- assert Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 0
-
- mock_modified.(%Tesla.Env{
- status: 200,
- body: File.read!("test/fixtures/tesla_mock/poll_modified.json"),
- headers: HttpRequestMock.activitypub_object_headers()
- })
-
- updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: -1)
- object_in_cache = Object.get_cached_by_ap_id(object.data["id"])
- assert updated_object == object_in_cache
- assert Enum.at(updated_object.data["oneOf"], 0)["replies"]["totalItems"] == 8
- assert Enum.at(updated_object.data["oneOf"], 1)["replies"]["totalItems"] == 3
- end
-
- test "returns the old object if refetch fails", %{mock_modified: mock_modified} do
- %Object{} =
- object =
- Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d",
- fetch: true
- )
-
- Object.set_cache(object)
-
- assert Enum.at(object.data["oneOf"], 0)["replies"]["totalItems"] == 4
- assert Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 0
-
- assert capture_log(fn ->
- mock_modified.(%Tesla.Env{status: 404, body: ""})
-
- updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: -1)
- object_in_cache = Object.get_cached_by_ap_id(object.data["id"])
- assert updated_object == object_in_cache
- assert Enum.at(updated_object.data["oneOf"], 0)["replies"]["totalItems"] == 4
- assert Enum.at(updated_object.data["oneOf"], 1)["replies"]["totalItems"] == 0
- end) =~
- "[error] Couldn't refresh https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d"
- end
-
- test "does not refetch if the time since the last refetch is greater than the interval", %{
- mock_modified: mock_modified
- } do
- %Object{} =
- object =
- Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d",
- fetch: true
- )
-
- Object.set_cache(object)
-
- assert Enum.at(object.data["oneOf"], 0)["replies"]["totalItems"] == 4
- assert Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 0
-
- mock_modified.(%Tesla.Env{
- status: 200,
- body: File.read!("test/fixtures/tesla_mock/poll_modified.json"),
- headers: HttpRequestMock.activitypub_object_headers()
- })
-
- updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: 100)
- object_in_cache = Object.get_cached_by_ap_id(object.data["id"])
- assert updated_object == object_in_cache
- assert Enum.at(updated_object.data["oneOf"], 0)["replies"]["totalItems"] == 4
- assert Enum.at(updated_object.data["oneOf"], 1)["replies"]["totalItems"] == 0
- end
-
- test "preserves internal fields on refetch", %{mock_modified: mock_modified} do
- %Object{} =
- object =
- Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d",
- fetch: true
- )
-
- Object.set_cache(object)
-
- assert Enum.at(object.data["oneOf"], 0)["replies"]["totalItems"] == 4
- assert Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 0
-
- user = insert(:user)
- activity = Activity.get_create_by_object_ap_id(object.data["id"])
- {:ok, activity} = CommonAPI.favorite(activity.id, user)
- object = Object.get_by_ap_id(activity.data["object"])
-
- assert object.data["like_count"] == 1
-
- mock_modified.(%Tesla.Env{
- status: 200,
- body: File.read!("test/fixtures/tesla_mock/poll_modified.json"),
- headers: HttpRequestMock.activitypub_object_headers()
- })
-
- updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: -1)
- object_in_cache = Object.get_cached_by_ap_id(object.data["id"])
- assert updated_object == object_in_cache
- assert Enum.at(updated_object.data["oneOf"], 0)["replies"]["totalItems"] == 8
- assert Enum.at(updated_object.data["oneOf"], 1)["replies"]["totalItems"] == 3
-
- assert updated_object.data["like_count"] == 1
- end
- end
-
describe ":hashtags association" do
test "Hashtag records are created with Object record and updated on its change" do
user = insert(:user)
diff --git a/test/pleroma/release_task_test.exs b/test/pleroma/release_task_test.exs
new file mode 100644
index 000000000..5a4293189
--- /dev/null
+++ b/test/pleroma/release_task_test.exs
@@ -0,0 +1,19 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.ReleaseTaskTest do
+ use Pleroma.DataCase, async: true
+
+ alias Pleroma.ReleaseTasks
+
+ test "finding the module" do
+ task = "search.meilisearch"
+ assert Mix.Tasks.Pleroma.Search.Meilisearch == ReleaseTasks.find_module(task)
+
+ task = "user"
+ assert Mix.Tasks.Pleroma.User == ReleaseTasks.find_module(task)
+
+ refute ReleaseTasks.find_module("doesnt.exist")
+ end
+end
diff --git a/test/pleroma/safe_zip_test.exs b/test/pleroma/safe_zip_test.exs
new file mode 100644
index 000000000..5063f05e4
--- /dev/null
+++ b/test/pleroma/safe_zip_test.exs
@@ -0,0 +1,496 @@
+defmodule Pleroma.SafeZipTest do
+ # Not making this async because it creates and deletes files
+ use ExUnit.Case
+
+ alias Pleroma.SafeZip
+
+ @fixtures_dir "test/fixtures"
+ @tmp_dir "test/zip_tmp"
+
+ setup do
+ # Ensure tmp directory exists
+ File.mkdir_p!(@tmp_dir)
+
+ on_exit(fn ->
+ # Clean up any files created during tests
+ File.rm_rf!(@tmp_dir)
+ File.mkdir_p!(@tmp_dir)
+ end)
+
+ :ok
+ end
+
+ describe "list_dir_file/1" do
+ test "lists files in a valid zip" do
+ {:ok, files} = SafeZip.list_dir_file(Path.join(@fixtures_dir, "emojis.zip"))
+ assert is_list(files)
+ assert length(files) > 0
+ end
+
+ test "returns an empty list for empty zip" do
+ {:ok, files} = SafeZip.list_dir_file(Path.join(@fixtures_dir, "empty.zip"))
+ assert files == []
+ end
+
+ test "returns error for non-existent file" do
+ assert {:error, _} = SafeZip.list_dir_file(Path.join(@fixtures_dir, "nonexistent.zip"))
+ end
+
+ test "only lists regular files, not directories" do
+ # Create a zip with both files and directories
+ zip_path = create_zip_with_directory()
+
+ # List files with SafeZip
+ {:ok, files} = SafeZip.list_dir_file(zip_path)
+
+ # Verify only regular files are listed, not directories
+ assert "file_in_dir/test_file.txt" in files
+ assert "root_file.txt" in files
+
+ # Directory entries should not be included in the list
+ refute "file_in_dir/" in files
+ end
+ end
+
+ describe "contains_all_data?/2" do
+ test "returns true when all files are in the archive" do
+ # For this test, we'll create our own zip file with known content
+ # to ensure we can test the contains_all_data? function properly
+ zip_path = create_zip_with_directory()
+ archive_data = File.read!(zip_path)
+
+ # Check if the archive contains the root file
+ # Note: The function expects charlists (Erlang strings) in the MapSet
+ assert SafeZip.contains_all_data?(archive_data, MapSet.new([~c"root_file.txt"]))
+ end
+
+ test "returns false when files are missing" do
+ archive_path = Path.join(@fixtures_dir, "emojis.zip")
+ archive_data = File.read!(archive_path)
+
+ # Create a MapSet with non-existent files
+ fset = MapSet.new([~c"nonexistent.txt"])
+
+ refute SafeZip.contains_all_data?(archive_data, fset)
+ end
+
+ test "returns false for invalid archive data" do
+ refute SafeZip.contains_all_data?("invalid data", MapSet.new([~c"file.txt"]))
+ end
+
+ test "only checks for regular files, not directories" do
+ # Create a zip with both files and directories
+ zip_path = create_zip_with_directory()
+ archive_data = File.read!(zip_path)
+
+ # Check if the archive contains a directory (should return false)
+ refute SafeZip.contains_all_data?(archive_data, MapSet.new([~c"file_in_dir/"]))
+
+ # For this test, we'll manually check if the file exists in the archive
+ # by extracting it and verifying it exists
+ extract_dir = Path.join(@tmp_dir, "extract_check")
+ File.mkdir_p!(extract_dir)
+ {:ok, files} = SafeZip.unzip_file(zip_path, extract_dir)
+
+ # Verify the root file was extracted
+ assert Enum.any?(files, fn file ->
+ Path.basename(file) == "root_file.txt"
+ end)
+
+ # Verify the file exists on disk
+ assert File.exists?(Path.join(extract_dir, "root_file.txt"))
+ end
+ end
+
+ describe "zip/4" do
+ test "creates a zip file on disk" do
+ # Create a test file
+ test_file_path = Path.join(@tmp_dir, "test_file.txt")
+ File.write!(test_file_path, "test content")
+
+ # Create a zip file
+ zip_path = Path.join(@tmp_dir, "test.zip")
+ assert {:ok, ^zip_path} = SafeZip.zip(zip_path, ["test_file.txt"], @tmp_dir, false)
+
+ # Verify the zip file exists
+ assert File.exists?(zip_path)
+ end
+
+ test "creates a zip file in memory" do
+ # Create a test file
+ test_file_path = Path.join(@tmp_dir, "test_file.txt")
+ File.write!(test_file_path, "test content")
+
+ # Create a zip file in memory
+ zip_name = Path.join(@tmp_dir, "test.zip")
+
+ assert {:ok, {^zip_name, zip_data}} =
+ SafeZip.zip(zip_name, ["test_file.txt"], @tmp_dir, true)
+
+ # Verify the zip data is binary
+ assert is_binary(zip_data)
+ end
+
+ test "returns error for unsafe paths" do
+ # Try to zip a file with path traversal
+ assert {:error, _} =
+ SafeZip.zip(
+ Path.join(@tmp_dir, "test.zip"),
+ ["../fixtures/test.txt"],
+ @tmp_dir,
+ false
+ )
+ end
+
+ test "can create zip with directories" do
+ # Create a directory structure
+ dir_path = Path.join(@tmp_dir, "test_dir")
+ File.mkdir_p!(dir_path)
+
+ file_in_dir_path = Path.join(dir_path, "file_in_dir.txt")
+ File.write!(file_in_dir_path, "file in directory")
+
+ # Create a zip file
+ zip_path = Path.join(@tmp_dir, "dir_test.zip")
+
+ assert {:ok, ^zip_path} =
+ SafeZip.zip(
+ zip_path,
+ ["test_dir/file_in_dir.txt"],
+ @tmp_dir,
+ false
+ )
+
+ # Verify the zip file exists
+ assert File.exists?(zip_path)
+
+ # Extract and verify the directory structure is preserved
+ extract_dir = Path.join(@tmp_dir, "extract")
+ {:ok, files} = SafeZip.unzip_file(zip_path, extract_dir)
+
+ # Check if the file path is in the list, accounting for possible full paths
+ assert Enum.any?(files, fn file ->
+ String.ends_with?(file, "file_in_dir.txt")
+ end)
+
+ # Verify the file exists in the expected location
+ assert File.exists?(Path.join([extract_dir, "test_dir", "file_in_dir.txt"]))
+ end
+ end
+
+ describe "unzip_file/3" do
+ test "extracts files from a zip archive" do
+ archive_path = Path.join(@fixtures_dir, "emojis.zip")
+
+ # Extract the archive
+ assert {:ok, files} = SafeZip.unzip_file(archive_path, @tmp_dir)
+
+ # Verify files were extracted
+ assert is_list(files)
+ assert length(files) > 0
+
+ # Verify at least one file exists
+ first_file = List.first(files)
+
+ # Simply check that the file exists in the tmp directory
+ assert File.exists?(Path.join(@tmp_dir, Path.basename(first_file)))
+ end
+
+ test "extracts specific files from a zip archive" do
+ archive_path = Path.join(@fixtures_dir, "emojis.zip")
+
+ # Get list of files in the archive
+ {:ok, all_files} = SafeZip.list_dir_file(archive_path)
+ file_to_extract = List.first(all_files)
+
+ # Extract only one file
+ assert {:ok, [extracted_file]} =
+ SafeZip.unzip_file(archive_path, @tmp_dir, [file_to_extract])
+
+ # Verify only the specified file was extracted
+ assert Path.basename(extracted_file) == Path.basename(file_to_extract)
+
+ # Check that the file exists in the tmp directory
+ assert File.exists?(Path.join(@tmp_dir, Path.basename(file_to_extract)))
+ end
+
+ test "returns error for invalid zip file" do
+ invalid_path = Path.join(@tmp_dir, "invalid.zip")
+ File.write!(invalid_path, "not a zip file")
+
+ assert {:error, _} = SafeZip.unzip_file(invalid_path, @tmp_dir)
+ end
+
+ test "creates directories when extracting files in subdirectories" do
+ # Create a zip with files in subdirectories
+ zip_path = create_zip_with_directory()
+
+ # Extract the archive
+ assert {:ok, files} = SafeZip.unzip_file(zip_path, @tmp_dir)
+
+ # Verify files were extracted - handle both relative and absolute paths
+ assert Enum.any?(files, fn file ->
+ Path.basename(file) == "test_file.txt" &&
+ String.contains?(file, "file_in_dir")
+ end)
+
+ assert Enum.any?(files, fn file ->
+ Path.basename(file) == "root_file.txt"
+ end)
+
+ # Verify directory was created
+ dir_path = Path.join(@tmp_dir, "file_in_dir")
+ assert File.exists?(dir_path)
+ assert File.dir?(dir_path)
+
+ # Verify file in directory was extracted
+ file_path = Path.join(dir_path, "test_file.txt")
+ assert File.exists?(file_path)
+ end
+ end
+
+ describe "unzip_data/3" do
+ test "extracts files from zip data" do
+ archive_path = Path.join(@fixtures_dir, "emojis.zip")
+ archive_data = File.read!(archive_path)
+
+ # Extract the archive from data
+ assert {:ok, files} = SafeZip.unzip_data(archive_data, @tmp_dir)
+
+ # Verify files were extracted
+ assert is_list(files)
+ assert length(files) > 0
+
+ # Verify at least one file exists
+ first_file = List.first(files)
+
+ # Simply check that the file exists in the tmp directory
+ assert File.exists?(Path.join(@tmp_dir, Path.basename(first_file)))
+ end
+
+ test "extracts specific files from zip data" do
+ archive_path = Path.join(@fixtures_dir, "emojis.zip")
+ archive_data = File.read!(archive_path)
+
+ # Get list of files in the archive
+ {:ok, all_files} = SafeZip.list_dir_file(archive_path)
+ file_to_extract = List.first(all_files)
+
+ # Extract only one file
+ assert {:ok, extracted_files} =
+ SafeZip.unzip_data(archive_data, @tmp_dir, [file_to_extract])
+
+ # Verify only the specified file was extracted
+ assert Enum.any?(extracted_files, fn path ->
+ Path.basename(path) == Path.basename(file_to_extract)
+ end)
+
+ # Simply check that the file exists in the tmp directory
+ assert File.exists?(Path.join(@tmp_dir, Path.basename(file_to_extract)))
+ end
+
+ test "returns error for invalid zip data" do
+ assert {:error, _} = SafeZip.unzip_data("not a zip file", @tmp_dir)
+ end
+
+ test "creates directories when extracting files in subdirectories from data" do
+ # Create a zip with files in subdirectories
+ zip_path = create_zip_with_directory()
+ archive_data = File.read!(zip_path)
+
+ # Extract the archive from data
+ assert {:ok, files} = SafeZip.unzip_data(archive_data, @tmp_dir)
+
+ # Verify files were extracted - handle both relative and absolute paths
+ assert Enum.any?(files, fn file ->
+ Path.basename(file) == "test_file.txt" &&
+ String.contains?(file, "file_in_dir")
+ end)
+
+ assert Enum.any?(files, fn file ->
+ Path.basename(file) == "root_file.txt"
+ end)
+
+ # Verify directory was created
+ dir_path = Path.join(@tmp_dir, "file_in_dir")
+ assert File.exists?(dir_path)
+ assert File.dir?(dir_path)
+
+ # Verify file in directory was extracted
+ file_path = Path.join(dir_path, "test_file.txt")
+ assert File.exists?(file_path)
+ end
+ end
+
+ # Security tests
+ describe "security checks" do
+ test "prevents path traversal in zip extraction" do
+ # Create a malicious zip file with path traversal
+ malicious_zip_path = create_malicious_zip_with_path_traversal()
+
+ # Try to extract it with SafeZip
+ assert {:error, _} = SafeZip.unzip_file(malicious_zip_path, @tmp_dir)
+
+ # Verify the file was not extracted outside the target directory
+ refute File.exists?(Path.join(Path.dirname(@tmp_dir), "traversal_attempt.txt"))
+ end
+
+ test "prevents directory traversal in zip listing" do
+ # Create a malicious zip file with path traversal
+ malicious_zip_path = create_malicious_zip_with_path_traversal()
+
+ # Try to list files with SafeZip
+ assert {:error, _} = SafeZip.list_dir_file(malicious_zip_path)
+ end
+
+ test "prevents path traversal in zip data extraction" do
+ # Create a malicious zip file with path traversal
+ malicious_zip_path = create_malicious_zip_with_path_traversal()
+ malicious_data = File.read!(malicious_zip_path)
+
+ # Try to extract it with SafeZip
+ assert {:error, _} = SafeZip.unzip_data(malicious_data, @tmp_dir)
+
+ # Verify the file was not extracted outside the target directory
+ refute File.exists?(Path.join(Path.dirname(@tmp_dir), "traversal_attempt.txt"))
+ end
+
+ test "handles zip bomb attempts" do
+ # Create a zip bomb (a zip with many files or large files)
+ zip_bomb_path = create_zip_bomb()
+
+ # The SafeZip module should handle this gracefully
+ # Either by successfully extracting it (if it's not too large)
+ # or by returning an error (if it detects a potential zip bomb)
+ result = SafeZip.unzip_file(zip_bomb_path, @tmp_dir)
+
+ case result do
+ {:ok, _} ->
+ # If it successfully extracts, make sure it didn't fill up the disk
+ # This is a simple check to ensure the extraction was controlled
+ assert File.exists?(@tmp_dir)
+
+ {:error, _} ->
+ # If it returns an error, that's also acceptable
+ # The important thing is that it doesn't crash or hang
+ assert true
+ end
+ end
+
+ test "handles deeply nested directory structures" do
+ # Create a zip with deeply nested directories
+ deep_nest_path = create_deeply_nested_zip()
+
+ # The SafeZip module should handle this gracefully
+ result = SafeZip.unzip_file(deep_nest_path, @tmp_dir)
+
+ case result do
+ {:ok, files} ->
+ # If it successfully extracts, verify the files were extracted
+ assert is_list(files)
+ assert length(files) > 0
+
+ {:error, _} ->
+ # If it returns an error, that's also acceptable
+ # The important thing is that it doesn't crash or hang
+ assert true
+ end
+ end
+ end
+
+ # Helper functions to create test fixtures
+
+ # Creates a zip file with a path traversal attempt
+ defp create_malicious_zip_with_path_traversal do
+ malicious_zip_path = Path.join(@tmp_dir, "path_traversal.zip")
+
+ # Create a file to include in the zip
+ test_file_path = Path.join(@tmp_dir, "test_file.txt")
+ File.write!(test_file_path, "malicious content")
+
+ # Use Erlang's zip module directly to create a zip with path traversal
+ {:ok, charlist_path} =
+ :zip.create(
+ String.to_charlist(malicious_zip_path),
+ [{String.to_charlist("../traversal_attempt.txt"), File.read!(test_file_path)}]
+ )
+
+ to_string(charlist_path)
+ end
+
+ # Creates a zip file with directory entries
+ defp create_zip_with_directory do
+ zip_path = Path.join(@tmp_dir, "with_directory.zip")
+
+ # Create files to include in the zip
+ root_file_path = Path.join(@tmp_dir, "root_file.txt")
+ File.write!(root_file_path, "root file content")
+
+ # Create a directory and a file in it
+ dir_path = Path.join(@tmp_dir, "file_in_dir")
+ File.mkdir_p!(dir_path)
+
+ file_in_dir_path = Path.join(dir_path, "test_file.txt")
+ File.write!(file_in_dir_path, "file in directory content")
+
+ # Use Erlang's zip module to create a zip with directory structure
+ {:ok, charlist_path} =
+ :zip.create(
+ String.to_charlist(zip_path),
+ [
+ {String.to_charlist("root_file.txt"), File.read!(root_file_path)},
+ {String.to_charlist("file_in_dir/test_file.txt"), File.read!(file_in_dir_path)}
+ ]
+ )
+
+ to_string(charlist_path)
+ end
+
+ # Creates a zip bomb (a zip with many small files)
+ defp create_zip_bomb do
+ zip_path = Path.join(@tmp_dir, "zip_bomb.zip")
+
+ # Create a small file to duplicate many times
+ small_file_path = Path.join(@tmp_dir, "small_file.txt")
+ File.write!(small_file_path, String.duplicate("A", 100))
+
+ # Create a list of many files to include in the zip
+ file_entries =
+ for i <- 1..100 do
+ {String.to_charlist("file_#{i}.txt"), File.read!(small_file_path)}
+ end
+
+ # Use Erlang's zip module to create a zip with many files
+ {:ok, charlist_path} =
+ :zip.create(
+ String.to_charlist(zip_path),
+ file_entries
+ )
+
+ to_string(charlist_path)
+ end
+
+ # Creates a zip with deeply nested directories
+ defp create_deeply_nested_zip do
+ zip_path = Path.join(@tmp_dir, "deep_nest.zip")
+
+ # Create a file to include in the zip
+ file_content = "test content"
+
+ # Create a list of deeply nested files
+ file_entries =
+ for i <- 1..10 do
+ nested_path = Enum.reduce(1..i, "nested", fn j, acc -> "#{acc}/level_#{j}" end)
+ {String.to_charlist("#{nested_path}/file.txt"), file_content}
+ end
+
+ # Use Erlang's zip module to create a zip with deeply nested directories
+ {:ok, charlist_path} =
+ :zip.create(
+ String.to_charlist(zip_path),
+ file_entries
+ )
+
+ to_string(charlist_path)
+ end
+end
diff --git a/test/pleroma/upload/filter/analyze_metadata_test.exs b/test/pleroma/upload/filter/analyze_metadata_test.exs
index e4ac673b2..6e1f2afaf 100644
--- a/test/pleroma/upload/filter/analyze_metadata_test.exs
+++ b/test/pleroma/upload/filter/analyze_metadata_test.exs
@@ -34,6 +34,20 @@ defmodule Pleroma.Upload.Filter.AnalyzeMetadataTest do
assert meta.blurhash == "eXJi-E:SwCEm5rCmn$+YWYn+15K#5A$xxCi{SiV]s*W:Efa#s.jE-T"
end
+ test "it gets dimensions for grayscale images" do
+ upload = %Pleroma.Upload{
+ name: "break_analyze.png",
+ content_type: "image/png",
+ path: Path.absname("test/fixtures/break_analyze.png"),
+ tempfile: Path.absname("test/fixtures/break_analyze.png")
+ }
+
+ {:ok, :filtered, meta} = AnalyzeMetadata.filter(upload)
+
+ assert %{width: 1410, height: 2048} = meta
+ assert is_nil(meta.blurhash)
+ end
+
test "adds the dimensions for videos" do
upload = %Pleroma.Upload{
name: "coolvideo.mp4",
diff --git a/test/pleroma/upload/filter/dedupe_test.exs b/test/pleroma/upload/filter/dedupe_test.exs
index 29c181509..4dc28b998 100644
--- a/test/pleroma/upload/filter/dedupe_test.exs
+++ b/test/pleroma/upload/filter/dedupe_test.exs
@@ -10,6 +10,10 @@ defmodule Pleroma.Upload.Filter.DedupeTest do
@shasum "e30397b58d226d6583ab5b8b3c5defb0c682bda5c31ef07a9f57c1c4986e3781"
+ test "generates a shard path for a shasum" do
+ assert "e3/03/97/" <> _path = Dedupe.shard_path(@shasum)
+ end
+
test "adds shasum" do
File.cp!(
"test/fixtures/image.jpg",
@@ -23,10 +27,12 @@ defmodule Pleroma.Upload.Filter.DedupeTest do
tempfile: Path.absname("test/fixtures/image_tmp.jpg")
}
+ expected_path = Dedupe.shard_path(@shasum <> ".jpg")
+
assert {
:ok,
:filtered,
- %Pleroma.Upload{id: @shasum, path: @shasum <> ".jpg"}
+ %Pleroma.Upload{id: @shasum, path: ^expected_path}
} = Dedupe.filter(upload)
end
end
diff --git a/test/pleroma/upload_test.exs b/test/pleroma/upload_test.exs
index facb634c3..5fd62fa43 100644
--- a/test/pleroma/upload_test.exs
+++ b/test/pleroma/upload_test.exs
@@ -149,6 +149,9 @@ defmodule Pleroma.UploadTest do
test "copies the file to the configured folder with deduping" do
File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg")
+ expected_filename = "e30397b58d226d6583ab5b8b3c5defb0c682bda5c31ef07a9f57c1c4986e3781.jpg"
+
+ expected_path = Pleroma.Upload.Filter.Dedupe.shard_path(expected_filename)
file = %Plug.Upload{
content_type: "image/jpeg",
@@ -159,8 +162,7 @@ defmodule Pleroma.UploadTest do
{:ok, data} = Upload.store(file, filters: [Pleroma.Upload.Filter.Dedupe])
assert List.first(data["url"])["href"] ==
- Pleroma.Upload.base_url() <>
- "e30397b58d226d6583ab5b8b3c5defb0c682bda5c31ef07a9f57c1c4986e3781.jpg"
+ Path.join([Pleroma.Upload.base_url(), expected_path])
end
test "copies the file to the configured folder without deduping" do
diff --git a/test/pleroma/user/backup_test.exs b/test/pleroma/user/backup_test.exs
index 24fe09f7e..f4b92adf8 100644
--- a/test/pleroma/user/backup_test.exs
+++ b/test/pleroma/user/backup_test.exs
@@ -185,13 +185,13 @@ defmodule Pleroma.User.BackupTest do
%{"@language" => "und"}
],
"bookmarks" => "bookmarks.json",
- "followers" => "http://cofe.io/users/cofe/followers",
- "following" => "http://cofe.io/users/cofe/following",
+ "followers" => "followers.json",
+ "following" => "following.json",
"id" => "http://cofe.io/users/cofe",
"inbox" => "http://cofe.io/users/cofe/inbox",
"likes" => "likes.json",
"name" => "Cofe",
- "outbox" => "http://cofe.io/users/cofe/outbox",
+ "outbox" => "outbox.json",
"preferredUsername" => "cofe",
"publicKey" => %{
"id" => "http://cofe.io/users/cofe#main-key",
diff --git a/test/pleroma/user_test.exs b/test/pleroma/user_test.exs
index 06afc0709..4a3d6bacc 100644
--- a/test/pleroma/user_test.exs
+++ b/test/pleroma/user_test.exs
@@ -2919,4 +2919,74 @@ defmodule Pleroma.UserTest do
assert [%{"verified_at" => ^verified_at}] = user.fields
end
+
+ describe "follow_hashtag/2" do
+ test "should follow a hashtag" do
+ user = insert(:user)
+ hashtag = insert(:hashtag)
+
+ assert {:ok, _} = user |> User.follow_hashtag(hashtag)
+
+ user = User.get_cached_by_ap_id(user.ap_id)
+
+ assert user.followed_hashtags |> Enum.count() == 1
+ assert hashtag.name in Enum.map(user.followed_hashtags, fn %{name: name} -> name end)
+ end
+
+ test "should not follow a hashtag twice" do
+ user = insert(:user)
+ hashtag = insert(:hashtag)
+
+ assert {:ok, _} = user |> User.follow_hashtag(hashtag)
+
+ assert {:ok, _} = user |> User.follow_hashtag(hashtag)
+
+ user = User.get_cached_by_ap_id(user.ap_id)
+
+ assert user.followed_hashtags |> Enum.count() == 1
+ assert hashtag.name in Enum.map(user.followed_hashtags, fn %{name: name} -> name end)
+ end
+
+ test "can follow multiple hashtags" do
+ user = insert(:user)
+ hashtag = insert(:hashtag)
+ other_hashtag = insert(:hashtag)
+
+ assert {:ok, _} = user |> User.follow_hashtag(hashtag)
+ assert {:ok, _} = user |> User.follow_hashtag(other_hashtag)
+
+ user = User.get_cached_by_ap_id(user.ap_id)
+
+ assert user.followed_hashtags |> Enum.count() == 2
+ assert hashtag.name in Enum.map(user.followed_hashtags, fn %{name: name} -> name end)
+ assert other_hashtag.name in Enum.map(user.followed_hashtags, fn %{name: name} -> name end)
+ end
+ end
+
+ describe "unfollow_hashtag/2" do
+ test "should unfollow a hashtag" do
+ user = insert(:user)
+ hashtag = insert(:hashtag)
+
+ assert {:ok, _} = user |> User.follow_hashtag(hashtag)
+ assert {:ok, _} = user |> User.unfollow_hashtag(hashtag)
+
+ user = User.get_cached_by_ap_id(user.ap_id)
+
+ assert user.followed_hashtags |> Enum.count() == 0
+ end
+
+ test "should not error when trying to unfollow a hashtag twice" do
+ user = insert(:user)
+ hashtag = insert(:hashtag)
+
+ assert {:ok, _} = user |> User.follow_hashtag(hashtag)
+ assert {:ok, _} = user |> User.unfollow_hashtag(hashtag)
+ assert {:ok, _} = user |> User.unfollow_hashtag(hashtag)
+
+ user = User.get_cached_by_ap_id(user.ap_id)
+
+ assert user.followed_hashtags |> Enum.count() == 0
+ end
+ end
end
diff --git a/test/pleroma/web/activity_pub/activity_pub_controller_test.exs b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs
index fffd8f744..4edca14d8 100644
--- a/test/pleroma/web/activity_pub/activity_pub_controller_test.exs
+++ b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs
@@ -1320,6 +1320,27 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
html_body: ~r/#{note.data["object"]}/i
)
end
+
+ test "it accepts an incoming Block", %{conn: conn, data: data} do
+ user = insert(:user)
+
+ data =
+ data
+ |> Map.put("type", "Block")
+ |> Map.put("to", [user.ap_id])
+ |> Map.put("cc", [])
+ |> Map.put("object", user.ap_id)
+
+ conn =
+ conn
+ |> assign(:valid_signature, true)
+ |> put_req_header("content-type", "application/activity+json")
+ |> post("/users/#{user.nickname}/inbox", data)
+
+ assert "ok" == json_response(conn, 200)
+ ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
+ assert Activity.get_by_ap_id(data["id"])
+ end
end
describe "GET /users/:nickname/outbox" do
@@ -1673,6 +1694,28 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
assert json_response(conn, 403)
end
+ test "it rejects update activity of object from other actor", %{conn: conn} do
+ note_activity = insert(:note_activity)
+ note_object = Object.normalize(note_activity, fetch: false)
+ user = insert(:user)
+
+ data = %{
+ type: "Update",
+ object: %{
+ id: note_object.data["id"]
+ }
+ }
+
+ conn =
+ conn
+ |> assign(:user, user)
+ |> put_req_header("content-type", "application/activity+json")
+ |> post("/users/#{user.nickname}/outbox", data)
+
+ assert json_response(conn, 400)
+ assert note_object == Object.normalize(note_activity, fetch: false)
+ end
+
test "it increases like count when receiving a like action", %{conn: conn} do
note_activity = insert(:note_activity)
note_object = Object.normalize(note_activity, fetch: false)
diff --git a/test/pleroma/web/activity_pub/activity_pub_test.exs b/test/pleroma/web/activity_pub/activity_pub_test.exs
index b4f6fb68a..c7adf6bba 100644
--- a/test/pleroma/web/activity_pub/activity_pub_test.exs
+++ b/test/pleroma/web/activity_pub/activity_pub_test.exs
@@ -232,12 +232,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
assert user.avatar == %{
"type" => "Image",
- "url" => [%{"href" => "https://jk.nipponalba.scot/images/profile.jpg"}]
+ "url" => [%{"href" => "https://jk.nipponalba.scot/images/profile.jpg"}],
+ "name" => "profile picture"
}
assert user.banner == %{
"type" => "Image",
- "url" => [%{"href" => "https://jk.nipponalba.scot/images/profile.jpg"}]
+ "url" => [%{"href" => "https://jk.nipponalba.scot/images/profile.jpg"}],
+ "name" => "profile picture"
}
end
@@ -432,6 +434,35 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
assert user.birthday == ~D[2001-02-12]
end
+
+ test "fetches avatar description" do
+ user_id = "https://example.com/users/marcin"
+
+ user_data =
+ "test/fixtures/users_mock/user.json"
+ |> File.read!()
+ |> String.replace("{{nickname}}", "marcin")
+ |> Jason.decode!()
+ |> Map.delete("featured")
+ |> Map.update("icon", %{}, fn image -> Map.put(image, "name", "image description") end)
+ |> Jason.encode!()
+
+ Tesla.Mock.mock(fn
+ %{
+ method: :get,
+ url: ^user_id
+ } ->
+ %Tesla.Env{
+ status: 200,
+ body: user_data,
+ headers: [{"content-type", "application/activity+json"}]
+ }
+ end)
+
+ {:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
+
+ assert user.avatar["name"] == "image description"
+ end
end
test "it fetches the appropriate tag-restricted posts" do
@@ -836,6 +867,33 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
end
end
+ describe "fetch activities for followed hashtags" do
+ test "it should return public activities that reference a given hashtag" do
+ hashtag = insert(:hashtag, name: "tenshi")
+ user = insert(:user)
+ other_user = insert(:user)
+
+ {:ok, normally_visible} =
+ CommonAPI.post(other_user, %{status: "hello :)", visibility: "public"})
+
+ {:ok, public} = CommonAPI.post(user, %{status: "maji #tenshi", visibility: "public"})
+ {:ok, _unrelated} = CommonAPI.post(user, %{status: "dai #tensh", visibility: "public"})
+ {:ok, unlisted} = CommonAPI.post(user, %{status: "maji #tenshi", visibility: "unlisted"})
+ {:ok, _private} = CommonAPI.post(user, %{status: "maji #tenshi", visibility: "private"})
+
+ activities =
+ ActivityPub.fetch_activities([other_user.follower_address], %{
+ followed_hashtags: [hashtag.id]
+ })
+
+ assert length(activities) == 3
+ normal_id = normally_visible.id
+ public_id = public.id
+ unlisted_id = unlisted.id
+ assert [%{id: ^normal_id}, %{id: ^public_id}, %{id: ^unlisted_id}] = activities
+ end
+ end
+
describe "fetch activities in context" do
test "retrieves activities that have a given context" do
{:ok, activity} = ActivityBuilder.insert(%{"type" => "Create", "context" => "2hu"})
diff --git a/test/pleroma/web/activity_pub/mrf/remote_report_policy_test.exs b/test/pleroma/web/activity_pub/mrf/remote_report_policy_test.exs
new file mode 100644
index 000000000..8d2a6b4fa
--- /dev/null
+++ b/test/pleroma/web/activity_pub/mrf/remote_report_policy_test.exs
@@ -0,0 +1,155 @@
+defmodule Pleroma.Web.ActivityPub.MRF.RemoteReportPolicyTest do
+ use Pleroma.DataCase, async: true
+
+ alias Pleroma.Web.ActivityPub.MRF.RemoteReportPolicy
+
+ setup do
+ clear_config([:mrf_remote_report, :reject_all], false)
+ end
+
+ test "doesn't impact local report" do
+ clear_config([:mrf_remote_report, :reject_anonymous], true)
+ clear_config([:mrf_remote_report, :reject_empty_message], true)
+
+ activity = %{
+ "type" => "Flag",
+ "actor" => "http://localhost:4001/actor",
+ "object" => ["https://mastodon.online/users/Gargron"]
+ }
+
+ assert {:ok, _} = RemoteReportPolicy.filter(activity)
+ end
+
+ test "rejects anonymous report if `reject_anonymous: true`" do
+ clear_config([:mrf_remote_report, :reject_anonymous], true)
+ clear_config([:mrf_remote_report, :reject_empty_message], true)
+
+ activity = %{
+ "type" => "Flag",
+ "actor" => "https://mastodon.social/actor",
+ "object" => ["https://mastodon.online/users/Gargron"]
+ }
+
+ assert {:reject, _} = RemoteReportPolicy.filter(activity)
+ end
+
+ test "preserves anonymous report if `reject_anonymous: false`" do
+ clear_config([:mrf_remote_report, :reject_anonymous], false)
+ clear_config([:mrf_remote_report, :reject_empty_message], false)
+
+ activity = %{
+ "type" => "Flag",
+ "actor" => "https://mastodon.social/actor",
+ "object" => ["https://mastodon.online/users/Gargron"]
+ }
+
+ assert {:ok, _} = RemoteReportPolicy.filter(activity)
+ end
+
+ test "rejects report on third party if `reject_third_party: true`" do
+ clear_config([:mrf_remote_report, :reject_third_party], true)
+ clear_config([:mrf_remote_report, :reject_empty_message], false)
+
+ activity = %{
+ "type" => "Flag",
+ "actor" => "https://mastodon.social/users/Gargron",
+ "object" => ["https://mastodon.online/users/Gargron"]
+ }
+
+ assert {:reject, _} = RemoteReportPolicy.filter(activity)
+ end
+
+ test "preserves report on first party if `reject_third_party: true`" do
+ clear_config([:mrf_remote_report, :reject_third_party], true)
+ clear_config([:mrf_remote_report, :reject_empty_message], false)
+
+ activity = %{
+ "type" => "Flag",
+ "actor" => "https://mastodon.social/users/Gargron",
+ "object" => ["http://localhost:4001/actor"]
+ }
+
+ assert {:ok, _} = RemoteReportPolicy.filter(activity)
+ end
+
+ test "preserves report on third party if `reject_third_party: false`" do
+ clear_config([:mrf_remote_report, :reject_third_party], false)
+ clear_config([:mrf_remote_report, :reject_empty_message], false)
+
+ activity = %{
+ "type" => "Flag",
+ "actor" => "https://mastodon.social/users/Gargron",
+ "object" => ["https://mastodon.online/users/Gargron"]
+ }
+
+ assert {:ok, _} = RemoteReportPolicy.filter(activity)
+ end
+
+ test "rejects empty message report if `reject_empty_message: true`" do
+ clear_config([:mrf_remote_report, :reject_anonymous], false)
+ clear_config([:mrf_remote_report, :reject_empty_message], true)
+
+ activity = %{
+ "type" => "Flag",
+ "actor" => "https://mastodon.social/users/Gargron",
+ "object" => ["https://mastodon.online/users/Gargron"]
+ }
+
+ assert {:reject, _} = RemoteReportPolicy.filter(activity)
+ end
+
+ test "rejects empty message report (\"\") if `reject_empty_message: true`" do
+ clear_config([:mrf_remote_report, :reject_anonymous], false)
+ clear_config([:mrf_remote_report, :reject_empty_message], true)
+
+ activity = %{
+ "type" => "Flag",
+ "actor" => "https://mastodon.social/users/Gargron",
+ "object" => ["https://mastodon.online/users/Gargron"],
+ "content" => ""
+ }
+
+ assert {:reject, _} = RemoteReportPolicy.filter(activity)
+ end
+
+ test "preserves empty message report if `reject_empty_message: false`" do
+ clear_config([:mrf_remote_report, :reject_anonymous], false)
+ clear_config([:mrf_remote_report, :reject_empty_message], false)
+
+ activity = %{
+ "type" => "Flag",
+ "actor" => "https://mastodon.social/users/Gargron",
+ "object" => ["https://mastodon.online/users/Gargron"]
+ }
+
+ assert {:ok, _} = RemoteReportPolicy.filter(activity)
+ end
+
+ test "preserves anonymous, empty message report with all settings disabled" do
+ clear_config([:mrf_remote_report, :reject_anonymous], false)
+ clear_config([:mrf_remote_report, :reject_empty_message], false)
+
+ activity = %{
+ "type" => "Flag",
+ "actor" => "https://mastodon.social/actor",
+ "object" => ["https://mastodon.online/users/Gargron"]
+ }
+
+ assert {:ok, _} = RemoteReportPolicy.filter(activity)
+ end
+
+ test "reject remote report if `reject_all: true`" do
+ clear_config([:mrf_remote_report, :reject_all], true)
+ clear_config([:mrf_remote_report, :reject_anonymous], false)
+ clear_config([:mrf_remote_report, :reject_empty_message], false)
+
+ activity = %{
+ "type" => "Flag",
+ "actor" => "https://mastodon.social/users/Gargron",
+ "content" => "Transphobia",
+ "object" => ["https://mastodon.online/users/Gargron"]
+ }
+
+ assert {:reject, _} = RemoteReportPolicy.filter(activity)
+ end
+end
diff --git a/test/pleroma/web/activity_pub/mrf/simple_policy_test.exs b/test/pleroma/web/activity_pub/mrf/simple_policy_test.exs
index 1a51b7d30..f49a7b8ff 100644
--- a/test/pleroma/web/activity_pub/mrf/simple_policy_test.exs
+++ b/test/pleroma/web/activity_pub/mrf/simple_policy_test.exs
@@ -252,6 +252,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
remote_message = build_remote_message()
assert SimplePolicy.filter(remote_message) == {:ok, remote_message}
+ assert SimplePolicy.id_filter(remote_message["actor"])
end
test "activity has a matching host" do
@@ -260,6 +261,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
remote_message = build_remote_message()
assert {:reject, _} = SimplePolicy.filter(remote_message)
+ refute SimplePolicy.id_filter(remote_message["actor"])
end
test "activity matches with wildcard domain" do
@@ -268,6 +270,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
remote_message = build_remote_message()
assert {:reject, _} = SimplePolicy.filter(remote_message)
+ refute SimplePolicy.id_filter(remote_message["actor"])
end
test "actor has a matching host" do
@@ -276,6 +279,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
remote_user = build_remote_user()
assert {:reject, _} = SimplePolicy.filter(remote_user)
+ refute SimplePolicy.id_filter(remote_user["id"])
end
test "reject Announce when object would be rejected" do
@@ -288,6 +292,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
}
assert {:reject, _} = SimplePolicy.filter(announce)
+ # Note: Non-Applicable for id_filter/1
end
test "reject by URI object" do
@@ -300,6 +305,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
}
assert {:reject, _} = SimplePolicy.filter(announce)
+ # Note: Non-Applicable for id_filter/1
end
end
@@ -370,6 +376,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
assert SimplePolicy.filter(local_message) == {:ok, local_message}
assert SimplePolicy.filter(remote_message) == {:ok, remote_message}
+ assert SimplePolicy.id_filter(local_message["actor"])
+ assert SimplePolicy.id_filter(remote_message["actor"])
end
test "is not empty but activity doesn't have a matching host" do
@@ -380,6 +388,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
assert SimplePolicy.filter(local_message) == {:ok, local_message}
assert {:reject, _} = SimplePolicy.filter(remote_message)
+ assert SimplePolicy.id_filter(local_message["actor"])
+ refute SimplePolicy.id_filter(remote_message["actor"])
end
test "activity has a matching host" do
@@ -390,6 +400,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
assert SimplePolicy.filter(local_message) == {:ok, local_message}
assert SimplePolicy.filter(remote_message) == {:ok, remote_message}
+ assert SimplePolicy.id_filter(local_message["actor"])
+ assert SimplePolicy.id_filter(remote_message["actor"])
end
test "activity matches with wildcard domain" do
@@ -400,6 +412,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
assert SimplePolicy.filter(local_message) == {:ok, local_message}
assert SimplePolicy.filter(remote_message) == {:ok, remote_message}
+ assert SimplePolicy.id_filter(local_message["actor"])
+ assert SimplePolicy.id_filter(remote_message["actor"])
end
test "actor has a matching host" do
@@ -408,6 +422,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do
remote_user = build_remote_user()
assert SimplePolicy.filter(remote_user) == {:ok, remote_user}
+ assert SimplePolicy.id_filter(remote_user["id"])
end
end
diff --git a/test/pleroma/web/activity_pub/object_validators/article_note_page_validator_test.exs b/test/pleroma/web/activity_pub/object_validators/article_note_page_validator_test.exs
index e1dbb20c3..829598246 100644
--- a/test/pleroma/web/activity_pub/object_validators/article_note_page_validator_test.exs
+++ b/test/pleroma/web/activity_pub/object_validators/article_note_page_validator_test.exs
@@ -128,6 +128,17 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidatorTest
%{valid?: true} = ArticleNotePageValidator.cast_and_validate(note)
end
+ test "a Note with validated likes collection validates" do
+ insert(:user, ap_id: "https://pol.social/users/mkljczk")
+
+ %{"object" => note} =
+ "test/fixtures/mastodon-update-with-likes.json"
+ |> File.read!()
+ |> Jason.decode!()
+
+ %{valid?: true} = ArticleNotePageValidator.cast_and_validate(note)
+ end
+
test "Fedibird quote post" do
insert(:user, ap_id: "https://fedibird.com/users/noellabo")
@@ -176,4 +187,71 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidatorTest
name: "RE: https://server.example/objects/123"
}
end
+
+ describe "Note language" do
+ test "it detects language from JSON-LD context" do
+ user = insert(:user)
+
+ note_activity = %{
+ "@context" => ["https://www.w3.org/ns/activitystreams", %{"@language" => "pl"}],
+ "to" => ["https://www.w3.org/ns/activitystreams#Public"],
+ "cc" => [],
+ "type" => "Create",
+ "object" => %{
+ "to" => ["https://www.w3.org/ns/activitystreams#Public"],
+ "cc" => [],
+ "id" => Utils.generate_object_id(),
+ "type" => "Note",
+ "content" => "Szczęść Boże",
+ "attributedTo" => user.ap_id
+ },
+ "actor" => user.ap_id
+ }
+
+ {:ok, _create_activity, meta} = ObjectValidator.validate(note_activity, [])
+
+ assert meta[:object_data]["language"] == "pl"
+ end
+
+ test "it detects language from contentMap" do
+ user = insert(:user)
+
+ note = %{
+ "to" => ["https://www.w3.org/ns/activitystreams#Public"],
+ "cc" => [],
+ "id" => Utils.generate_object_id(),
+ "type" => "Note",
+ "content" => "Szczęść Boże",
+ "contentMap" => %{
+ "de" => "Gott segne",
+ "pl" => "Szczęść Boże"
+ },
+ "attributedTo" => user.ap_id
+ }
+
+ {:ok, object} = ArticleNotePageValidator.cast_and_apply(note)
+
+ assert object.language == "pl"
+ end
+
+ test "it adds contentMap if language is specified" do
+ user = insert(:user)
+
+ note = %{
+ "to" => ["https://www.w3.org/ns/activitystreams#Public"],
+ "cc" => [],
+ "id" => Utils.generate_object_id(),
+ "type" => "Note",
+ "content" => "тест",
+ "language" => "uk",
+ "attributedTo" => user.ap_id
+ }
+
+ {:ok, object} = ArticleNotePageValidator.cast_and_apply(note)
+
+ assert object.contentMap == %{
+ "uk" => "тест"
+ }
+ end
+ end
end
diff --git a/test/pleroma/web/activity_pub/transmogrifier/note_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/note_handling_test.exs
index ed71dcb90..fd7a3c772 100644
--- a/test/pleroma/web/activity_pub/transmogrifier/note_handling_test.exs
+++ b/test/pleroma/web/activity_pub/transmogrifier/note_handling_test.exs
@@ -219,6 +219,36 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.NoteHandlingTest do
"<p><span class=\"h-card\"><a href=\"http://localtesting.pleroma.lol/users/lain\" class=\"u-url mention\">@<span>lain</span></a></span></p>"
end
+ test "it only uses contentMap if content is not present" do
+ user = insert(:user)
+
+ message = %{
+ "@context" => "https://www.w3.org/ns/activitystreams",
+ "to" => ["https://www.w3.org/ns/activitystreams#Public"],
+ "cc" => [],
+ "type" => "Create",
+ "object" => %{
+ "to" => ["https://www.w3.org/ns/activitystreams#Public"],
+ "cc" => [],
+ "id" => Utils.generate_object_id(),
+ "type" => "Note",
+ "content" => "Hi",
+ "contentMap" => %{
+ "de" => "Hallo",
+ "uk" => "Привіт"
+ },
+ "inReplyTo" => nil,
+ "attributedTo" => user.ap_id
+ },
+ "actor" => user.ap_id
+ }
+
+ {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(message)
+ object = Object.normalize(data["object"], fetch: false)
+
+ assert object.data["content"] == "Hi"
+ end
+
test "it works for incoming notices with a nil contentMap (firefish)" do
data =
File.read!("test/fixtures/mastodon-post-activity-contentmap.json")
diff --git a/test/pleroma/web/activity_pub/transmogrifier_test.exs b/test/pleroma/web/activity_pub/transmogrifier_test.exs
index 6da7e4a89..fcb8d65d1 100644
--- a/test/pleroma/web/activity_pub/transmogrifier_test.exs
+++ b/test/pleroma/web/activity_pub/transmogrifier_test.exs
@@ -384,6 +384,24 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
assert modified["object"]["quoteUrl"] == quote_id
assert modified["object"]["quoteUri"] == quote_id
end
+
+ test "it adds language of the object to its json-ld context" do
+ user = insert(:user)
+
+ {:ok, activity} = CommonAPI.post(user, %{status: "Cześć", language: "pl"})
+ {:ok, modified} = Transmogrifier.prepare_outgoing(activity.object.data)
+
+ assert [_, _, %{"@language" => "pl"}] = modified["@context"]
+ end
+
+ test "it adds language of the object to Create activity json-ld context" do
+ user = insert(:user)
+
+ {:ok, activity} = CommonAPI.post(user, %{status: "Cześć", language: "pl"})
+ {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
+
+ assert [_, _, %{"@language" => "pl"}] = modified["@context"]
+ end
end
describe "actor rewriting" do
@@ -621,5 +639,14 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
processed = Transmogrifier.prepare_object(original)
assert processed["formerRepresentations"] == original["formerRepresentations"]
end
+
+ test "it uses contentMap to specify post language" do
+ user = insert(:user)
+
+ {:ok, activity} = CommonAPI.post(user, %{status: "Cześć", language: "pl"})
+ object = Transmogrifier.prepare_object(activity.object.data)
+
+ assert %{"contentMap" => %{"pl" => "Cześć"}} = object
+ end
end
end
diff --git a/test/pleroma/web/activity_pub/utils_test.exs b/test/pleroma/web/activity_pub/utils_test.exs
index 872a440cb..45fef154e 100644
--- a/test/pleroma/web/activity_pub/utils_test.exs
+++ b/test/pleroma/web/activity_pub/utils_test.exs
@@ -173,16 +173,30 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do
end
end
- test "make_json_ld_header/0" do
- assert Utils.make_json_ld_header() == %{
- "@context" => [
- "https://www.w3.org/ns/activitystreams",
- "http://localhost:4001/schemas/litepub-0.1.jsonld",
- %{
- "@language" => "und"
- }
- ]
- }
+ describe "make_json_ld_header/1" do
+ test "makes jsonld header" do
+ assert Utils.make_json_ld_header() == %{
+ "@context" => [
+ "https://www.w3.org/ns/activitystreams",
+ "http://localhost:4001/schemas/litepub-0.1.jsonld",
+ %{
+ "@language" => "und"
+ }
+ ]
+ }
+ end
+
+ test "includes language if specified" do
+ assert Utils.make_json_ld_header(%{"language" => "pl"}) == %{
+ "@context" => [
+ "https://www.w3.org/ns/activitystreams",
+ "http://localhost:4001/schemas/litepub-0.1.jsonld",
+ %{
+ "@language" => "pl"
+ }
+ ]
+ }
+ end
end
describe "get_existing_votes" do
diff --git a/test/pleroma/web/activity_pub/views/user_view_test.exs b/test/pleroma/web/activity_pub/views/user_view_test.exs
index 651e535ac..a32e72829 100644
--- a/test/pleroma/web/activity_pub/views/user_view_test.exs
+++ b/test/pleroma/web/activity_pub/views/user_view_test.exs
@@ -68,6 +68,23 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
result = UserView.render("user.json", %{user: user})
assert result["icon"]["url"] == "https://someurl"
assert result["image"]["url"] == "https://somebanner"
+
+ refute result["icon"]["name"]
+ refute result["image"]["name"]
+ end
+
+ test "Avatar has a description if the user set one" do
+ user =
+ insert(:user,
+ avatar: %{
+ "url" => [%{"href" => "https://someurl"}],
+ "name" => "a drawing of pleroma-tan using pleroma groups"
+ }
+ )
+
+ result = UserView.render("user.json", %{user: user})
+
+ assert result["icon"]["name"] == "a drawing of pleroma-tan using pleroma groups"
end
test "renders an invisible user with the invisible property set to true" do
diff --git a/test/pleroma/web/fallback_test.exs b/test/pleroma/web/fallback_test.exs
index ed34d6490..9184cf8f1 100644
--- a/test/pleroma/web/fallback_test.exs
+++ b/test/pleroma/web/fallback_test.exs
@@ -32,7 +32,7 @@ defmodule Pleroma.Web.FallbackTest do
resp = get(conn, "/foo")
assert html_response(resp, 200) =~ "<title>a cool title</title>"
- refute html_response(resp, 200) =~ "initial-results"
+ assert html_response(resp, 200) =~ "<meta content=\"noindex, noarchive\" name=\"robots\">"
end
test "GET /*path", %{conn: conn} do
diff --git a/test/pleroma/web/feed/tag_controller_test.exs b/test/pleroma/web/feed/tag_controller_test.exs
index 7d196b228..662235f31 100644
--- a/test/pleroma/web/feed/tag_controller_test.exs
+++ b/test/pleroma/web/feed/tag_controller_test.exs
@@ -191,4 +191,60 @@ defmodule Pleroma.Web.Feed.TagControllerTest do
|> response(404)
end
end
+
+ describe "restricted for unauthenticated" do
+ test "returns 404 when local timeline is disabled", %{conn: conn} do
+ clear_config([:restrict_unauthenticated, :timelines], %{local: true, federated: false})
+
+ conn
+ |> put_req_header("accept", "application/rss+xml")
+ |> get(tag_feed_path(conn, :feed, "pleromaart.rss"))
+ |> response(404)
+ end
+
+ test "returns local posts only when federated timeline is disabled", %{conn: conn} do
+ clear_config([:restrict_unauthenticated, :timelines], %{local: false, federated: true})
+
+ local_user = insert(:user)
+ remote_user = insert(:user, local: false)
+
+ local_note =
+ insert(:note,
+ user: local_user,
+ data: %{
+ "content" => "local post #PleromaArt",
+ "summary" => "",
+ "tag" => ["pleromaart"]
+ }
+ )
+
+ remote_note =
+ insert(:note,
+ user: remote_user,
+ data: %{
+ "content" => "remote post #PleromaArt",
+ "summary" => "",
+ "tag" => ["pleromaart"]
+ },
+ local: false
+ )
+
+ insert(:note_activity, user: local_user, note: local_note)
+ insert(:note_activity, user: remote_user, note: remote_note, local: false)
+
+ response =
+ conn
+ |> put_req_header("accept", "application/rss+xml")
+ |> get(tag_feed_path(conn, :feed, "pleromaart.rss"))
+ |> response(200)
+
+ xml = parse(response)
+
+ assert xpath(xml, ~x"//channel/title/text()") == ~c"#pleromaart"
+
+ assert xpath(xml, ~x"//channel/item/title/text()"l) == [
+ ~c"local post #PleromaArt"
+ ]
+ end
+ end
end
diff --git a/test/pleroma/web/feed/user_controller_test.exs b/test/pleroma/web/feed/user_controller_test.exs
index 1c17d47b4..0a3aaff5c 100644
--- a/test/pleroma/web/feed/user_controller_test.exs
+++ b/test/pleroma/web/feed/user_controller_test.exs
@@ -147,6 +147,15 @@ defmodule Pleroma.Web.Feed.UserControllerTest do
assert response(conn, 404)
end
+ test "returns noindex meta for missing user", %{conn: conn} do
+ conn =
+ conn
+ |> put_req_header("accept", "text/html")
+ |> get("/users/nonexisting")
+
+ assert html_response(conn, 200) =~ "<meta content=\"noindex, noarchive\" name=\"robots\">"
+ end
+
test "returns feed with public and unlisted activities", %{conn: conn} do
user = insert(:user)
diff --git a/test/pleroma/web/mastodon_api/controllers/media_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/media_controller_test.exs
index 4adbaa640..3f696d94d 100644
--- a/test/pleroma/web/mastodon_api/controllers/media_controller_test.exs
+++ b/test/pleroma/web/mastodon_api/controllers/media_controller_test.exs
@@ -56,7 +56,7 @@ defmodule Pleroma.Web.MastodonAPI.MediaControllerTest do
conn
|> put_req_header("content-type", "multipart/form-data")
|> post("/api/v2/media", %{"file" => image, "description" => desc})
- |> json_response_and_validate_schema(202)
+ |> json_response_and_validate_schema(200)
assert media_id = response["id"]
@@ -111,7 +111,7 @@ defmodule Pleroma.Web.MastodonAPI.MediaControllerTest do
"file" => large_binary,
"description" => desc
})
- |> json_response_and_validate_schema(202)
+ |> json_response_and_validate_schema(200)
assert media_id = response["id"]
diff --git a/test/pleroma/web/mastodon_api/controllers/poll_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/poll_controller_test.exs
index 7912b1d5f..51af87742 100644
--- a/test/pleroma/web/mastodon_api/controllers/poll_controller_test.exs
+++ b/test/pleroma/web/mastodon_api/controllers/poll_controller_test.exs
@@ -3,6 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.PollControllerTest do
+ use Oban.Testing, repo: Pleroma.Repo
use Pleroma.Web.ConnCase, async: true
alias Pleroma.Object
@@ -27,6 +28,33 @@ defmodule Pleroma.Web.MastodonAPI.PollControllerTest do
response = json_response_and_validate_schema(conn, 200)
id = to_string(object.id)
assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
+
+ # Local activities should not generate an Oban job to refresh
+ assert activity.local
+
+ refute_enqueued(
+ worker: Pleroma.Workers.PollWorker,
+ args: %{"op" => "refresh", "activity_id" => activity.id}
+ )
+ end
+
+ test "creates an oban job to refresh poll if activity is remote", %{conn: conn} do
+ user = insert(:user, local: false)
+ question = insert(:question, user: user)
+ activity = insert(:question_activity, question: question, local: false)
+
+ # Ensure this is not represented as a local activity
+ refute activity.local
+
+ object = Object.normalize(activity, fetch: false)
+
+ get(conn, "/api/v1/polls/#{object.id}")
+ |> json_response_and_validate_schema(200)
+
+ assert_enqueued(
+ worker: Pleroma.Workers.PollWorker,
+ args: %{"op" => "refresh", "activity_id" => activity.id}
+ )
end
test "does not expose polls for private statuses", %{conn: conn} do
diff --git a/test/pleroma/web/mastodon_api/controllers/tag_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/tag_controller_test.exs
new file mode 100644
index 000000000..71c8e7fc0
--- /dev/null
+++ b/test/pleroma/web/mastodon_api/controllers/tag_controller_test.exs
@@ -0,0 +1,159 @@
+defmodule Pleroma.Web.MastodonAPI.TagControllerTest do
+ use Pleroma.Web.ConnCase
+
+ import Pleroma.Factory
+ import Tesla.Mock
+
+ alias Pleroma.User
+
+ setup do
+ mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
+ :ok
+ end
+
+ describe "GET /api/v1/tags/:id" do
+ test "returns 200 with tag" do
+ %{user: user, conn: conn} = oauth_access(["read"])
+
+ tag = insert(:hashtag, name: "jubjub")
+ {:ok, _user} = User.follow_hashtag(user, tag)
+
+ response =
+ conn
+ |> get("/api/v1/tags/jubjub")
+ |> json_response_and_validate_schema(200)
+
+ assert %{
+ "name" => "jubjub",
+ "url" => "http://localhost:4001/tags/jubjub",
+ "history" => [],
+ "following" => true
+ } = response
+ end
+
+ test "returns 404 with unknown tag" do
+ %{conn: conn} = oauth_access(["read"])
+
+ conn
+ |> get("/api/v1/tags/jubjub")
+ |> json_response_and_validate_schema(404)
+ end
+ end
+
+ describe "POST /api/v1/tags/:id/follow" do
+ test "should follow a hashtag" do
+ %{user: user, conn: conn} = oauth_access(["write:follows"])
+ hashtag = insert(:hashtag, name: "jubjub")
+
+ response =
+ conn
+ |> post("/api/v1/tags/jubjub/follow")
+ |> json_response_and_validate_schema(200)
+
+ assert response["following"] == true
+ user = User.get_cached_by_ap_id(user.ap_id)
+ assert User.following_hashtag?(user, hashtag)
+ end
+
+ test "should 404 if hashtag doesn't exist" do
+ %{conn: conn} = oauth_access(["write:follows"])
+
+ response =
+ conn
+ |> post("/api/v1/tags/rubrub/follow")
+ |> json_response_and_validate_schema(404)
+
+ assert response["error"] == "Hashtag not found"
+ end
+ end
+
+ describe "POST /api/v1/tags/:id/unfollow" do
+ test "should unfollow a hashtag" do
+ %{user: user, conn: conn} = oauth_access(["write:follows"])
+ hashtag = insert(:hashtag, name: "jubjub")
+ {:ok, user} = User.follow_hashtag(user, hashtag)
+
+ response =
+ conn
+ |> post("/api/v1/tags/jubjub/unfollow")
+ |> json_response_and_validate_schema(200)
+
+ assert response["following"] == false
+ user = User.get_cached_by_ap_id(user.ap_id)
+ refute User.following_hashtag?(user, hashtag)
+ end
+
+ test "should 404 if hashtag doesn't exist" do
+ %{conn: conn} = oauth_access(["write:follows"])
+
+ response =
+ conn
+ |> post("/api/v1/tags/rubrub/unfollow")
+ |> json_response_and_validate_schema(404)
+
+ assert response["error"] == "Hashtag not found"
+ end
+ end
+
+ describe "GET /api/v1/followed_tags" do
+ test "should list followed tags" do
+ %{user: user, conn: conn} = oauth_access(["read:follows"])
+
+ response =
+ conn
+ |> get("/api/v1/followed_tags")
+ |> json_response_and_validate_schema(200)
+
+ assert Enum.empty?(response)
+
+ hashtag = insert(:hashtag, name: "jubjub")
+ {:ok, _user} = User.follow_hashtag(user, hashtag)
+
+ response =
+ conn
+ |> get("/api/v1/followed_tags")
+ |> json_response_and_validate_schema(200)
+
+ assert [%{"name" => "jubjub"}] = response
+ end
+
+ test "should include a link header to paginate" do
+ %{user: user, conn: conn} = oauth_access(["read:follows"])
+
+ for i <- 1..21 do
+ hashtag = insert(:hashtag, name: "jubjub#{i}}")
+ {:ok, _user} = User.follow_hashtag(user, hashtag)
+ end
+
+ response =
+ conn
+ |> get("/api/v1/followed_tags")
+
+ json = json_response_and_validate_schema(response, 200)
+ assert Enum.count(json) == 20
+ assert [link_header] = get_resp_header(response, "link")
+ assert link_header =~ "rel=\"next\""
+ next_link = extract_next_link_header(link_header)
+
+ response =
+ conn
+ |> get(next_link)
+ |> json_response_and_validate_schema(200)
+
+ assert Enum.count(response) == 1
+ end
+
+ test "should refuse access without read:follows scope" do
+ %{conn: conn} = oauth_access(["write"])
+
+ conn
+ |> get("/api/v1/followed_tags")
+ |> json_response_and_validate_schema(403)
+ end
+ end
+
+ defp extract_next_link_header(header) do
+ [_, next_link] = Regex.run(~r{<(?<next_link>.*)>; rel="next"}, header)
+ next_link
+ end
+end
diff --git a/test/pleroma/web/mastodon_api/views/status_view_test.exs b/test/pleroma/web/mastodon_api/views/status_view_test.exs
index bc6dec32a..e6a164d72 100644
--- a/test/pleroma/web/mastodon_api/views/status_view_test.exs
+++ b/test/pleroma/web/mastodon_api/views/status_view_test.exs
@@ -951,6 +951,26 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
assert status.edited_at
end
+ test "it shows post language" do
+ user = insert(:user)
+
+ {:ok, post} = CommonAPI.post(user, %{status: "Szczęść Boże", language: "pl"})
+
+ status = StatusView.render("show.json", activity: post)
+
+ assert status.language == "pl"
+ end
+
+ test "doesn't show post language if it's 'und'" do
+ user = insert(:user)
+
+ {:ok, post} = CommonAPI.post(user, %{status: "sdifjogijodfg", language: "und"})
+
+ status = StatusView.render("show.json", activity: post)
+
+ assert status.language == nil
+ end
+
test "with a source object" do
note =
insert(:note,
diff --git a/test/pleroma/web/media_proxy/media_proxy_controller_test.exs b/test/pleroma/web/media_proxy/media_proxy_controller_test.exs
index f0c1dd640..f7e52483c 100644
--- a/test/pleroma/web/media_proxy/media_proxy_controller_test.exs
+++ b/test/pleroma/web/media_proxy/media_proxy_controller_test.exs
@@ -248,8 +248,8 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyControllerTest do
response = get(conn, url)
- assert response.status == 302
- assert redirected_to(response) == media_proxy_url
+ assert response.status == 301
+ assert redirected_to(response, 301) == media_proxy_url
end
test "with `static` param and non-GIF image preview requested, " <>
@@ -290,8 +290,8 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyControllerTest do
response = get(conn, url)
- assert response.status == 302
- assert redirected_to(response) == media_proxy_url
+ assert response.status == 301
+ assert redirected_to(response, 301) == media_proxy_url
end
test "thumbnails PNG images into PNG", %{
@@ -356,5 +356,32 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyControllerTest do
assert response.status == 302
assert redirected_to(response) == media_proxy_url
end
+
+ test "redirects to media proxy URI with 301 when image is too small for preview", %{
+ conn: conn,
+ url: url,
+ media_proxy_url: media_proxy_url
+ } do
+ clear_config([:media_preview_proxy],
+ enabled: true,
+ min_content_length: 1000,
+ image_quality: 85,
+ thumbnail_max_width: 100,
+ thumbnail_max_height: 100
+ )
+
+ Tesla.Mock.mock(fn
+ %{method: :head, url: ^media_proxy_url} ->
+ %Tesla.Env{
+ status: 200,
+ body: "",
+ headers: [{"content-type", "image/png"}, {"content-length", "500"}]
+ }
+ end)
+
+ response = get(conn, url)
+ assert response.status == 301
+ assert redirected_to(response, 301) == media_proxy_url
+ end
end
end
diff --git a/test/pleroma/web/metadata/providers/activity_pub_test.exs b/test/pleroma/web/metadata/providers/activity_pub_test.exs
new file mode 100644
index 000000000..c5cf78a60
--- /dev/null
+++ b/test/pleroma/web/metadata/providers/activity_pub_test.exs
@@ -0,0 +1,40 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2024 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Metadata.Providers.ActivityPubTest do
+ use Pleroma.DataCase
+ import Pleroma.Factory
+
+ alias Pleroma.Web.CommonAPI
+ alias Pleroma.Web.Metadata.Providers.ActivityPub
+
+ setup do: clear_config([Pleroma.Web.Metadata, :unfurl_nsfw])
+
+ test "it renders a link for user info" do
+ user = insert(:user)
+ res = ActivityPub.build_tags(%{user: user})
+
+ assert res == [
+ {:link, [rel: "alternate", type: "application/activity+json", href: user.ap_id], []}
+ ]
+ end
+
+ test "it renders a link for a post" do
+ user = insert(:user)
+ {:ok, %{id: activity_id, object: object}} = CommonAPI.post(user, %{status: "hi"})
+
+ result = ActivityPub.build_tags(%{object: object, user: user, activity_id: activity_id})
+
+ assert [
+ {:link,
+ [rel: "alternate", type: "application/activity+json", href: object.data["id"]], []}
+ ] == result
+ end
+
+ test "it returns an empty array for anything else" do
+ result = ActivityPub.build_tags(%{})
+
+ assert result == []
+ end
+end
diff --git a/test/pleroma/web/metadata/providers/feed_test.exs b/test/pleroma/web/metadata/providers/feed_test.exs
index e593453da..40d9d0909 100644
--- a/test/pleroma/web/metadata/providers/feed_test.exs
+++ b/test/pleroma/web/metadata/providers/feed_test.exs
@@ -15,4 +15,10 @@ defmodule Pleroma.Web.Metadata.Providers.FeedTest do
[rel: "alternate", type: "application/atom+xml", href: "/users/lain/feed.atom"], []}
]
end
+
+ test "it doesn't render a link to remote user's feed" do
+ user = insert(:user, nickname: "lain@lain.com", local: false)
+
+ assert Feed.build_tags(%{user: user}) == []
+ end
end
diff --git a/test/pleroma/web/o_auth/ldap_authorization_test.exs b/test/pleroma/web/o_auth/ldap_authorization_test.exs
index 07ce2eed8..35b947fd0 100644
--- a/test/pleroma/web/o_auth/ldap_authorization_test.exs
+++ b/test/pleroma/web/o_auth/ldap_authorization_test.exs
@@ -28,11 +28,7 @@ defmodule Pleroma.Web.OAuth.LDAPAuthorizationTest do
{:eldap, [],
[
open: fn [^host], [{:port, ^port}, {:ssl, false} | _] -> {:ok, self()} end,
- simple_bind: fn _connection, _dn, ^password -> :ok end,
- close: fn _connection ->
- send(self(), :close_connection)
- :ok
- end
+ simple_bind: fn _connection, _dn, ^password -> :ok end
]}
] do
conn =
@@ -50,7 +46,6 @@ defmodule Pleroma.Web.OAuth.LDAPAuthorizationTest do
token = Repo.get_by(Token, token: token)
assert token.user_id == user.id
- assert_received :close_connection
end
end
@@ -72,10 +67,6 @@ defmodule Pleroma.Web.OAuth.LDAPAuthorizationTest do
wholeSubtree: fn -> :ok end,
search: fn _connection, _options ->
{:ok, {:eldap_search_result, [{:eldap_entry, ~c"", []}], []}}
- end,
- close: fn _connection ->
- send(self(), :close_connection)
- :ok
end
]}
] do
@@ -94,7 +85,6 @@ defmodule Pleroma.Web.OAuth.LDAPAuthorizationTest do
token = Repo.get_by(Token, token: token) |> Repo.preload(:user)
assert token.user.nickname == user.nickname
- assert_received :close_connection
end
end
@@ -111,11 +101,7 @@ defmodule Pleroma.Web.OAuth.LDAPAuthorizationTest do
{:eldap, [],
[
open: fn [^host], [{:port, ^port}, {:ssl, false} | _] -> {:ok, self()} end,
- simple_bind: fn _connection, _dn, ^password -> {:error, :invalidCredentials} end,
- close: fn _connection ->
- send(self(), :close_connection)
- :ok
- end
+ simple_bind: fn _connection, _dn, ^password -> {:error, :invalidCredentials} end
]}
] do
conn =
@@ -129,7 +115,6 @@ defmodule Pleroma.Web.OAuth.LDAPAuthorizationTest do
})
assert %{"error" => "Invalid credentials"} = json_response(conn, 400)
- assert_received :close_connection
end
end
end
diff --git a/test/pleroma/web/rich_media/card_test.exs b/test/pleroma/web/rich_media/card_test.exs
index 387defc8c..c69f85323 100644
--- a/test/pleroma/web/rich_media/card_test.exs
+++ b/test/pleroma/web/rich_media/card_test.exs
@@ -83,4 +83,23 @@ defmodule Pleroma.Web.RichMedia.CardTest do
Card.get_by_activity(activity)
)
end
+
+ test "refuses to crawl URL in activity from ignored host/domain" do
+ clear_config([:rich_media, :ignore_hosts], ["example.com"])
+
+ user = insert(:user)
+
+ url = "https://example.com/ogp"
+
+ {:ok, activity} =
+ CommonAPI.post(user, %{
+ status: "[test](#{url})",
+ content_type: "text/markdown"
+ })
+
+ refute_enqueued(
+ worker: RichMediaWorker,
+ args: %{"url" => url, "activity_id" => activity.id}
+ )
+ end
end
diff --git a/test/pleroma/web/streamer_test.exs b/test/pleroma/web/streamer_test.exs
index 262ff11d2..85978e824 100644
--- a/test/pleroma/web/streamer_test.exs
+++ b/test/pleroma/web/streamer_test.exs
@@ -558,6 +558,36 @@ defmodule Pleroma.Web.StreamerTest do
assert_receive {:render_with_user, _, "status_update.json", ^create, _}
refute Streamer.filtered_by_user?(user, edited)
end
+
+ test "it streams posts containing followed hashtags on the 'user' stream", %{
+ user: user,
+ token: oauth_token
+ } do
+ hashtag = insert(:hashtag, %{name: "tenshi"})
+ other_user = insert(:user)
+ {:ok, user} = User.follow_hashtag(user, hashtag)
+
+ Streamer.get_topic_and_add_socket("user", user, oauth_token)
+ {:ok, activity} = CommonAPI.post(other_user, %{status: "hey #tenshi"})
+
+ assert_receive {:render_with_user, _, "update.json", ^activity, _}
+ end
+
+ test "should not stream private posts containing followed hashtags on the 'user' stream", %{
+ user: user,
+ token: oauth_token
+ } do
+ hashtag = insert(:hashtag, %{name: "tenshi"})
+ other_user = insert(:user)
+ {:ok, user} = User.follow_hashtag(user, hashtag)
+
+ Streamer.get_topic_and_add_socket("user", user, oauth_token)
+
+ {:ok, activity} =
+ CommonAPI.post(other_user, %{status: "hey #tenshi", visibility: "private"})
+
+ refute_receive {:render_with_user, _, "update.json", ^activity, _}
+ end
end
describe "public streams" do
diff --git a/test/pleroma/web/twitter_api/controller_test.exs b/test/pleroma/web/twitter_api/controller_test.exs
index 495d371d2..494be9ec7 100644
--- a/test/pleroma/web/twitter_api/controller_test.exs
+++ b/test/pleroma/web/twitter_api/controller_test.exs
@@ -69,7 +69,7 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
|> hd()
|> Map.keys()
- assert keys -- ["id", "app_name", "valid_until"] == []
+ assert Enum.sort(keys) == Enum.sort(["id", "app_name", "valid_until", "scopes"])
end
test "revoke token", %{token: token} do
diff --git a/test/pleroma/workers/poll_worker_test.exs b/test/pleroma/workers/poll_worker_test.exs
index 749df8aff..a7cbbdb83 100644
--- a/test/pleroma/workers/poll_worker_test.exs
+++ b/test/pleroma/workers/poll_worker_test.exs
@@ -11,10 +11,10 @@ defmodule Pleroma.Workers.PollWorkerTest do
alias Pleroma.Workers.PollWorker
- test "poll notification job" do
+ test "local poll ending notification job" do
user = insert(:user)
question = insert(:question, user: user)
- activity = insert(:question_activity, question: question)
+ activity = insert(:question_activity, question: question, user: user)
PollWorker.schedule_poll_end(activity)
@@ -44,6 +44,65 @@ defmodule Pleroma.Workers.PollWorkerTest do
# Ensure notifications were streamed out when job executes
assert called(Pleroma.Web.Streamer.stream(["user", "user:notification"], :_))
assert called(Pleroma.Web.Push.send(:_))
+
+ # Skip refreshing polls for local activities
+ assert activity.local
+
+ refute_enqueued(
+ worker: PollWorker,
+ args: %{"op" => "refresh", "activity_id" => activity.id}
+ )
+ end
+ end
+
+ test "remote poll ending notification job schedules refresh" do
+ user = insert(:user, local: false)
+ question = insert(:question, user: user)
+ activity = insert(:question_activity, question: question, user: user)
+
+ PollWorker.schedule_poll_end(activity)
+
+ expected_job_args = %{"activity_id" => activity.id, "op" => "poll_end"}
+
+ assert_enqueued(args: expected_job_args)
+
+ [job] = all_enqueued(worker: PollWorker)
+ PollWorker.perform(job)
+
+ refute activity.local
+
+ assert_enqueued(
+ worker: PollWorker,
+ args: %{"op" => "refresh", "activity_id" => activity.id}
+ )
+ end
+
+ test "poll refresh" do
+ user = insert(:user, local: false)
+ question = insert(:question, user: user)
+ activity = insert(:question_activity, question: question)
+
+ PollWorker.new(%{"op" => "refresh", "activity_id" => activity.id})
+ |> Oban.insert()
+
+ expected_job_args = %{"activity_id" => activity.id, "op" => "refresh"}
+
+ assert_enqueued(args: expected_job_args)
+
+ with_mocks([
+ {
+ Pleroma.Web.Streamer,
+ [],
+ [
+ stream: fn _, _ -> nil end
+ ]
+ }
+ ]) do
+ [job] = all_enqueued(worker: PollWorker)
+ PollWorker.perform(job)
+
+ # Ensure updates are streamed out
+ assert called(Pleroma.Web.Streamer.stream(["user", "list", "public", "public:local"], :_))
end
end
end
diff --git a/test/support/factory.ex b/test/support/factory.ex
index 8f1c6faf9..88c4ed8e5 100644
--- a/test/support/factory.ex
+++ b/test/support/factory.ex
@@ -241,6 +241,7 @@ defmodule Pleroma.Factory do
def question_factory(attrs \\ %{}) do
user = attrs[:user] || insert(:user)
+ closed = attrs[:closed] || DateTime.utc_now() |> DateTime.add(86_400) |> DateTime.to_iso8601()
data = %{
"id" => Pleroma.Web.ActivityPub.Utils.generate_object_id(),
@@ -251,7 +252,7 @@ defmodule Pleroma.Factory do
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
"cc" => [user.follower_address],
"context" => Pleroma.Web.ActivityPub.Utils.generate_context_id(),
- "closed" => DateTime.utc_now() |> DateTime.add(86_400) |> DateTime.to_iso8601(),
+ "closed" => closed,
"content" => "Which flavor of ice cream do you prefer?",
"oneOf" => [
%{
@@ -509,7 +510,8 @@ defmodule Pleroma.Factory do
%Pleroma.Activity{
data: data,
actor: data["actor"],
- recipients: data["to"]
+ recipients: data["to"],
+ local: user.local
}
|> Map.merge(attrs)
end
@@ -666,4 +668,11 @@ defmodule Pleroma.Factory do
|> Map.merge(params)
|> Pleroma.Announcement.add_rendered_properties()
end
+
+ def hashtag_factory(params \\ %{}) do
+ %Pleroma.Hashtag{
+ name: "test #{sequence(:hashtag_name, & &1)}"
+ }
+ |> Map.merge(params)
+ end
end