Merge remote-tracking branch 'origin/develop' into status-notification-type

Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
marcin mikołajczak 2024-05-18 11:30:25 +02:00
commit 2e76ceb5b4
202 changed files with 3806 additions and 1496 deletions

View file

@ -0,0 +1 @@
{"@context":"https://www.w3.org/ns/activitystreams","type":"Note","id":"https://cc.mkdir.uk/ap/note/e5d1d0a1-1ab3-4498-9949-588e3fdea286","attributedTo":"https://cc.mkdir.uk/ap/acct/hiira","inReplyTo":"","quoteUrl":"","content":"おはコンー","published":"2024-01-19T22:08:05Z","to":["https://www.w3.org/ns/activitystreams#Public"],"tag":null,"attachment":[],"object":null}

View file

@ -0,0 +1 @@
{"@context":"https://www.w3.org/ns/activitystreams","type":"Note","id":"https://www.minds.com/api/activitypub/users/1198929502760083472/entities/urn:comment:1600926863310458883:0:0:0:1600932467852709903","attributedTo":"https://www.minds.com/api/activitypub/users/1198929502760083472","content":"\u003Ca class=\u0022u-url mention\u0022 href=\u0022https://www.minds.com/lain\u0022 target=\u0022_blank\u0022\u003E@lain\u003C/a\u003E corn syrup.","to":["https://www.w3.org/ns/activitystreams#Public"],"cc":["https://www.minds.com/api/activitypub/users/1198929502760083472/followers","https://lain.com/users/lain"],"tag":[{"type":"Mention","href":"https://www.minds.com/api/activitypub/users/464237775479123984","name":"@lain"}],"url":"https://www.minds.com/newsfeed/1600926863310458883?focusedCommentUrn=urn:comment:1600926863310458883:0:0:0:1600932467852709903","published":"2024-02-04T17:34:03+00:00","inReplyTo":"https://lain.com/objects/36254095-c839-4167-bcc2-b361d5de9198","source":{"content":"@lain corn syrup.","mediaType":"text/plain"}}

View file

@ -0,0 +1 @@
{"@context":["https://www.w3.org/ns/activitystreams","https://lain.com/schemas/litepub-0.1.jsonld",{"@language":"und"}],"actor":"https://lain.com/users/lain","attachment":[],"attributedTo":"https://lain.com/users/lain","cc":["https://lain.com/users/lain/followers"],"content":"which diet is the best for cognitive dissonance","context":"https://lain.com/contexts/98c8a130-e813-4797-8973-600e80114317","conversation":"https://lain.com/contexts/98c8a130-e813-4797-8973-600e80114317","id":"https://lain.com/objects/36254095-c839-4167-bcc2-b361d5de9198","published":"2024-02-04T17:11:23.931890Z","repliesCount":11,"sensitive":null,"source":{"content":"which diet is the best for cognitive dissonance","mediaType":"text/plain"},"summary":"","tag":[],"to":["https://www.w3.org/ns/activitystreams#Public"],"type":"Note"}

12
test/fixtures/rich_media/google.html vendored Normal file
View file

@ -0,0 +1,12 @@
<meta property="og:url" content="https://google.com">
<meta property="og:type" content="website">
<meta property="og:title" content="Google">
<meta property="og:description" content="Search the world's information, including webpages, images, videos and more. Google has many special features to help you find exactly what you're looking for.">
<meta property="og:image" content="">
<meta name="twitter:card" content="summary_large_image">
<meta property="twitter:domain" content="google.com">
<meta property="twitter:url" content="https://google.com">
<meta name="twitter:title" content="Google">
<meta name="twitter:description" content="Search the world's information, including webpages, images, videos and more. Google has many special features to help you find exactly what you're looking for.">
<meta name="twitter:image" content="">

View file

@ -1,3 +1,3 @@
<link rel="alternate" type="application/json+oembed"
href="http://example.com/oembed.json"
href="https://example.com/oembed.json"
title="Bacon Lollys oEmbed Profile" />

392
test/fixtures/rich_media/reddit.html vendored Normal file

File diff suppressed because one or more lines are too long

12
test/fixtures/rich_media/yahoo.html vendored Normal file
View file

@ -0,0 +1,12 @@
<meta property="og:url" content="https://yahoo.com">
<meta property="og:type" content="website">
<meta property="og:title" content="Yahoo | Mail, Weather, Search, Politics, News, Finance, Sports & Videos">
<meta property="og:description" content="Latest news coverage, email, free stock quotes, live scores and video are just the beginning. Discover more every day at Yahoo!">
<meta property="og:image" content="https://s.yimg.com/cv/apiv2/social/images/yahoo_default_logo.png">
<meta name="twitter:card" content="summary_large_image">
<meta property="twitter:domain" content="yahoo.com">
<meta property="twitter:url" content="https://yahoo.com">
<meta name="twitter:title" content="Yahoo | Mail, Weather, Search, Politics, News, Finance, Sports & Videos">
<meta name="twitter:description" content="Latest news coverage, email, free stock quotes, live scores and video are just the beginning. Discover more every day at Yahoo!">
<meta name="twitter:image" content="https://s.yimg.com/cv/apiv2/social/images/yahoo_default_logo.png">

View file

@ -0,0 +1 @@
{"type":"Question","id":"https://friends.grishka.me/posts/54642","attributedTo":"https://friends.grishka.me/users/1","content":"<p>здесь тоже можно что-то написать отдельно от опроса</p>","published":"2021-09-04T00:22:16Z","url":"https://friends.grishka.me/posts/54642","to":["https://www.w3.org/ns/activitystreams#Public"],"cc":["https://friends.grishka.me/users/1/followers"],"replies":{"type":"Collection","id":"https://friends.grishka.me/posts/54642/replies","first":{"type":"CollectionPage","items":[],"partOf":"https://friends.grishka.me/posts/54642/replies","next":"https://friends.grishka.me/posts/54642/replies?page=1"}},"sensitive":false,"likes":"https://friends.grishka.me/posts/54642/likes","name":"тестовый опрос","oneOf":[{"type":"Note","id":"https://friends.grishka.me/posts/54642#options/76","name":"тестовый ответ 1","replies":{"type":"Collection","id":"https://friends.grishka.me/activitypub/objects/polls/24/options/76/votes","totalItems":4,"items":[]}},{"type":"Note","id":"https://friends.grishka.me/posts/54642#options/77","name":"тестовый ответ 2","replies":{"type":"Collection","id":"https://friends.grishka.me/activitypub/objects/polls/24/options/77/votes","totalItems":4,"items":[]}},{"type":"Note","id":"https://friends.grishka.me/posts/54642#options/78","name":"тестовый ответ 3","replies":{"type":"Collection","id":"https://friends.grishka.me/activitypub/objects/polls/24/options/78/votes","totalItems":6,"items":[]}}],"votersCount":14,"nonAnonymous":true,"@context":["https://www.w3.org/ns/activitystreams",{"sensitive":"as:sensitive","toot":"http://joinmastodon.org/ns#","sm":"http://smithereen.software/ns#","votersCount":"toot:votersCount","nonAnonymous":"sm:nonAnonymous"}]}

View file

@ -0,0 +1 @@
{"type":"Person","id":"https://friends.grishka.me/users/1","name":"Григорий Клюшников","icon":{"type":"Image","image":{"type":"Image","url":"https://friends.grishka.me/i/6QLsOws97AWp5N_osd74C1IC1ijnFopyCBD9MSEeXNQ/q:93/bG9jYWw6Ly8vcy91cGxvYWRzL2F2YXRhcnMvNTYzODRhODEwODk5ZTRjMzI4YmY4YmQwM2Q2MWM3NmMud2VicA.jpg","mediaType":"image/jpeg","width":1280,"height":960},"width":573,"height":572,"cropRegion":[0.26422762870788574,0.3766937553882599,0.7113820910453796,0.9728997349739075],"url":"https://friends.grishka.me/i/ql_49PQcETAWgY_nC-Qj63H_Oa6FyOAEoWFkUSSkUvQ/c:573:572:nowe:338:362/q:93/bG9jYWw6Ly8vcy91cGxvYWRzL2F2YXRhcnMvNTYzODRhODEwODk5ZTRjMzI4YmY4YmQwM2Q2MWM3NmMud2VicA.jpg","mediaType":"image/jpeg"},"summary":"<p>Делаю эту хрень, пытаюсь вырвать социальные сети из жадных лап корпораций</p>\n<p></p>\n<p></p>\n<p></p>\n<p></p>\n<p></p>\n<p></p>\n<p></p>\n<p>This server does NOT support direct messages. Please write me <a href=\"https://t.me/grishka\">on Telegram</a> or <a href=\"https://matrix.to/#/@grishk:matrix.org\">Matrix</a>.</p>","url":"https://friends.grishka.me/grishka","preferredUsername":"grishka","inbox":"https://friends.grishka.me/users/1/inbox","outbox":"https://friends.grishka.me/users/1/outbox","followers":"https://friends.grishka.me/users/1/followers","following":"https://friends.grishka.me/users/1/following","endpoints":{"sharedInbox":"https://friends.grishka.me/activitypub/sharedInbox","collectionSimpleQuery":"https://friends.grishka.me/users/1/collectionQuery"},"publicKey":{"id":"https://friends.grishka.me/users/1#main-key","owner":"https://friends.grishka.me/users/1","publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjlakm+i/d9ER/hIeR7KfiFW+SdLZj2SkKIeM8cmR+YFJuh9ghFqXrkFEjcaqUnAFqe5gYDNSQACnDLA8y4DnzjfGNIohKAnRoa9x6GORmfKQvcnjaTZ53S1NvUiPPyc0Pv/vfCtY/Ab0CEXe5BLqL38oZn817Jf7pBrPRTYH7m012kvwAUTT6k0Y8lPITBEG7nzYbbuGcrN9Y/RDdwE08jmBXlZ45bahRH3VNXVpQE17dCzJB+7k+iJ1R7YCoI+DuMlBYGXGE2KVk46NZTuLnOjFV9SyXfWX4/SrJM4oxev+SX2N75tQgmNZmVVHeqg2ZcbC0WCfNjJOi2HHS9MujwIDAQAB\n-----END PUBLIC KEY-----\n"},"wall":"https://friends.grishka.me/users/1/wall","firstName":"Григорий","lastName":"Клюшников","middleName":"Александрович","vcard:bday":"1993-01-22","gender":"http://schema.org#Male","supportsFriendRequests":true,"friends":"https://friends.grishka.me/users/1/friends","groups":"https://friends.grishka.me/users/1/groups","capabilities":{"supportsFriendRequests":true},"@context":["https://www.w3.org/ns/activitystreams",{"sm":"http://smithereen.software/ns#","cropRegion":{"@id":"sm:cropRegion","@container":"@list"},"wall":{"@id":"sm:wall","@type":"@id"},"collectionSimpleQuery":"sm:collectionSimpleQuery","sc":"http://schema.org#","firstName":"sc:givenName","lastName":"sc:familyName","middleName":"sc:additionalName","gender":{"@id":"sc:gender","@type":"sc:GenderType"},"maidenName":"sm:maidenName","friends":{"@id":"sm:friends","@type":"@id"},"groups":{"@id":"sm:groups","@type":"@id"},"vcard":"http://www.w3.org/2006/vcard/ns#","capabilities":"litepub:capabilities","supportsFriendRequests":"sm:supportsFriendRequests","litepub":"http://litepub.social/ns#"},"https://w3id.org/security/v1"]}

View file

@ -0,0 +1,60 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2024 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.BookmarkFolderTest do
use Pleroma.DataCase, async: true
import Pleroma.Factory
alias Pleroma.BookmarkFolder
describe "create/3" do
test "with valid params" do
user = insert(:user)
{:ok, folder} = BookmarkFolder.create(user.id, "Read later", "🕓")
assert folder.user_id == user.id
assert folder.name == "Read later"
assert folder.emoji == "🕓"
end
test "with invalid params" do
{:error, changeset} = BookmarkFolder.create(nil, "", "not an emoji")
refute changeset.valid?
assert changeset.errors == [
emoji: {"Invalid emoji", []},
user_id: {"can't be blank", [validation: :required]},
name: {"can't be blank", [validation: :required]}
]
end
end
test "update/3" do
user = insert(:user)
{:ok, folder} = BookmarkFolder.create(user.id, "Read ltaer")
{:ok, folder} = BookmarkFolder.update(folder.id, "Read later")
assert folder.name == "Read later"
end
test "for_user/1" do
user = insert(:user)
other_user = insert(:user)
{:ok, _} = BookmarkFolder.create(user.id, "Folder 1")
{:ok, _} = BookmarkFolder.create(user.id, "Folder 2")
{:ok, _} = BookmarkFolder.create(other_user.id, "Folder 3")
folders = BookmarkFolder.for_user(user.id)
assert length(folders) == 2
end
test "belongs_to_user?/2" do
user = insert(:user)
other_user = insert(:user)
{:ok, folder} = BookmarkFolder.create(user.id, "Folder")
assert true == BookmarkFolder.belongs_to_user?(folder.id, user.id)
assert false == BookmarkFolder.belongs_to_user?(folder.id, other_user.id)
end
end

View file

@ -6,15 +6,17 @@ defmodule Pleroma.BookmarkTest do
use Pleroma.DataCase, async: true
import Pleroma.Factory
alias Pleroma.Bookmark
alias Pleroma.BookmarkFolder
alias Pleroma.Web.CommonAPI
describe "create/2" do
describe "create/3" do
test "with valid params" do
user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{status: "Some cool information"})
{:ok, bookmark} = Bookmark.create(user.id, activity.id)
assert bookmark.user_id == user.id
assert bookmark.activity_id == activity.id
assert bookmark.folder_id == nil
end
test "with invalid params" do
@ -26,6 +28,19 @@ defmodule Pleroma.BookmarkTest do
activity_id: {"can't be blank", [validation: :required]}
]
end
test "update existing bookmark folder" do
user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{status: "Some cool information"})
{:ok, bookmark} = Bookmark.create(user.id, activity.id)
assert bookmark.folder_id == nil
{:ok, bookmark_folder} = BookmarkFolder.create(user.id, "Read later")
{:ok, bookmark} = Bookmark.create(user.id, activity.id, bookmark_folder.id)
assert bookmark.folder_id == bookmark_folder.id
end
end
describe "destroy/2" do

View file

@ -202,7 +202,7 @@ defmodule Pleroma.HTMLTest do
})
object = Object.normalize(activity, fetch: false)
{:ok, url} = HTML.extract_first_external_url_from_object(object)
url = HTML.extract_first_external_url_from_object(object)
assert url == "https://github.com/komeiji-satori/Dress"
end
@ -217,7 +217,7 @@ defmodule Pleroma.HTMLTest do
})
object = Object.normalize(activity, fetch: false)
{:ok, url} = HTML.extract_first_external_url_from_object(object)
url = HTML.extract_first_external_url_from_object(object)
assert url == "https://github.com/syuilo/misskey/blob/develop/docs/setup.en.md"
@ -233,7 +233,7 @@ defmodule Pleroma.HTMLTest do
})
object = Object.normalize(activity, fetch: false)
{:ok, url} = HTML.extract_first_external_url_from_object(object)
url = HTML.extract_first_external_url_from_object(object)
assert url == "https://www.pixiv.net/member_illust.php?mode=medium&illust_id=72255140"
end
@ -249,7 +249,7 @@ defmodule Pleroma.HTMLTest do
})
object = Object.normalize(activity, fetch: false)
{:ok, url} = HTML.extract_first_external_url_from_object(object)
url = HTML.extract_first_external_url_from_object(object)
assert url == "https://www.pixiv.net/member_illust.php?mode=medium&illust_id=72255140"
end
@ -261,7 +261,7 @@ defmodule Pleroma.HTMLTest do
object = Object.normalize(activity, fetch: false)
assert {:ok, nil} = HTML.extract_first_external_url_from_object(object)
assert nil == HTML.extract_first_external_url_from_object(object)
end
test "skips attachment links" do
@ -275,7 +275,7 @@ defmodule Pleroma.HTMLTest do
object = Object.normalize(activity, fetch: false)
assert {:ok, nil} = HTML.extract_first_external_url_from_object(object)
assert nil == HTML.extract_first_external_url_from_object(object)
end
end
end

View file

@ -268,17 +268,6 @@ 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()

View file

@ -0,0 +1,22 @@
# Pleroma: A lightweight social networking server
# Copyright © 2024 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.MapsTest do
use Pleroma.DataCase, async: true
alias Pleroma.Maps
describe "filter_empty_values/1" do
assert %{"bar" => "b", "ray" => ["foo"], "objs" => %{"a" => "b"}} ==
Maps.filter_empty_values(%{
"foo" => nil,
"fooz" => "",
"bar" => "b",
"rei" => [],
"ray" => ["foo"],
"obj" => %{},
"objs" => %{"a" => "b"}
})
end
end

View file

@ -6,7 +6,6 @@ defmodule Pleroma.NotificationTest do
use Pleroma.DataCase, async: false
import Pleroma.Factory
import Mock
alias Pleroma.FollowingRelationship
alias Pleroma.Notification
@ -18,8 +17,6 @@ defmodule Pleroma.NotificationTest do
alias Pleroma.Web.ActivityPub.Transmogrifier
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.MastodonAPI.NotificationView
alias Pleroma.Web.Push
alias Pleroma.Web.Streamer
setup do
Mox.stub_with(Pleroma.UnstubbedConfigMock, Pleroma.Config)
@ -191,158 +188,7 @@ defmodule Pleroma.NotificationTest do
assert [user2.id, user3.id, user1.id] == Enum.map(notifications, & &1.user_id)
end
describe "CommonApi.post/2 notification-related functionality" do
test_with_mock "creates but does NOT send notification to blocker user",
Push,
[:passthrough],
[] do
user = insert(:user)
blocker = insert(:user)
{:ok, _user_relationship} = User.block(blocker, user)
{:ok, _activity} = CommonAPI.post(user, %{status: "hey @#{blocker.nickname}!"})
blocker_id = blocker.id
assert [%Notification{user_id: ^blocker_id}] = Repo.all(Notification)
refute called(Push.send(:_))
end
test_with_mock "creates but does NOT send notification to notification-muter user",
Push,
[:passthrough],
[] do
user = insert(:user)
muter = insert(:user)
{:ok, _user_relationships} = User.mute(muter, user)
{:ok, _activity} = CommonAPI.post(user, %{status: "hey @#{muter.nickname}!"})
muter_id = muter.id
assert [%Notification{user_id: ^muter_id}] = Repo.all(Notification)
refute called(Push.send(:_))
end
test_with_mock "creates but does NOT send notification to thread-muter user",
Push,
[:passthrough],
[] do
user = insert(:user)
thread_muter = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{status: "hey @#{thread_muter.nickname}!"})
{:ok, _} = CommonAPI.add_mute(thread_muter, activity)
{:ok, _same_context_activity} =
CommonAPI.post(user, %{
status: "hey-hey-hey @#{thread_muter.nickname}!",
in_reply_to_status_id: activity.id
})
[pre_mute_notification, post_mute_notification] =
Repo.all(from(n in Notification, where: n.user_id == ^thread_muter.id, order_by: n.id))
pre_mute_notification_id = pre_mute_notification.id
post_mute_notification_id = post_mute_notification.id
assert called(
Push.send(
:meck.is(fn
%Notification{id: ^pre_mute_notification_id} -> true
_ -> false
end)
)
)
refute called(
Push.send(
:meck.is(fn
%Notification{id: ^post_mute_notification_id} -> true
_ -> false
end)
)
)
end
end
describe "create_notification" do
@tag needs_streamer: true
test "it creates a notification for user and send to the 'user' and the 'user:notification' stream" do
%{user: user, token: oauth_token} = oauth_access(["read"])
task =
Task.async(fn ->
{:ok, _topic} = Streamer.get_topic_and_add_socket("user", user, oauth_token)
assert_receive {:render_with_user, _, _, _, _}, 4_000
end)
task_user_notification =
Task.async(fn ->
{:ok, _topic} =
Streamer.get_topic_and_add_socket("user:notification", user, oauth_token)
assert_receive {:render_with_user, _, _, _, _}, 4_000
end)
activity = insert(:note_activity)
notify = Notification.create_notification(activity, user)
assert notify.user_id == user.id
Task.await(task)
Task.await(task_user_notification)
end
test "it creates a notification for user if the user blocks the activity author" do
activity = insert(:note_activity)
author = User.get_cached_by_ap_id(activity.data["actor"])
user = insert(:user)
{:ok, _user_relationship} = User.block(user, author)
assert Notification.create_notification(activity, user)
end
test "it creates a notification for the user if the user mutes the activity author" do
muter = insert(:user)
muted = insert(:user)
{:ok, _} = User.mute(muter, muted)
muter = Repo.get(User, muter.id)
{:ok, activity} = CommonAPI.post(muted, %{status: "Hi @#{muter.nickname}"})
notification = Notification.create_notification(activity, muter)
assert notification.id
assert notification.seen
end
test "notification created if user is muted without notifications" do
muter = insert(:user)
muted = insert(:user)
{:ok, _user_relationships} = User.mute(muter, muted, %{notifications: false})
{:ok, activity} = CommonAPI.post(muted, %{status: "Hi @#{muter.nickname}"})
assert Notification.create_notification(activity, muter)
end
test "it creates a notification for an activity from a muted thread" do
muter = insert(:user)
other_user = insert(:user)
{:ok, activity} = CommonAPI.post(muter, %{status: "hey"})
CommonAPI.add_mute(muter, activity)
{:ok, activity} =
CommonAPI.post(other_user, %{
status: "Hi @#{muter.nickname}",
in_reply_to_status_id: activity.id
})
notification = Notification.create_notification(activity, muter)
assert notification.id
assert notification.seen
end
test "it disables notifications from strangers" do
follower = insert(:user)
@ -696,7 +542,7 @@ defmodule Pleroma.NotificationTest do
status: "hey @#{other_user.nickname}!"
})
{enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity)
enabled_receivers = Notification.get_notified_from_activity(activity)
assert other_user in enabled_receivers
end
@ -728,7 +574,7 @@ defmodule Pleroma.NotificationTest do
{:ok, activity} = Transmogrifier.handle_incoming(create_activity)
{enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity)
enabled_receivers = Notification.get_notified_from_activity(activity)
assert other_user in enabled_receivers
end
@ -755,7 +601,7 @@ defmodule Pleroma.NotificationTest do
{:ok, activity} = Transmogrifier.handle_incoming(create_activity)
{enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity)
enabled_receivers = Notification.get_notified_from_activity(activity)
assert other_user not in enabled_receivers
end
@ -772,8 +618,7 @@ defmodule Pleroma.NotificationTest do
{:ok, activity_two} = CommonAPI.favorite(third_user, activity_one.id)
{enabled_receivers, _disabled_receivers} =
Notification.get_notified_from_activity(activity_two)
enabled_receivers = Notification.get_notified_from_activity(activity_two)
assert other_user not in enabled_receivers
end
@ -795,7 +640,7 @@ defmodule Pleroma.NotificationTest do
|> Map.put("to", [other_user.ap_id | like_data["to"]])
|> ActivityPub.persist(local: true)
{enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(like)
enabled_receivers = Notification.get_notified_from_activity(like)
assert other_user not in enabled_receivers
end
@ -812,39 +657,36 @@ defmodule Pleroma.NotificationTest do
{:ok, activity_two} = CommonAPI.repeat(activity_one.id, third_user)
{enabled_receivers, _disabled_receivers} =
Notification.get_notified_from_activity(activity_two)
enabled_receivers = Notification.get_notified_from_activity(activity_two)
assert other_user not in enabled_receivers
end
test "it returns blocking recipient in disabled recipients list" do
test "it does not return blocking recipient in recipients list" do
user = insert(:user)
other_user = insert(:user)
{:ok, _user_relationship} = User.block(other_user, user)
{:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
{enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
enabled_receivers = Notification.get_notified_from_activity(activity)
assert [] == enabled_receivers
assert [other_user] == disabled_receivers
end
test "it returns notification-muting recipient in disabled recipients list" do
test "it does not return notification-muting recipient in recipients list" do
user = insert(:user)
other_user = insert(:user)
{:ok, _user_relationships} = User.mute(other_user, user)
{:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
{enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
enabled_receivers = Notification.get_notified_from_activity(activity)
assert [] == enabled_receivers
assert [other_user] == disabled_receivers
end
test "it returns thread-muting recipient in disabled recipients list" do
test "it does not return thread-muting recipient in recipients list" do
user = insert(:user)
other_user = insert(:user)
@ -858,14 +700,12 @@ defmodule Pleroma.NotificationTest do
in_reply_to_status_id: activity.id
})
{enabled_receivers, disabled_receivers} =
Notification.get_notified_from_activity(same_context_activity)
enabled_receivers = Notification.get_notified_from_activity(same_context_activity)
assert [other_user] == disabled_receivers
refute other_user in enabled_receivers
end
test "it returns non-following domain-blocking recipient in disabled recipients list" do
test "it does not return non-following domain-blocking recipient in recipients list" do
blocked_domain = "blocked.domain"
user = insert(:user, %{ap_id: "https://#{blocked_domain}/@actor"})
other_user = insert(:user)
@ -874,10 +714,9 @@ defmodule Pleroma.NotificationTest do
{:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
{enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
enabled_receivers = Notification.get_notified_from_activity(activity)
assert [] == enabled_receivers
assert [other_user] == disabled_receivers
end
test "it returns following domain-blocking recipient in enabled recipients list" do
@ -890,10 +729,9 @@ defmodule Pleroma.NotificationTest do
{:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
{enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
enabled_receivers = Notification.get_notified_from_activity(activity)
assert [other_user] == enabled_receivers
assert [] == disabled_receivers
end
test "it sends edited notifications to those who repeated a status" do
@ -913,11 +751,10 @@ defmodule Pleroma.NotificationTest do
status: "hey @#{other_user.nickname}! mew mew"
})
{enabled_receivers, _disabled_receivers} =
Notification.get_notified_from_activity(edit_activity)
enabled_receivers = Notification.get_notified_from_activity(edit_activity)
assert repeated_user in enabled_receivers
assert other_user not in enabled_receivers
refute other_user in enabled_receivers
end
end
@ -1205,13 +1042,13 @@ defmodule Pleroma.NotificationTest do
assert Notification.for_user(user) == []
end
test "it returns notifications from a muted user when with_muted is set", %{user: user} do
test "it doesn't return notifications from a muted user when with_muted is set", %{user: user} do
muted = insert(:user)
{:ok, _user_relationships} = User.mute(user, muted)
{:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"})
assert length(Notification.for_user(user, %{with_muted: true})) == 1
assert Enum.empty?(Notification.for_user(user, %{with_muted: true}))
end
test "it doesn't return notifications from a blocked user when with_muted is set", %{

View file

@ -0,0 +1,57 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.RuleTest do
use Pleroma.DataCase, async: true
alias Pleroma.Repo
alias Pleroma.Rule
test "getting a list of rules sorted by priority" do
%{id: id1} = Rule.create(%{text: "Example rule"})
%{id: id2} = Rule.create(%{text: "Second rule", priority: 2})
%{id: id3} = Rule.create(%{text: "Third rule", priority: 1})
rules =
Rule.query()
|> Repo.all()
assert [%{id: ^id1}, %{id: ^id3}, %{id: ^id2}] = rules
end
test "creating rules" do
%{id: id} = Rule.create(%{text: "Example rule"})
assert %{text: "Example rule"} = Rule.get(id)
end
test "editing rules" do
%{id: id} = Rule.create(%{text: "Example rule"})
Rule.update(%{text: "There are no rules", priority: 2}, id)
assert %{text: "There are no rules", priority: 2} = Rule.get(id)
end
test "deleting rules" do
%{id: id} = Rule.create(%{text: "Example rule"})
Rule.delete(id)
assert [] =
Rule.query()
|> Pleroma.Repo.all()
end
test "getting rules by ids" do
%{id: id1} = Rule.create(%{text: "Example rule"})
%{id: id2} = Rule.create(%{text: "Second rule"})
%{id: _id3} = Rule.create(%{text: "Third rule"})
rules = Rule.get([id1, id2])
assert Enum.all?(rules, &(&1.id in [id1, id2]))
assert length(rules) == 2
end
end

View file

@ -35,21 +35,6 @@ defmodule Pleroma.Search.DatabaseSearchTest do
assert [] = Search.search(nil, "wednesday")
end
test "using plainto_tsquery on postgres < 11" do
old_version = :persistent_term.get({Pleroma.Repo, :postgres_version})
:persistent_term.put({Pleroma.Repo, :postgres_version}, 10.0)
on_exit(fn -> :persistent_term.put({Pleroma.Repo, :postgres_version}, old_version) end)
user = insert(:user)
{:ok, post} = CommonAPI.post(user, %{status: "it's wednesday my dudes"})
{:ok, _post2} = CommonAPI.post(user, %{status: "it's wednesday my bros"})
# plainto doesn't understand complex queries
assert [result] = Search.search(nil, "wednesday -dudes")
assert result.id == post.id
end
test "using websearch_to_tsquery" do
user = insert(:user)
{:ok, _post} = CommonAPI.post(user, %{status: "it's wednesday my dudes"})

View file

@ -166,6 +166,7 @@ defmodule Pleroma.User.BackupTest do
test "it creates a zip archive with user data" do
user = insert(:user, %{nickname: "cofe", name: "Cofe", ap_id: "http://cofe.io/users/cofe"})
%{ap_id: other_ap_id} = other_user = insert(:user)
{:ok, %{object: %{data: %{"id" => id1}}} = status1} =
CommonAPI.post(user, %{status: "status1"})
@ -182,6 +183,8 @@ defmodule Pleroma.User.BackupTest do
Bookmark.create(user.id, status2.id)
Bookmark.create(user.id, status3.id)
CommonAPI.follow(user, other_user)
assert {:ok, backup} = user |> Backup.new() |> Repo.insert()
assert {:ok, path} = Backup.export(backup, self())
assert {:ok, zipfile} = :zip.zip_open(String.to_charlist(path), [:memory])
@ -261,6 +264,16 @@ defmodule Pleroma.User.BackupTest do
"type" => "OrderedCollection"
} = Jason.decode!(json)
assert {:ok, {'following.json', json}} = :zip.zip_get('following.json', zipfile)
assert %{
"@context" => "https://www.w3.org/ns/activitystreams",
"id" => "following.json",
"orderedItems" => [^other_ap_id],
"totalItems" => 1,
"type" => "OrderedCollection"
} = Jason.decode!(json)
:zip.zip_close(zipfile)
File.rm!(path)
end

View file

@ -2928,4 +2928,51 @@ defmodule Pleroma.UserTest do
refute User.endorses?(user, pinned_user)
end
end
test "it checks fields links for a backlink" do
user = insert(:user, ap_id: "https://social.example.org/users/lain")
fields = [
%{"name" => "Link", "value" => "http://example.com/rel_me/null"},
%{"name" => "Verified link", "value" => "http://example.com/rel_me/link"},
%{"name" => "Not a link", "value" => "i'm not a link"}
]
user
|> User.update_and_set_cache(%{raw_fields: fields})
ObanHelpers.perform_all()
user = User.get_cached_by_id(user.id)
assert [
%{"verified_at" => nil},
%{"verified_at" => verified_at},
%{"verified_at" => nil}
] = user.fields
assert is_binary(verified_at)
end
test "updating fields does not invalidate previously validated links" do
user = insert(:user, ap_id: "https://social.example.org/users/lain")
user
|> User.update_and_set_cache(%{
raw_fields: [%{"name" => "verified link", "value" => "http://example.com/rel_me/link"}]
})
ObanHelpers.perform_all()
%User{fields: [%{"verified_at" => verified_at}]} = user = User.get_cached_by_id(user.id)
user
|> User.update_and_set_cache(%{
raw_fields: [%{"name" => "Verified link", "value" => "http://example.com/rel_me/link"}]
})
user = User.get_cached_by_id(user.id)
assert [%{"verified_at" => ^verified_at}] = user.fields
end
end

View file

@ -0,0 +1,73 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2024 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.ForceMentionTest do
use Pleroma.DataCase
require Pleroma.Constants
alias Pleroma.Web.ActivityPub.MRF.ForceMention
import Pleroma.Factory
test "adds mention to a reply" do
lain =
insert(:user, ap_id: "https://lain.com/users/lain", nickname: "lain@lain.com", local: false)
niobleoum =
insert(:user,
ap_id: "https://www.minds.com/api/activitypub/users/1198929502760083472",
nickname: "niobleoum@minds.com",
local: false
)
status = File.read!("test/fixtures/minds-pleroma-mentioned-post.json") |> Jason.decode!()
status_activity = %{
"type" => "Create",
"actor" => lain.ap_id,
"object" => status
}
Pleroma.Web.ActivityPub.Transmogrifier.handle_incoming(status_activity)
reply = File.read!("test/fixtures/minds-invalid-mention-post.json") |> Jason.decode!()
reply_activity = %{
"type" => "Create",
"actor" => niobleoum.ap_id,
"object" => reply
}
{:ok, %{"object" => %{"tag" => tag}}} = ForceMention.filter(reply_activity)
assert Enum.find(tag, fn %{"href" => href} -> href == lain.ap_id end)
end
test "adds mention to a quote" do
user1 = insert(:user, ap_id: "https://misskey.io/users/83ssedkv53")
user2 = insert(:user, ap_id: "https://misskey.io/users/7rkrarq81i")
status = File.read!("test/fixtures/tesla_mock/misskey.io_8vs6wxufd0.json") |> Jason.decode!()
status_activity = %{
"type" => "Create",
"actor" => user1.ap_id,
"object" => status
}
Pleroma.Web.ActivityPub.Transmogrifier.handle_incoming(status_activity)
quote_post = File.read!("test/fixtures/quote_post/misskey_quote_post.json") |> Jason.decode!()
quote_activity = %{
"type" => "Create",
"actor" => user2.ap_id,
"object" => quote_post
}
{:ok, %{"object" => %{"tag" => tag}}} = ForceMention.filter(quote_activity)
assert Enum.find(tag, fn %{"href" => href} -> href == user1.ap_id end)
end
end

View file

@ -87,6 +87,32 @@ defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicyTest do
assert File.exists?(fullpath)
end
test "rejects invalid shortcodes", %{path: path} do
message = %{
"type" => "Create",
"object" => %{
"emoji" => [{"fired/fox", "https://example.org/emoji/firedfox"}],
"actor" => "https://example.org/users/admin"
}
}
fullpath = Path.join(path, "fired/fox.png")
Tesla.Mock.mock(fn %{method: :get, url: "https://example.org/emoji/firedfox"} ->
%Tesla.Env{status: 200, body: File.read!("test/fixtures/image.jpg")}
end)
clear_config(:mrf_steal_emoji, hosts: ["example.org"], size_limit: 284_468)
refute "firedfox" in installed()
refute File.exists?(path)
assert {:ok, _message} = StealEmojiPolicy.filter(message)
refute "fired/fox" in installed()
refute File.exists?(fullpath)
end
test "reject regex shortcode", %{message: message} do
refute "firedfox" in installed()

View file

@ -93,6 +93,17 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidatorTest
%{valid?: true} = ArticleNotePageValidator.cast_and_validate(note)
end
test "a Note from Convergence AP Bridge validates" do
insert(:user, ap_id: "https://cc.mkdir.uk/ap/acct/hiira")
note =
"test/fixtures/ccworld-ap-bridge_note.json"
|> File.read!()
|> Jason.decode!()
%{valid?: true} = ArticleNotePageValidator.cast_and_validate(note)
end
test "a note with an attachment should work", _ do
insert(:user, %{ap_id: "https://owncast.localhost.localdomain/federation/user/streamer"})

View file

@ -25,6 +25,17 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do
setup_all do: clear_config([:instance, :federating], true)
describe "should_federate?/1" do
test "it returns false when the inbox is nil" do
refute Publisher.should_federate?(nil, false)
refute Publisher.should_federate?(nil, true)
end
test "it returns true when public is true" do
assert Publisher.should_federate?(false, true)
end
end
describe "gather_webfinger_links/1" do
test "it returns links" do
user = insert(:user)
@ -205,6 +216,7 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do
refute called(Instances.set_reachable(inbox))
end
@tag capture_log: true
test_with_mock "calls `Instances.set_unreachable` on target inbox on non-2xx HTTP response code",
Instances,
[:passthrough],

View file

@ -827,31 +827,6 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do
{:ok, announce, _} = SideEffects.handle(announce)
assert Repo.get_by(Notification, user_id: poster.id, activity_id: announce.id)
end
test "it streams out the announce", %{announce: announce} do
with_mocks([
{
Pleroma.Web.Streamer,
[],
[
stream: fn _, _ -> nil end
]
},
{
Pleroma.Web.Push,
[],
[
send: fn _ -> nil end
]
}
]) do
{:ok, announce, _} = SideEffects.handle(announce)
assert called(Pleroma.Web.Streamer.stream(["user", "list"], announce))
assert called(Pleroma.Web.Push.send(:_))
end
end
end
describe "removing a follower" do

View file

@ -91,6 +91,13 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
assert %{"alsoKnownAs" => ^akas} = UserView.render("user.json", %{user: user})
end
test "renders full nickname" do
clear_config([Pleroma.Web.WebFinger, :domain], "plemora.dev")
user = insert(:user, nickname: "user")
assert %{"webfinger" => "acct:user@plemora.dev"} = UserView.render("user.json", %{user: user})
end
describe "endpoints" do
test "local users have a usable endpoints structure" do
user = insert(:user)

View file

@ -11,6 +11,7 @@ defmodule Pleroma.Web.AdminAPI.ReportControllerTest do
alias Pleroma.ModerationLog
alias Pleroma.Repo
alias Pleroma.ReportNote
alias Pleroma.Rule
alias Pleroma.Web.CommonAPI
setup do
@ -436,6 +437,34 @@ defmodule Pleroma.Web.AdminAPI.ReportControllerTest do
"error" => "Invalid credentials."
}
end
test "returns reports with specified role_id", %{conn: conn} do
[reporter, target_user] = insert_pair(:user)
%{id: rule_id} = Rule.create(%{text: "Example rule"})
rule_id = to_string(rule_id)
{:ok, %{id: report_id}} =
CommonAPI.report(reporter, %{
account_id: target_user.id,
comment: "",
rule_ids: [rule_id]
})
{:ok, _report} =
CommonAPI.report(reporter, %{
account_id: target_user.id,
comment: ""
})
response =
conn
|> get("/api/pleroma/admin/reports?rule_id=#{rule_id}")
|> json_response_and_validate_schema(:ok)
assert %{"reports" => [%{"id" => ^report_id}]} = response
end
end
describe "POST /api/pleroma/admin/reports/:id/notes" do

View file

@ -0,0 +1,82 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.RuleControllerTest do
use Pleroma.Web.ConnCase, async: true
import Pleroma.Factory
alias Pleroma.Rule
setup do
admin = insert(:user, is_admin: true)
token = insert(:oauth_admin_token, user: admin)
conn =
build_conn()
|> assign(:user, admin)
|> assign(:token, token)
{:ok, %{admin: admin, token: token, conn: conn}}
end
describe "GET /api/pleroma/admin/rules" do
test "sorts rules by priority", %{conn: conn} do
%{id: id1} = Rule.create(%{text: "Example rule"})
%{id: id2} = Rule.create(%{text: "Second rule", priority: 2})
%{id: id3} = Rule.create(%{text: "Third rule", priority: 1})
id1 = to_string(id1)
id2 = to_string(id2)
id3 = to_string(id3)
response =
conn
|> get("/api/pleroma/admin/rules")
|> json_response_and_validate_schema(:ok)
assert [%{"id" => ^id1}, %{"id" => ^id3}, %{"id" => ^id2}] = response
end
end
describe "POST /api/pleroma/admin/rules" do
test "creates a rule", %{conn: conn} do
%{"id" => id} =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/admin/rules", %{text: "Example rule"})
|> json_response_and_validate_schema(:ok)
assert %{text: "Example rule"} = Rule.get(id)
end
end
describe "PATCH /api/pleroma/admin/rules" do
test "edits a rule", %{conn: conn} do
%{id: id} = Rule.create(%{text: "Example rule"})
conn
|> put_req_header("content-type", "application/json")
|> patch("/api/pleroma/admin/rules/#{id}", %{text: "There are no rules", priority: 2})
|> json_response_and_validate_schema(:ok)
assert %{text: "There are no rules", priority: 2} = Rule.get(id)
end
end
describe "DELETE /api/pleroma/admin/rules" do
test "deletes a rule", %{conn: conn} do
%{id: id} = Rule.create(%{text: "Example rule"})
conn
|> put_req_header("content-type", "application/json")
|> delete("/api/pleroma/admin/rules/#{id}")
|> json_response_and_validate_schema(:ok)
assert [] =
Rule.query()
|> Pleroma.Repo.all()
end
end
end

View file

@ -7,6 +7,7 @@ defmodule Pleroma.Web.AdminAPI.ReportViewTest do
import Pleroma.Factory
alias Pleroma.Rule
alias Pleroma.Web.AdminAPI
alias Pleroma.Web.AdminAPI.Report
alias Pleroma.Web.AdminAPI.ReportView
@ -38,7 +39,8 @@ defmodule Pleroma.Web.AdminAPI.ReportViewTest do
statuses: [],
notes: [],
state: "open",
id: activity.id
id: activity.id,
rules: []
}
result =
@ -76,7 +78,8 @@ defmodule Pleroma.Web.AdminAPI.ReportViewTest do
statuses: [StatusView.render("show.json", %{activity: activity})],
state: "open",
notes: [],
id: report_activity.id
id: report_activity.id,
rules: []
}
result =
@ -168,4 +171,22 @@ defmodule Pleroma.Web.AdminAPI.ReportViewTest do
assert report2.id == rendered |> Enum.at(0) |> Map.get(:id)
assert report1.id == rendered |> Enum.at(1) |> Map.get(:id)
end
test "renders included rules" do
user = insert(:user)
other_user = insert(:user)
%{id: rule_id, text: text} = Rule.create(%{text: "Example rule"})
rule_id = to_string(rule_id)
{:ok, activity} =
CommonAPI.report(user, %{
account_id: other_user.id,
rule_ids: [rule_id]
})
assert %{rules: [%{id: ^rule_id, text: ^text}]} =
ReportView.render("show.json", Report.extract_report_info(activity))
end
end

View file

@ -12,6 +12,7 @@ defmodule Pleroma.Web.CommonAPITest do
alias Pleroma.Notification
alias Pleroma.Object
alias Pleroma.Repo
alias Pleroma.Rule
alias Pleroma.UnstubbedConfigMock, as: ConfigMock
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
@ -1363,6 +1364,33 @@ defmodule Pleroma.Web.CommonAPITest do
assert first_report.data["state"] == "resolved"
assert second_report.data["state"] == "resolved"
end
test "creates a report with provided rules" do
reporter = insert(:user)
target_user = insert(:user)
%{id: rule_id} = Rule.create(%{text: "There are no rules"})
reporter_ap_id = reporter.ap_id
target_ap_id = target_user.ap_id
report_data = %{
account_id: target_user.id,
rule_ids: [rule_id]
}
assert {:ok, flag_activity} = CommonAPI.report(reporter, report_data)
assert %Activity{
actor: ^reporter_ap_id,
data: %{
"type" => "Flag",
"object" => [^target_ap_id],
"state" => "open",
"rules" => [^rule_id]
}
} = flag_activity
end
end
describe "reblog muting" do

View file

@ -6,6 +6,7 @@ defmodule Pleroma.Web.MastodonAPI.InstanceControllerTest do
# TODO: Should not need Cachex
use Pleroma.Web.ConnCase
alias Pleroma.Rule
alias Pleroma.User
import Pleroma.Factory
@ -40,7 +41,8 @@ defmodule Pleroma.Web.MastodonAPI.InstanceControllerTest do
"banner_upload_limit" => _,
"background_image" => from_config_background,
"shout_limit" => _,
"description_limit" => _
"description_limit" => _,
"rules" => _
} = result
assert result["pleroma"]["metadata"]["account_activation_required"] != nil
@ -107,10 +109,47 @@ defmodule Pleroma.Web.MastodonAPI.InstanceControllerTest do
|> json_response_and_validate_schema(200)
end
test "get instance contact information", %{conn: conn} do
user = insert(:user, %{local: true})
clear_config([:instance, :contact_username], user.nickname)
conn = get(conn, "/api/v1/instance")
assert result = json_response_and_validate_schema(conn, 200)
assert result["contact_account"]["id"] == user.id
end
test "get instance information v2", %{conn: conn} do
clear_config([:auth, :oauth_consumer_strategies], [])
assert get(conn, "/api/v2/instance")
|> json_response_and_validate_schema(200)
end
test "get instance rules", %{conn: conn} do
Rule.create(%{text: "Example rule", hint: "Rule description", priority: 1})
Rule.create(%{text: "Third rule", priority: 2})
Rule.create(%{text: "Second rule", priority: 1})
conn = get(conn, "/api/v1/instance")
assert result = json_response_and_validate_schema(conn, 200)
assert [
%{
"text" => "Example rule",
"hint" => "Rule description"
},
%{
"text" => "Second rule",
"hint" => ""
},
%{
"text" => "Third rule",
"hint" => ""
}
] = result["rules"]
end
end

View file

@ -7,6 +7,7 @@ defmodule Pleroma.Web.MastodonAPI.ReportControllerTest do
alias Pleroma.Activity
alias Pleroma.Repo
alias Pleroma.Rule
alias Pleroma.Web.CommonAPI
import Pleroma.Factory
@ -81,6 +82,44 @@ defmodule Pleroma.Web.MastodonAPI.ReportControllerTest do
|> json_response_and_validate_schema(200)
end
test "submit a report with rule_ids", %{
conn: conn,
target_user: target_user
} do
%{id: rule_id} = Rule.create(%{text: "There are no rules"})
rule_id = to_string(rule_id)
assert %{"action_taken" => false, "id" => id} =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/reports", %{
"account_id" => target_user.id,
"forward" => "false",
"rule_ids" => [rule_id]
})
|> json_response_and_validate_schema(200)
assert %Activity{data: %{"rules" => [^rule_id]}} = Activity.get_report(id)
end
test "rules field is empty if provided wrong rule id", %{
conn: conn,
target_user: target_user
} do
assert %{"id" => id} =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/reports", %{
"account_id" => target_user.id,
"forward" => "false",
"rule_ids" => ["-1"]
})
|> json_response_and_validate_schema(200)
assert %Activity{data: %{"rules" => []}} = Activity.get_report(id)
end
test "account_id is required", %{
conn: conn,
activity: activity

View file

@ -322,26 +322,20 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do
end
test "search fetches remote statuses and prefers them over other results", %{conn: conn} do
old_version = :persistent_term.get({Pleroma.Repo, :postgres_version})
:persistent_term.put({Pleroma.Repo, :postgres_version}, 10.0)
on_exit(fn -> :persistent_term.put({Pleroma.Repo, :postgres_version}, old_version) end)
{:ok, %{id: activity_id}} =
CommonAPI.post(insert(:user), %{
status: "check out http://mastodon.example.org/@admin/99541947525187367"
})
capture_log(fn ->
{:ok, %{id: activity_id}} =
CommonAPI.post(insert(:user), %{
status: "check out http://mastodon.example.org/@admin/99541947525187367"
})
%{"url" => result_url, "id" => result_id} =
conn
|> get("/api/v1/search?q=http://mastodon.example.org/@admin/99541947525187367")
|> json_response_and_validate_schema(200)
|> Map.get("statuses")
|> List.first()
results =
conn
|> get("/api/v1/search?q=http://mastodon.example.org/@admin/99541947525187367")
|> json_response_and_validate_schema(200)
assert [
%{"url" => "http://mastodon.example.org/@admin/99541947525187367"},
%{"id" => ^activity_id}
] = results["statuses"]
end)
refute match?(^result_id, activity_id)
assert match?(^result_url, "http://mastodon.example.org/@admin/99541947525187367")
end
test "search doesn't show statuses that it shouldn't", %{conn: conn} do

View file

@ -329,68 +329,6 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
assert real_status == fake_status
end
test "fake statuses' preview card is not cached", %{conn: conn} do
Pleroma.StaticStubbedConfigMock
|> stub(:get, fn
[:rich_media, :enabled] -> true
path -> Pleroma.Test.StaticConfig.get(path)
end)
Tesla.Mock.mock(fn
%{
method: :get,
url: "https://example.com/twitter-card"
} ->
%Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/twitter_card.html")}
env ->
apply(HttpRequestMock, :request, [env])
end)
conn1 =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses", %{
"status" => "https://example.com/ogp",
"preview" => true
})
conn2 =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses", %{
"status" => "https://example.com/twitter-card",
"preview" => true
})
assert %{"card" => %{"title" => "The Rock"}} = json_response_and_validate_schema(conn1, 200)
assert %{"card" => %{"title" => "Small Island Developing States Photo Submission"}} =
json_response_and_validate_schema(conn2, 200)
end
test "posting a status with OGP link preview", %{conn: conn} do
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
Pleroma.StaticStubbedConfigMock
|> stub(:get, fn
[:rich_media, :enabled] -> true
path -> Pleroma.Test.StaticConfig.get(path)
end)
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses", %{
"status" => "https://example.com/ogp"
})
assert %{"id" => id, "card" => %{"title" => "The Rock"}} =
json_response_and_validate_schema(conn, 200)
assert Activity.get_by_id(id)
end
test "posting a direct status", %{conn: conn} do
user2 = insert(:user)
content = "direct cofe @#{user2.nickname}"
@ -1705,91 +1643,6 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
end
end
describe "cards" do
setup do
Pleroma.StaticStubbedConfigMock
|> stub(:get, fn
[:rich_media, :enabled] -> true
path -> Pleroma.Test.StaticConfig.get(path)
end)
oauth_access(["read:statuses"])
end
test "returns rich-media card", %{conn: conn, user: user} do
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
{:ok, activity} = CommonAPI.post(user, %{status: "https://example.com/ogp"})
card_data = %{
"image" => "http://ia.media-imdb.com/images/rock.jpg",
"provider_name" => "example.com",
"provider_url" => "https://example.com",
"title" => "The Rock",
"type" => "link",
"url" => "https://example.com/ogp",
"description" =>
"Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
"pleroma" => %{
"opengraph" => %{
"image" => "http://ia.media-imdb.com/images/rock.jpg",
"title" => "The Rock",
"type" => "video.movie",
"url" => "https://example.com/ogp",
"description" =>
"Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer."
}
}
}
response =
conn
|> get("/api/v1/statuses/#{activity.id}/card")
|> json_response_and_validate_schema(200)
assert response == card_data
# works with private posts
{:ok, activity} =
CommonAPI.post(user, %{status: "https://example.com/ogp", visibility: "direct"})
response_two =
conn
|> get("/api/v1/statuses/#{activity.id}/card")
|> json_response_and_validate_schema(200)
assert response_two == card_data
end
test "replaces missing description with an empty string", %{conn: conn, user: user} do
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
{:ok, activity} = CommonAPI.post(user, %{status: "https://example.com/ogp-missing-data"})
response =
conn
|> get("/api/v1/statuses/#{activity.id}/card")
|> json_response_and_validate_schema(:ok)
assert response == %{
"type" => "link",
"title" => "Pleroma",
"description" => "",
"image" => nil,
"provider_name" => "example.com",
"provider_url" => "https://example.com",
"url" => "https://example.com/ogp-missing-data",
"pleroma" => %{
"opengraph" => %{
"title" => "Pleroma",
"type" => "website",
"url" => "https://example.com/ogp-missing-data"
}
}
}
end
end
test "bookmarks" do
bookmarks_uri = "/api/v1/bookmarks"
@ -1834,6 +1687,60 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
json_response_and_validate_schema(bookmarks, 200)
end
test "bookmark folders" do
%{conn: conn, user: user} = oauth_access(["write:bookmarks", "read:bookmarks"])
{:ok, folder} = Pleroma.BookmarkFolder.create(user.id, "folder")
author = insert(:user)
folder_bookmarks_uri = "/api/v1/bookmarks?folder_id=#{folder.id}"
{:ok, activity1} = CommonAPI.post(author, %{status: "heweoo?"})
{:ok, activity2} = CommonAPI.post(author, %{status: "heweoo!"})
# Add bookmark with a folder
response =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses/#{activity1.id}/bookmark", %{folder_id: folder.id})
assert json_response_and_validate_schema(response, 200)["bookmarked"] == true
assert json_response_and_validate_schema(response, 200)["pleroma"]["bookmark_folder"] ==
folder.id
response =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses/#{activity2.id}/bookmark")
assert json_response_and_validate_schema(response, 200)["bookmarked"] == true
assert json_response_and_validate_schema(response, 200)["pleroma"]["bookmark_folder"] == nil
bookmarks =
get(conn, folder_bookmarks_uri)
|> json_response_and_validate_schema(200)
assert length(bookmarks) == 1
# Update folder for existing bookmark
response =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses/#{activity2.id}/bookmark", %{folder_id: folder.id})
assert json_response_and_validate_schema(response, 200)["bookmarked"] == true
assert json_response_and_validate_schema(response, 200)["pleroma"]["bookmark_folder"] ==
folder.id
bookmarks =
get(conn, folder_bookmarks_uri)
|> json_response_and_validate_schema(200)
assert length(bookmarks) == 2
end
describe "conversation muting" do
setup do: oauth_access(["write:mutes"])

View file

@ -511,10 +511,15 @@ defmodule Pleroma.Web.MastodonAPI.UpdateCredentialsTest do
|> json_response_and_validate_schema(200)
assert account_data["fields"] == [
%{"name" => "<a href=\"http://google.com\">foo</a>", "value" => "bar"},
%{
"name" => "<a href=\"http://google.com\">foo</a>",
"value" => "bar",
"verified_at" => nil
},
%{
"name" => "link.io",
"value" => ~S(<a href="http://cofe.io" rel="ugc">cofe.io</a>)
"value" => ~S(<a href="http://cofe.io" rel="ugc">cofe.io</a>),
"verified_at" => nil
}
]
@ -573,8 +578,8 @@ defmodule Pleroma.Web.MastodonAPI.UpdateCredentialsTest do
|> json_response_and_validate_schema(200)
assert account_data["fields"] == [
%{"name" => ":firefox:", "value" => "is best 2hu"},
%{"name" => "they wins", "value" => ":blank:"}
%{"name" => ":firefox:", "value" => "is best 2hu", "verified_at" => nil},
%{"name" => "they wins", "value" => ":blank:", "verified_at" => nil}
]
assert account_data["source"]["fields"] == [
@ -602,10 +607,11 @@ defmodule Pleroma.Web.MastodonAPI.UpdateCredentialsTest do
|> json_response_and_validate_schema(200)
assert account["fields"] == [
%{"name" => "foo", "value" => "bar"},
%{"name" => "foo", "value" => "bar", "verified_at" => nil},
%{
"name" => "link",
"value" => ~S(<a href="http://cofe.io" rel="ugc">http://cofe.io</a>)
"value" => ~S(<a href="http://cofe.io" rel="ugc">http://cofe.io</a>),
"verified_at" => nil
}
]
@ -627,7 +633,7 @@ defmodule Pleroma.Web.MastodonAPI.UpdateCredentialsTest do
|> json_response_and_validate_schema(200)
assert account["fields"] == [
%{"name" => "foo", "value" => ""}
%{"name" => "foo", "value" => "", "verified_at" => nil}
]
end

View file

@ -43,7 +43,8 @@ defmodule Pleroma.Web.MastodonAPI.PollViewTest do
%{title: "why are you even asking?", votes_count: 0}
],
votes_count: 0,
voters_count: 0
voters_count: 0,
pleroma: %{non_anonymous: false}
}
result = PollView.render("show.json", %{object: object})
@ -165,4 +166,11 @@ defmodule Pleroma.Web.MastodonAPI.PollViewTest do
]
} = PollView.render("show.json", %{object: object})
end
test "that poll is non anonymous" do
object = Object.normalize("https://friends.grishka.me/posts/54642", fetch: true)
result = PollView.render("show.json", %{object: object})
assert result[:pleroma][:non_anonymous] == true
end
end

View file

@ -17,6 +17,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.MastodonAPI.AccountView
alias Pleroma.Web.MastodonAPI.StatusView
alias Pleroma.Web.RichMedia.Card
require Bitwise
@ -341,7 +342,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
emoji_reactions: [],
parent_visible: false,
pinned_at: nil,
quotes_count: 0
quotes_count: 0,
bookmark_folder: nil
}
}
@ -731,56 +733,72 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
describe "rich media cards" do
test "a rich media card without a site name renders correctly" do
page_url = "http://example.com"
page_url = "https://example.com"
card = %{
url: page_url,
image: page_url <> "/example.jpg",
title: "Example website"
}
{:ok, card} =
Card.create(page_url, %{image: page_url <> "/example.jpg", title: "Example website"})
%{provider_name: "example.com"} =
StatusView.render("card.json", %{page_url: page_url, rich_media: card})
assert match?(%{provider_name: "example.com"}, StatusView.render("card.json", card))
end
test "a rich media card without a site name or image renders correctly" do
page_url = "http://example.com"
page_url = "https://example.com"
card = %{
url: page_url,
title: "Example website"
fields = %{
"url" => page_url,
"title" => "Example website"
}
%{provider_name: "example.com"} =
StatusView.render("card.json", %{page_url: page_url, rich_media: card})
{:ok, card} = Card.create(page_url, fields)
assert match?(%{provider_name: "example.com"}, StatusView.render("card.json", card))
end
test "a rich media card without an image renders correctly" do
page_url = "http://example.com"
page_url = "https://example.com"
card = %{
url: page_url,
site_name: "Example site name",
title: "Example website"
fields = %{
"url" => page_url,
"site_name" => "Example site name",
"title" => "Example website"
}
%{provider_name: "example.com"} =
StatusView.render("card.json", %{page_url: page_url, rich_media: card})
{:ok, card} = Card.create(page_url, fields)
assert match?(%{provider_name: "example.com"}, StatusView.render("card.json", card))
end
test "a rich media card without descriptions returns the fields with empty strings" do
page_url = "https://example.com"
fields = %{
"url" => page_url,
"site_name" => "Example site name",
"title" => "Example website"
}
{:ok, card} = Card.create(page_url, fields)
assert match?(
%{description: "", image_description: ""},
StatusView.render("card.json", card)
)
end
test "a rich media card with all relevant data renders correctly" do
page_url = "http://example.com"
page_url = "https://example.com"
card = %{
url: page_url,
site_name: "Example site name",
title: "Example website",
image: page_url <> "/example.jpg",
description: "Example description"
fields = %{
"url" => page_url,
"site_name" => "Example site name",
"title" => "Example website",
"image" => page_url <> "/example.jpg",
"description" => "Example description"
}
%{provider_name: "example.com"} =
StatusView.render("card.json", %{page_url: page_url, rich_media: card})
{:ok, card} = Card.create(page_url, fields)
assert match?(%{provider_name: "example.com"}, StatusView.render("card.json", card))
end
test "a rich media card has all media proxied" do
@ -790,25 +808,25 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
ConfigMock
|> stub_with(Pleroma.Test.StaticConfig)
page_url = "http://example.com"
page_url = "https://example.com"
card = %{
url: page_url,
site_name: "Example site name",
title: "Example website",
image: page_url <> "/example.jpg",
audio: page_url <> "/example.ogg",
video: page_url <> "/example.mp4",
description: "Example description"
fields = %{
"url" => page_url,
"site_name" => "Example site name",
"title" => "Example website",
"image" => page_url <> "/example.jpg",
"audio" => page_url <> "/example.ogg",
"video" => page_url <> "/example.mp4",
"description" => "Example description"
}
strcard = for {k, v} <- card, into: %{}, do: {to_string(k), v}
{:ok, card} = Card.create(page_url, fields)
%{
provider_name: "example.com",
image: image,
pleroma: %{opengraph: og}
} = StatusView.render("card.json", %{page_url: page_url, rich_media: strcard})
} = StatusView.render("card.json", card)
assert String.match?(image, ~r/\/proxy\//)
assert String.match?(og["image"], ~r/\/proxy\//)

View file

@ -0,0 +1,161 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2024 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.PleromaAPI.BookmarkFolderControllerTest do
use Pleroma.Web.ConnCase
alias Pleroma.BookmarkFolder
# alias Pleroma.Object
# alias Pleroma.Tests.Helpers
# alias Pleroma.UnstubbedConfigMock, as: ConfigMock
# alias Pleroma.User
# alias Pleroma.Web.ActivityPub.ActivityPub
# alias Pleroma.Web.CommonAPI
# import Mox
import Pleroma.Factory
describe "GET /api/v1/pleroma/bookmark_folders" do
setup do: oauth_access(["read:bookmarks"])
test "it lists bookmark folders", %{conn: conn, user: user} do
{:ok, folder} = BookmarkFolder.create(user.id, "Bookmark folder")
folder_id = folder.id
result =
conn
|> get("/api/v1/pleroma/bookmark_folders")
|> json_response_and_validate_schema(200)
assert [
%{
"id" => ^folder_id,
"name" => "Bookmark folder",
"emoji" => nil,
"emoji_url" => nil
}
] = result
end
end
describe "POST /api/v1/pleroma/bookmark_folders" do
setup do: oauth_access(["write:bookmarks"])
test "it creates a bookmark folder", %{conn: conn} do
result =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/pleroma/bookmark_folders", %{
name: "Bookmark folder",
emoji: "📁"
})
|> json_response_and_validate_schema(200)
assert %{
"name" => "Bookmark folder",
"emoji" => "📁",
"emoji_url" => nil
} = result
end
test "it creates a bookmark folder with custom emoji", %{conn: conn} do
result =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/pleroma/bookmark_folders", %{
name: "Bookmark folder",
emoji: ":firefox:"
})
|> json_response_and_validate_schema(200)
assert %{
"name" => "Bookmark folder",
"emoji" => ":firefox:",
"emoji_url" => "http://localhost:4001/emoji/Firefox.gif"
} = result
end
test "it returns error for invalid emoji", %{conn: conn} do
result =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/pleroma/bookmark_folders", %{
name: "Bookmark folder",
emoji: "not an emoji"
})
|> json_response_and_validate_schema(422)
assert %{"error" => "Invalid emoji"} = result
end
end
describe "PATCH /api/v1/pleroma/bookmark_folders/:id" do
setup do: oauth_access(["write:bookmarks"])
test "it updates a bookmark folder", %{conn: conn, user: user} do
{:ok, folder} = BookmarkFolder.create(user.id, "Bookmark folder")
result =
conn
|> put_req_header("content-type", "application/json")
|> patch("/api/v1/pleroma/bookmark_folders/#{folder.id}", %{
name: "bookmark folder"
})
|> json_response_and_validate_schema(200)
assert %{
"name" => "bookmark folder"
} = result
end
test "it returns error when updating others' folders", %{conn: conn} do
other_user = insert(:user)
{:ok, folder} = BookmarkFolder.create(other_user.id, "Bookmark folder")
result =
conn
|> put_req_header("content-type", "application/json")
|> patch("/api/v1/pleroma/bookmark_folders/#{folder.id}", %{
name: "bookmark folder"
})
|> json_response_and_validate_schema(403)
assert %{
"error" => "Access denied"
} = result
end
end
describe "DELETE /api/v1/pleroma/bookmark_folders/:id" do
setup do: oauth_access(["write:bookmarks"])
test "it deleting a bookmark folder", %{conn: conn, user: user} do
{:ok, folder} = BookmarkFolder.create(user.id, "Bookmark folder")
assert conn
|> delete("/api/v1/pleroma/bookmark_folders/#{folder.id}")
|> json_response_and_validate_schema(200)
folders = BookmarkFolder.for_user(user.id)
assert length(folders) == 0
end
test "it returns error when deleting others' folders", %{conn: conn} do
other_user = insert(:user)
{:ok, folder} = BookmarkFolder.create(other_user.id, "Bookmark folder")
result =
conn
|> patch("/api/v1/pleroma/bookmark_folders/#{folder.id}")
|> json_response_and_validate_schema(403)
assert %{
"error" => "Access denied"
} = result
end
end
end

View file

@ -9,7 +9,6 @@ defmodule Pleroma.Web.PleromaAPI.ChatMessageReferenceViewTest do
alias Pleroma.Chat
alias Pleroma.Chat.MessageReference
alias Pleroma.Object
alias Pleroma.StaticStubbedConfigMock
alias Pleroma.UnstubbedConfigMock, as: ConfigMock
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.CommonAPI
@ -18,6 +17,8 @@ defmodule Pleroma.Web.PleromaAPI.ChatMessageReferenceViewTest do
import Mox
import Pleroma.Factory
setup do: clear_config([:rich_media, :enabled], true)
test "it displays a chat message" do
user = insert(:user)
recipient = insert(:user)
@ -49,6 +50,7 @@ defmodule Pleroma.Web.PleromaAPI.ChatMessageReferenceViewTest do
:chat_message_id_idempotency_key_cache, ^id -> {:ok, "123"}
cache, key -> NullCache.get(cache, key)
end)
|> stub(:fetch, fn :rich_media_cache, _, _ -> {:ok, {:ok, %{}}} end)
chat_message = MessageReferenceView.render("show.json", chat_message_reference: cm_ref)
@ -61,16 +63,7 @@ defmodule Pleroma.Web.PleromaAPI.ChatMessageReferenceViewTest do
assert match?([%{shortcode: "firefox"}], chat_message[:emojis])
assert chat_message[:idempotency_key] == "123"
StaticStubbedConfigMock
|> stub(:get, fn
[:rich_media, :enabled] -> true
path -> Pleroma.Test.StaticConfig.get(path)
end)
Tesla.Mock.mock_global(fn
%{url: "https://example.com/ogp"} ->
%Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/ogp.html")}
end)
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
{:ok, activity} =
CommonAPI.post_chat_message(recipient, user, "gkgkgk https://example.com/ogp",

View file

@ -0,0 +1,71 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2024 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.RichMedia.CardTest do
use Pleroma.DataCase, async: true
alias Pleroma.UnstubbedConfigMock, as: ConfigMock
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.RichMedia.Card
import Mox
import Pleroma.Factory
import Tesla.Mock
setup do
mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
ConfigMock
|> stub_with(Pleroma.Test.StaticConfig)
:ok
end
setup do: clear_config([:rich_media, :enabled], true)
test "crawls URL in activity" do
user = insert(:user)
url = "https://example.com/ogp"
url_hash = Card.url_to_hash(url)
{:ok, activity} =
CommonAPI.post(user, %{
status: "[test](#{url})",
content_type: "text/markdown"
})
assert %Card{url_hash: ^url_hash, fields: _} = Card.get_by_activity(activity)
end
test "recrawls URLs on status edits/updates" do
original_url = "https://google.com/"
original_url_hash = Card.url_to_hash(original_url)
updated_url = "https://yahoo.com/"
updated_url_hash = Card.url_to_hash(updated_url)
user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{status: "I like this site #{original_url}"})
# Force a backfill
Card.get_by_activity(activity)
assert match?(
%Card{url_hash: ^original_url_hash, fields: _},
Card.get_by_activity(activity)
)
{:ok, _} = CommonAPI.update(user, activity, %{status: "I like this site #{updated_url}"})
activity = Pleroma.Activity.get_by_id(activity.id)
# Force a backfill
Card.get_by_activity(activity)
assert match?(
%Card{url_hash: ^updated_url_hash, fields: _},
Card.get_by_activity(activity)
)
end
end

View file

@ -1,111 +0,0 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.RichMedia.HelpersTest do
use Pleroma.DataCase, async: true
alias Pleroma.StaticStubbedConfigMock, as: ConfigMock
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.RichMedia.Helpers
import Mox
import Pleroma.Factory
import Tesla.Mock
setup do
mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
ConfigMock
|> stub(:get, fn
[:rich_media, :enabled] -> false
path -> Pleroma.Test.StaticConfig.get(path)
end)
|> stub(:get, fn
path, default -> Pleroma.Test.StaticConfig.get(path, default)
end)
:ok
end
test "refuses to crawl incomplete URLs" do
user = insert(:user)
{:ok, activity} =
CommonAPI.post(user, %{
status: "[test](example.com/ogp)",
content_type: "text/markdown"
})
ConfigMock
|> stub(:get, fn
[:rich_media, :enabled] -> true
path -> Pleroma.Test.StaticConfig.get(path)
end)
assert %{} == Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
end
test "refuses to crawl malformed URLs" do
user = insert(:user)
{:ok, activity} =
CommonAPI.post(user, %{
status: "[test](example.com[]/ogp)",
content_type: "text/markdown"
})
ConfigMock
|> stub(:get, fn
[:rich_media, :enabled] -> true
path -> Pleroma.Test.StaticConfig.get(path)
end)
assert %{} == Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
end
test "crawls valid, complete URLs" do
user = insert(:user)
{:ok, activity} =
CommonAPI.post(user, %{
status: "[test](https://example.com/ogp)",
content_type: "text/markdown"
})
ConfigMock
|> stub(:get, fn
[:rich_media, :enabled] -> true
path -> Pleroma.Test.StaticConfig.get(path)
end)
assert %{page_url: "https://example.com/ogp", rich_media: _} =
Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
end
# This does not seem to work. The urls are being fetched.
@tag skip: true
test "refuses to crawl URLs of private network from posts" do
user = insert(:user)
{:ok, activity} =
CommonAPI.post(user, %{status: "http://127.0.0.1:4000/notice/9kCP7VNyPJXFOXDrgO"})
{:ok, activity2} = CommonAPI.post(user, %{status: "https://10.111.10.1/notice/9kCP7V"})
{:ok, activity3} = CommonAPI.post(user, %{status: "https://172.16.32.40/notice/9kCP7V"})
{:ok, activity4} = CommonAPI.post(user, %{status: "https://192.168.10.40/notice/9kCP7V"})
{:ok, activity5} = CommonAPI.post(user, %{status: "https://pleroma.local/notice/9kCP7V"})
ConfigMock
|> stub(:get, fn
[:rich_media, :enabled] -> true
path -> Pleroma.Test.StaticConfig.get(path)
end)
assert %{} = Helpers.fetch_data_for_activity(activity)
assert %{} = Helpers.fetch_data_for_activity(activity2)
assert %{} = Helpers.fetch_data_for_activity(activity3)
assert %{} = Helpers.fetch_data_for_activity(activity4)
assert %{} = Helpers.fetch_data_for_activity(activity5)
end
end

View file

@ -3,8 +3,22 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.RichMedia.Parser.TTL.AwsSignedUrlTest do
# Relies on Cachex, needs to be synchronous
use Pleroma.DataCase
use Pleroma.DataCase, async: false
use Oban.Testing, repo: Pleroma.Repo
import Mox
alias Pleroma.UnstubbedConfigMock, as: ConfigMock
alias Pleroma.Web.RichMedia.Card
setup do
ConfigMock
|> stub_with(Pleroma.Test.StaticConfig)
clear_config([:rich_media, :enabled], true)
:ok
end
test "s3 signed url is parsed correct for expiration time" do
url = "https://pleroma.social/amz"
@ -43,26 +57,29 @@ defmodule Pleroma.Web.RichMedia.Parser.TTL.AwsSignedUrlTest do
<meta name="twitter:site" content="Pleroma" />
<meta name="twitter:title" content="Pleroma" />
<meta name="twitter:description" content="Pleroma" />
<meta name="twitter:image" content="#{Map.get(metadata, :image)}" />
<meta name="twitter:image" content="#{Map.get(metadata, "image")}" />
"""
Tesla.Mock.mock(fn
%{
method: :get,
url: "https://pleroma.social/amz"
url: ^url
} ->
%Tesla.Env{status: 200, body: body}
%{method: :head} ->
%Tesla.Env{status: 200}
end)
Cachex.put(:rich_media_cache, url, metadata)
Card.get_or_backfill_by_url(url)
Pleroma.Web.RichMedia.Parser.set_ttl_based_on_image(metadata, url)
assert_enqueued(worker: Pleroma.Workers.RichMediaExpirationWorker, args: %{"url" => url})
{:ok, cache_ttl} = Cachex.ttl(:rich_media_cache, url)
[%Oban.Job{scheduled_at: scheduled_at}] = all_enqueued()
# as there is delay in setting and pulling the data from cache we ignore 1 second
# make it 2 seconds for flakyness
assert_in_delta(valid_till * 1000, cache_ttl, 2000)
timestamp_dt = Timex.parse!(timestamp, "{ISO:Basic:Z}")
assert DateTime.diff(scheduled_at, timestamp_dt) == valid_till
end
defp construct_s3_url(timestamp, valid_till) do
@ -71,11 +88,11 @@ defmodule Pleroma.Web.RichMedia.Parser.TTL.AwsSignedUrlTest do
defp construct_metadata(timestamp, valid_till, url) do
%{
image: construct_s3_url(timestamp, valid_till),
site: "Pleroma",
title: "Pleroma",
description: "Pleroma",
url: url
"image" => construct_s3_url(timestamp, valid_till),
"site" => "Pleroma",
"title" => "Pleroma",
"description" => "Pleroma",
"url" => url
}
end
end

View file

@ -0,0 +1,41 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2024 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.RichMedia.Parser.TTL.OpengraphTest do
use Pleroma.DataCase
use Oban.Testing, repo: Pleroma.Repo
import Mox
alias Pleroma.UnstubbedConfigMock, as: ConfigMock
alias Pleroma.Web.RichMedia.Card
setup do
ConfigMock
|> stub_with(Pleroma.Test.StaticConfig)
clear_config([:rich_media, :enabled], true)
:ok
end
test "OpenGraph TTL value is honored" do
url = "https://reddit.com/r/somepost"
Tesla.Mock.mock(fn
%{
method: :get,
url: ^url
} ->
%Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/reddit.html")}
%{method: :head} ->
%Tesla.Env{status: 200}
end)
Card.get_or_backfill_by_url(url)
assert_enqueued(worker: Pleroma.Workers.RichMediaExpirationWorker, args: %{"url" => url})
end
end

View file

@ -3,95 +3,26 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.RichMedia.ParserTest do
use ExUnit.Case, async: true
use Pleroma.DataCase
alias Pleroma.Web.RichMedia.Parser
import Tesla.Mock
setup do
Tesla.Mock.mock(fn
%{
method: :get,
url: "http://example.com/ogp"
} ->
%Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/ogp.html")}
%{
method: :get,
url: "http://example.com/non-ogp"
} ->
%Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/non_ogp_embed.html")}
%{
method: :get,
url: "http://example.com/ogp-missing-title"
} ->
%Tesla.Env{
status: 200,
body: File.read!("test/fixtures/rich_media/ogp-missing-title.html")
}
%{
method: :get,
url: "http://example.com/twitter-card"
} ->
%Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/twitter_card.html")}
%{
method: :get,
url: "http://example.com/oembed"
} ->
%Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/oembed.html")}
%{
method: :get,
url: "http://example.com/oembed.json"
} ->
%Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/oembed.json")}
%{method: :get, url: "http://example.com/empty"} ->
%Tesla.Env{status: 200, body: "hello"}
%{method: :get, url: "http://example.com/malformed"} ->
%Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/malformed-data.html")}
%{method: :get, url: "http://example.com/error"} ->
{:error, :overload}
%{
method: :head,
url: "http://example.com/huge-page"
} ->
%Tesla.Env{
status: 200,
headers: [{"content-length", "2000001"}, {"content-type", "text/html"}]
}
%{
method: :head,
url: "http://example.com/pdf-file"
} ->
%Tesla.Env{
status: 200,
headers: [{"content-length", "1000000"}, {"content-type", "application/pdf"}]
}
%{method: :head} ->
%Tesla.Env{status: 404, body: "", headers: []}
end)
:ok
mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
end
test "returns error when no metadata present" do
assert {:error, _} = Parser.parse("http://example.com/empty")
assert {:error, _} = Parser.parse("https://example.com/empty")
end
test "doesn't just add a title" do
assert {:error, {:invalid_metadata, _}} = Parser.parse("http://example.com/non-ogp")
assert {:error, {:invalid_metadata, _}} = Parser.parse("https://example.com/non-ogp")
end
test "parses ogp" do
assert Parser.parse("http://example.com/ogp") ==
assert Parser.parse("https://example.com/ogp") ==
{:ok,
%{
"image" => "http://ia.media-imdb.com/images/rock.jpg",
@ -99,12 +30,12 @@ defmodule Pleroma.Web.RichMedia.ParserTest do
"description" =>
"Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
"type" => "video.movie",
"url" => "http://example.com/ogp"
"url" => "https://example.com/ogp"
}}
end
test "falls back to <title> when ogp:title is missing" do
assert Parser.parse("http://example.com/ogp-missing-title") ==
assert Parser.parse("https://example.com/ogp-missing-title") ==
{:ok,
%{
"image" => "http://ia.media-imdb.com/images/rock.jpg",
@ -112,12 +43,12 @@ defmodule Pleroma.Web.RichMedia.ParserTest do
"description" =>
"Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
"type" => "video.movie",
"url" => "http://example.com/ogp-missing-title"
"url" => "https://example.com/ogp-missing-title"
}}
end
test "parses twitter card" do
assert Parser.parse("http://example.com/twitter-card") ==
assert Parser.parse("https://example.com/twitter-card") ==
{:ok,
%{
"card" => "summary",
@ -125,12 +56,12 @@ defmodule Pleroma.Web.RichMedia.ParserTest do
"image" => "https://farm6.staticflickr.com/5510/14338202952_93595258ff_z.jpg",
"title" => "Small Island Developing States Photo Submission",
"description" => "View the album on Flickr.",
"url" => "http://example.com/twitter-card"
"url" => "https://example.com/twitter-card"
}}
end
test "parses OEmbed and filters HTML tags" do
assert Parser.parse("http://example.com/oembed") ==
assert Parser.parse("https://example.com/oembed") ==
{:ok,
%{
"author_name" => "\u202E\u202D\u202Cbees\u202C",
@ -150,7 +81,7 @@ defmodule Pleroma.Web.RichMedia.ParserTest do
"thumbnail_width" => 150,
"title" => "Bacon Lollys",
"type" => "photo",
"url" => "http://example.com/oembed",
"url" => "https://example.com/oembed",
"version" => "1.0",
"web_page" => "https://www.flickr.com/photos/bees/2362225867/",
"web_page_short_url" => "https://flic.kr/p/4AK2sc",
@ -159,18 +90,41 @@ defmodule Pleroma.Web.RichMedia.ParserTest do
end
test "rejects invalid OGP data" do
assert {:error, _} = Parser.parse("http://example.com/malformed")
assert {:error, _} = Parser.parse("https://example.com/malformed")
end
test "returns error if getting page was not successful" do
assert {:error, :overload} = Parser.parse("http://example.com/error")
assert {:error, :overload} = Parser.parse("https://example.com/error")
end
test "does a HEAD request to check if the body is too large" do
assert {:error, :body_too_large} = Parser.parse("http://example.com/huge-page")
assert {:error, :body_too_large} = Parser.parse("https://example.com/huge-page")
end
test "does a HEAD request to check if the body is html" do
assert {:error, {:content_type, _}} = Parser.parse("http://example.com/pdf-file")
assert {:error, {:content_type, _}} = Parser.parse("https://example.com/pdf-file")
end
test "refuses to crawl incomplete URLs" do
url = "example.com/ogp"
assert :error == Parser.parse(url)
end
test "refuses to crawl malformed URLs" do
url = "example.com[]/ogp"
assert :error == Parser.parse(url)
end
test "refuses to crawl URLs of private network from posts" do
[
"http://127.0.0.1:4000/notice/9kCP7VNyPJXFOXDrgO",
"https://10.111.10.1/notice/9kCP7V",
"https://172.16.32.40/notice/9kCP7V",
"https://192.168.10.40/notice/9kCP7V",
"https://pleroma.local/notice/9kCP7V"
]
|> Enum.each(fn url ->
assert :error == Parser.parse(url)
end)
end
end

View file

@ -26,9 +26,15 @@ defmodule Pleroma.CachexProxy do
@impl true
defdelegate fetch!(cache, key, func), to: Cachex
@impl true
defdelegate fetch(cache, key, func), to: Cachex
@impl true
defdelegate expire_at(cache, str, num), to: Cachex
@impl true
defdelegate expire(cache, str, num), to: Cachex
@impl true
defdelegate exists?(cache, key), to: Cachex

View file

@ -1059,7 +1059,7 @@ defmodule HttpRequestMock do
}}
end
def get("http://example.com/malformed", _, _, _) do
def get("https://example.com/malformed", _, _, _) do
{:ok,
%Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/malformed-data.html")}}
end
@ -1464,6 +1464,63 @@ defmodule HttpRequestMock do
}}
end
def get("https://google.com/", _, _, _) do
{:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/google.html")}}
end
def get("https://yahoo.com/", _, _, _) do
{:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/yahoo.html")}}
end
def get("https://example.com/error", _, _, _), do: {:error, :overload}
def get("https://example.com/ogp-missing-title", _, _, _) do
{:ok,
%Tesla.Env{
status: 200,
body: File.read!("test/fixtures/rich_media/ogp-missing-title.html")
}}
end
def get("https://example.com/oembed", _, _, _) do
{:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/oembed.html")}}
end
def get("https://example.com/oembed.json", _, _, _) do
{:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/oembed.json")}}
end
def get("https://example.com/twitter-card", _, _, _) do
{:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/twitter_card.html")}}
end
def get("https://example.com/non-ogp", _, _, _) do
{:ok,
%Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/non_ogp_embed.html")}}
end
def get("https://example.com/empty", _, _, _) do
{:ok, %Tesla.Env{status: 200, body: "hello"}}
end
def get("https://friends.grishka.me/posts/54642", _, _, _) do
{:ok,
%Tesla.Env{
status: 200,
body: File.read!("test/fixtures/tesla_mock/smithereen_non_anonymous_poll.json"),
headers: activitypub_object_headers()
}}
end
def get("https://friends.grishka.me/users/1", _, _, _) do
{:ok,
%Tesla.Env{
status: 200,
body: File.read!("test/fixtures/tesla_mock/smithereen_user.json"),
headers: activitypub_object_headers()
}}
end
def get(url, query, body, headers) do
{:error,
"Mock response not implemented for GET #{inspect(url)}, #{query}, #{inspect(body)}, #{inspect(headers)}"}
@ -1537,14 +1594,41 @@ defmodule HttpRequestMock do
# Most of the rich media mocks are missing HEAD requests, so we just return 404.
@rich_media_mocks [
"https://example.com/empty",
"https://example.com/error",
"https://example.com/malformed",
"https://example.com/non-ogp",
"https://example.com/oembed",
"https://example.com/oembed.json",
"https://example.com/ogp",
"https://example.com/ogp-missing-data",
"https://example.com/twitter-card"
"https://example.com/ogp-missing-title",
"https://example.com/twitter-card",
"https://google.com/",
"https://pleroma.local/notice/9kCP7V",
"https://yahoo.com/"
]
def head(url, _query, _body, _headers) when url in @rich_media_mocks do
{:ok, %Tesla.Env{status: 404, body: ""}}
end
def head("https://example.com/pdf-file", _, _, _) do
{:ok,
%Tesla.Env{
status: 200,
headers: [{"content-length", "1000000"}, {"content-type", "application/pdf"}]
}}
end
def head("https://example.com/huge-page", _, _, _) do
{:ok,
%Tesla.Env{
status: 200,
headers: [{"content-length", "2000001"}, {"content-type", "text/html"}]
}}
end
def head(url, query, body, headers) do
{:error,
"Mock response not implemented for HEAD #{inspect(url)}, #{query}, #{inspect(body)}, #{inspect(headers)}"}

View file

@ -28,6 +28,9 @@ defmodule Pleroma.NullCache do
end
end
@impl true
def fetch(_, key, func), do: func.(key)
@impl true
def get_and_update(_, _, func) do
func.(nil)
@ -36,6 +39,9 @@ defmodule Pleroma.NullCache do
@impl true
def expire_at(_, _, _), do: {:ok, true}
@impl true
def expire(_, _, _), do: {:ok, true}
@impl true
def exists?(_, _), do: {:ok, false}

View file

@ -4,6 +4,8 @@
Code.put_compiler_option(:warnings_as_errors, true)
ExUnit.configure(max_cases: System.schedulers_online())
ExUnit.start(exclude: [:federated, :erratic])
if match?({:unix, :darwin}, :os.type()) do