Merge branch 'from/upstream-develop/tusooa/edits' into 'develop'
Editing Closes #1429, #2859, and #2288 See merge request pleroma/pleroma!3678
This commit is contained in:
commit
e40c221c31
67 changed files with 3309 additions and 119 deletions
|
|
@ -127,6 +127,28 @@ defmodule Pleroma.NotificationTest do
|
|||
subscriber_notifications = Notification.for_user(subscriber)
|
||||
assert Enum.empty?(subscriber_notifications)
|
||||
end
|
||||
|
||||
test "it sends edited notifications to those who repeated a status" do
|
||||
user = insert(:user)
|
||||
repeated_user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
|
||||
{:ok, activity_one} =
|
||||
CommonAPI.post(user, %{
|
||||
status: "hey @#{other_user.nickname}!"
|
||||
})
|
||||
|
||||
{:ok, _activity_two} = CommonAPI.repeat(activity_one.id, repeated_user)
|
||||
|
||||
{:ok, _edit_activity} =
|
||||
CommonAPI.update(user, activity_one, %{
|
||||
status: "hey @#{other_user.nickname}! mew mew"
|
||||
})
|
||||
|
||||
assert [%{type: "reblog"}] = Notification.for_user(user)
|
||||
assert [%{type: "update"}] = Notification.for_user(repeated_user)
|
||||
assert [%{type: "mention"}] = Notification.for_user(other_user)
|
||||
end
|
||||
end
|
||||
|
||||
test "create_poll_notifications/1" do
|
||||
|
|
@ -839,6 +861,30 @@ defmodule Pleroma.NotificationTest do
|
|||
assert [other_user] == enabled_receivers
|
||||
assert [] == disabled_receivers
|
||||
end
|
||||
|
||||
test "it sends edited notifications to those who repeated a status" do
|
||||
user = insert(:user)
|
||||
repeated_user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
|
||||
{:ok, activity_one} =
|
||||
CommonAPI.post(user, %{
|
||||
status: "hey @#{other_user.nickname}!"
|
||||
})
|
||||
|
||||
{:ok, _activity_two} = CommonAPI.repeat(activity_one.id, repeated_user)
|
||||
|
||||
{:ok, edit_activity} =
|
||||
CommonAPI.update(user, activity_one, %{
|
||||
status: "hey @#{other_user.nickname}! mew mew"
|
||||
})
|
||||
|
||||
{enabled_receivers, _disabled_receivers} =
|
||||
Notification.get_notified_from_activity(edit_activity)
|
||||
|
||||
assert repeated_user in enabled_receivers
|
||||
assert other_user not in enabled_receivers
|
||||
end
|
||||
end
|
||||
|
||||
describe "notification lifecycle" do
|
||||
|
|
|
|||
|
|
@ -269,4 +269,271 @@ defmodule Pleroma.Object.FetcherTest do
|
|||
refute called(Pleroma.Signature.sign(:_, :_))
|
||||
end
|
||||
end
|
||||
|
||||
describe "refetching" do
|
||||
setup do
|
||||
object1 = %{
|
||||
"id" => "https://mastodon.social/1",
|
||||
"actor" => "https://mastodon.social/users/emelie",
|
||||
"attributedTo" => "https://mastodon.social/users/emelie",
|
||||
"type" => "Note",
|
||||
"content" => "test 1",
|
||||
"bcc" => [],
|
||||
"bto" => [],
|
||||
"cc" => [],
|
||||
"to" => [],
|
||||
"summary" => ""
|
||||
}
|
||||
|
||||
object2 = %{
|
||||
"id" => "https://mastodon.social/2",
|
||||
"actor" => "https://mastodon.social/users/emelie",
|
||||
"attributedTo" => "https://mastodon.social/users/emelie",
|
||||
"type" => "Note",
|
||||
"content" => "test 2",
|
||||
"bcc" => [],
|
||||
"bto" => [],
|
||||
"cc" => [],
|
||||
"to" => [],
|
||||
"summary" => "",
|
||||
"formerRepresentations" => %{
|
||||
"type" => "OrderedCollection",
|
||||
"orderedItems" => [
|
||||
%{
|
||||
"type" => "Note",
|
||||
"content" => "orig 2",
|
||||
"actor" => "https://mastodon.social/users/emelie",
|
||||
"attributedTo" => "https://mastodon.social/users/emelie",
|
||||
"bcc" => [],
|
||||
"bto" => [],
|
||||
"cc" => [],
|
||||
"to" => [],
|
||||
"summary" => ""
|
||||
}
|
||||
],
|
||||
"totalItems" => 1
|
||||
}
|
||||
}
|
||||
|
||||
mock(fn
|
||||
%{
|
||||
method: :get,
|
||||
url: "https://mastodon.social/1"
|
||||
} ->
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
headers: [{"content-type", "application/activity+json"}],
|
||||
body: Jason.encode!(object1)
|
||||
}
|
||||
|
||||
%{
|
||||
method: :get,
|
||||
url: "https://mastodon.social/2"
|
||||
} ->
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
headers: [{"content-type", "application/activity+json"}],
|
||||
body: Jason.encode!(object2)
|
||||
}
|
||||
|
||||
%{
|
||||
method: :get,
|
||||
url: "https://mastodon.social/users/emelie/collections/featured"
|
||||
} ->
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
headers: [{"content-type", "application/activity+json"}],
|
||||
body:
|
||||
Jason.encode!(%{
|
||||
"id" => "https://mastodon.social/users/emelie/collections/featured",
|
||||
"type" => "OrderedCollection",
|
||||
"actor" => "https://mastodon.social/users/emelie",
|
||||
"attributedTo" => "https://mastodon.social/users/emelie",
|
||||
"orderedItems" => [],
|
||||
"totalItems" => 0
|
||||
})
|
||||
}
|
||||
|
||||
env ->
|
||||
apply(HttpRequestMock, :request, [env])
|
||||
end)
|
||||
|
||||
%{object1: object1, object2: object2}
|
||||
end
|
||||
|
||||
test "it keeps formerRepresentations if remote does not have this attr", %{object1: object1} do
|
||||
full_object1 =
|
||||
object1
|
||||
|> Map.merge(%{
|
||||
"formerRepresentations" => %{
|
||||
"type" => "OrderedCollection",
|
||||
"orderedItems" => [
|
||||
%{
|
||||
"type" => "Note",
|
||||
"content" => "orig 2",
|
||||
"actor" => "https://mastodon.social/users/emelie",
|
||||
"attributedTo" => "https://mastodon.social/users/emelie",
|
||||
"bcc" => [],
|
||||
"bto" => [],
|
||||
"cc" => [],
|
||||
"to" => [],
|
||||
"summary" => ""
|
||||
}
|
||||
],
|
||||
"totalItems" => 1
|
||||
}
|
||||
})
|
||||
|
||||
{:ok, o} = Object.create(full_object1)
|
||||
|
||||
assert {:ok, refetched} = Fetcher.refetch_object(o)
|
||||
|
||||
assert %{"formerRepresentations" => %{"orderedItems" => [%{"content" => "orig 2"}]}} =
|
||||
refetched.data
|
||||
end
|
||||
|
||||
test "it uses formerRepresentations from remote if possible", %{object2: object2} do
|
||||
{:ok, o} = Object.create(object2)
|
||||
|
||||
assert {:ok, refetched} = Fetcher.refetch_object(o)
|
||||
|
||||
assert %{"formerRepresentations" => %{"orderedItems" => [%{"content" => "orig 2"}]}} =
|
||||
refetched.data
|
||||
end
|
||||
|
||||
test "it replaces formerRepresentations with the one from remote", %{object2: object2} do
|
||||
full_object2 =
|
||||
object2
|
||||
|> Map.merge(%{
|
||||
"content" => "mew mew #def",
|
||||
"formerRepresentations" => %{
|
||||
"type" => "OrderedCollection",
|
||||
"orderedItems" => [
|
||||
%{"type" => "Note", "content" => "mew mew 2"}
|
||||
],
|
||||
"totalItems" => 1
|
||||
}
|
||||
})
|
||||
|
||||
{:ok, o} = Object.create(full_object2)
|
||||
|
||||
assert {:ok, refetched} = Fetcher.refetch_object(o)
|
||||
|
||||
assert %{
|
||||
"content" => "test 2",
|
||||
"formerRepresentations" => %{"orderedItems" => [%{"content" => "orig 2"}]}
|
||||
} = refetched.data
|
||||
end
|
||||
|
||||
test "it adds to formerRepresentations if the remote does not have one and the object has changed",
|
||||
%{object1: object1} do
|
||||
full_object1 =
|
||||
object1
|
||||
|> Map.merge(%{
|
||||
"content" => "mew mew #def",
|
||||
"formerRepresentations" => %{
|
||||
"type" => "OrderedCollection",
|
||||
"orderedItems" => [
|
||||
%{"type" => "Note", "content" => "mew mew 1"}
|
||||
],
|
||||
"totalItems" => 1
|
||||
}
|
||||
})
|
||||
|
||||
{:ok, o} = Object.create(full_object1)
|
||||
|
||||
assert {:ok, refetched} = Fetcher.refetch_object(o)
|
||||
|
||||
assert %{
|
||||
"content" => "test 1",
|
||||
"formerRepresentations" => %{
|
||||
"orderedItems" => [
|
||||
%{"content" => "mew mew #def"},
|
||||
%{"content" => "mew mew 1"}
|
||||
],
|
||||
"totalItems" => 2
|
||||
}
|
||||
} = refetched.data
|
||||
end
|
||||
end
|
||||
|
||||
describe "fetch with history" do
|
||||
setup do
|
||||
object2 = %{
|
||||
"id" => "https://mastodon.social/2",
|
||||
"actor" => "https://mastodon.social/users/emelie",
|
||||
"attributedTo" => "https://mastodon.social/users/emelie",
|
||||
"type" => "Note",
|
||||
"content" => "test 2",
|
||||
"bcc" => [],
|
||||
"bto" => [],
|
||||
"cc" => ["https://mastodon.social/users/emelie/followers"],
|
||||
"to" => [],
|
||||
"summary" => "",
|
||||
"formerRepresentations" => %{
|
||||
"type" => "OrderedCollection",
|
||||
"orderedItems" => [
|
||||
%{
|
||||
"type" => "Note",
|
||||
"content" => "orig 2",
|
||||
"actor" => "https://mastodon.social/users/emelie",
|
||||
"attributedTo" => "https://mastodon.social/users/emelie",
|
||||
"bcc" => [],
|
||||
"bto" => [],
|
||||
"cc" => ["https://mastodon.social/users/emelie/followers"],
|
||||
"to" => [],
|
||||
"summary" => ""
|
||||
}
|
||||
],
|
||||
"totalItems" => 1
|
||||
}
|
||||
}
|
||||
|
||||
mock(fn
|
||||
%{
|
||||
method: :get,
|
||||
url: "https://mastodon.social/2"
|
||||
} ->
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
headers: [{"content-type", "application/activity+json"}],
|
||||
body: Jason.encode!(object2)
|
||||
}
|
||||
|
||||
%{
|
||||
method: :get,
|
||||
url: "https://mastodon.social/users/emelie/collections/featured"
|
||||
} ->
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
headers: [{"content-type", "application/activity+json"}],
|
||||
body:
|
||||
Jason.encode!(%{
|
||||
"id" => "https://mastodon.social/users/emelie/collections/featured",
|
||||
"type" => "OrderedCollection",
|
||||
"actor" => "https://mastodon.social/users/emelie",
|
||||
"attributedTo" => "https://mastodon.social/users/emelie",
|
||||
"orderedItems" => [],
|
||||
"totalItems" => 0
|
||||
})
|
||||
}
|
||||
|
||||
env ->
|
||||
apply(HttpRequestMock, :request, [env])
|
||||
end)
|
||||
|
||||
%{object2: object2}
|
||||
end
|
||||
|
||||
test "it gets history", %{object2: object2} do
|
||||
{:ok, object} = Fetcher.fetch_object_from_id(object2["id"])
|
||||
|
||||
assert %{
|
||||
"formerRepresentations" => %{
|
||||
"type" => "OrderedCollection",
|
||||
"orderedItems" => [%{}]
|
||||
}
|
||||
} = object.data
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
76
test/pleroma/object/updater_test.exs
Normal file
76
test/pleroma/object/updater_test.exs
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Object.UpdaterTest do
|
||||
use Pleroma.DataCase
|
||||
use Oban.Testing, repo: Pleroma.Repo
|
||||
|
||||
import Pleroma.Factory
|
||||
|
||||
alias Pleroma.Object.Updater
|
||||
|
||||
describe "make_update_object_data/3" do
|
||||
setup do
|
||||
note = insert(:note)
|
||||
%{original_data: note.data}
|
||||
end
|
||||
|
||||
test "it makes an updated field", %{original_data: original_data} do
|
||||
new_data = Map.put(original_data, "content", "new content")
|
||||
|
||||
date = Pleroma.Web.ActivityPub.Utils.make_date()
|
||||
update_object_data = Updater.make_update_object_data(original_data, new_data, date)
|
||||
assert %{"updated" => ^date} = update_object_data
|
||||
end
|
||||
|
||||
test "it creates formerRepresentations", %{original_data: original_data} do
|
||||
new_data = Map.put(original_data, "content", "new content")
|
||||
|
||||
date = Pleroma.Web.ActivityPub.Utils.make_date()
|
||||
update_object_data = Updater.make_update_object_data(original_data, new_data, date)
|
||||
|
||||
history_item = original_data |> Map.drop(["id", "formerRepresentations"])
|
||||
|
||||
assert %{
|
||||
"formerRepresentations" => %{
|
||||
"totalItems" => 1,
|
||||
"orderedItems" => [^history_item]
|
||||
}
|
||||
} = update_object_data
|
||||
end
|
||||
end
|
||||
|
||||
describe "make_new_object_data_from_update_object/2" do
|
||||
test "it reuses formerRepresentations if it exists" do
|
||||
%{data: original_data} = insert(:note)
|
||||
|
||||
new_data =
|
||||
original_data
|
||||
|> Map.put("content", "edited")
|
||||
|
||||
date = Pleroma.Web.ActivityPub.Utils.make_date()
|
||||
update_object_data = Updater.make_update_object_data(original_data, new_data, date)
|
||||
|
||||
history = update_object_data["formerRepresentations"]["orderedItems"]
|
||||
|
||||
update_object_data =
|
||||
update_object_data
|
||||
|> put_in(
|
||||
["formerRepresentations", "orderedItems"],
|
||||
history ++ [Map.put(original_data, "summary", "additional summary")]
|
||||
)
|
||||
|> put_in(["formerRepresentations", "totalItems"], length(history) + 1)
|
||||
|
||||
%{
|
||||
updated_data: updated_data,
|
||||
updated: updated,
|
||||
used_history_in_new_object?: used_history_in_new_object?
|
||||
} = Updater.make_new_object_data_from_update_object(original_data, update_object_data)
|
||||
|
||||
assert updated
|
||||
assert used_history_in_new_object?
|
||||
assert updated_data["formerRepresentations"] == update_object_data["formerRepresentations"]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -49,20 +49,22 @@ defmodule Pleroma.UploadTest do
|
|||
test "it returns file" do
|
||||
File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg")
|
||||
|
||||
assert Upload.store(@upload_file) ==
|
||||
{:ok,
|
||||
%{
|
||||
"name" => "image.jpg",
|
||||
"type" => "Document",
|
||||
"mediaType" => "image/jpeg",
|
||||
"url" => [
|
||||
%{
|
||||
"href" => "http://localhost:4001/media/post-process-file.jpg",
|
||||
"mediaType" => "image/jpeg",
|
||||
"type" => "Link"
|
||||
}
|
||||
]
|
||||
}}
|
||||
assert {:ok, result} = Upload.store(@upload_file)
|
||||
|
||||
assert result ==
|
||||
%{
|
||||
"id" => result["id"],
|
||||
"name" => "image.jpg",
|
||||
"type" => "Document",
|
||||
"mediaType" => "image/jpeg",
|
||||
"url" => [
|
||||
%{
|
||||
"href" => "http://localhost:4001/media/post-process-file.jpg",
|
||||
"mediaType" => "image/jpeg",
|
||||
"type" => "Link"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Task.await(Agent.get(TestUploaderSuccess, fn task_pid -> task_pid end))
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicyTest do
|
|||
import Pleroma.Factory
|
||||
import ExUnit.CaptureLog
|
||||
|
||||
alias Pleroma.Web.ActivityPub.MRF
|
||||
alias Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy
|
||||
|
||||
@linkless_message %{
|
||||
|
|
@ -49,11 +50,23 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicyTest do
|
|||
|
||||
assert user.note_count == 0
|
||||
|
||||
message =
|
||||
@linkful_message
|
||||
|> Map.put("actor", user.ap_id)
|
||||
message = %{
|
||||
"type" => "Create",
|
||||
"actor" => user.ap_id,
|
||||
"object" => %{
|
||||
"formerRepresentations" => %{
|
||||
"type" => "OrderedCollection",
|
||||
"orderedItems" => [
|
||||
%{
|
||||
"content" => "<a href='https://example.com'>hi world!</a>"
|
||||
}
|
||||
]
|
||||
},
|
||||
"content" => "mew"
|
||||
}
|
||||
}
|
||||
|
||||
{:reject, _} = AntiLinkSpamPolicy.filter(message)
|
||||
{:reject, _} = MRF.filter_one(AntiLinkSpamPolicy, message)
|
||||
end
|
||||
|
||||
test "it allows posts with links for local users" do
|
||||
|
|
@ -67,6 +80,18 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicyTest do
|
|||
|
||||
{:ok, _message} = AntiLinkSpamPolicy.filter(message)
|
||||
end
|
||||
|
||||
test "it disallows posts with links in history" do
|
||||
user = insert(:user, local: false)
|
||||
|
||||
assert user.note_count == 0
|
||||
|
||||
message =
|
||||
@linkful_message
|
||||
|> Map.put("actor", user.ap_id)
|
||||
|
||||
{:reject, _} = AntiLinkSpamPolicy.filter(message)
|
||||
end
|
||||
end
|
||||
|
||||
describe "with old user" do
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrependedTest do
|
|||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Web.ActivityPub.MRF
|
||||
alias Pleroma.Web.ActivityPub.MRF.EnsureRePrepended
|
||||
|
||||
describe "rewrites summary" do
|
||||
|
|
@ -35,10 +36,58 @@ defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrependedTest do
|
|||
assert {:ok, res} = EnsureRePrepended.filter(message)
|
||||
assert res["object"]["summary"] == "re: object-summary"
|
||||
end
|
||||
|
||||
test "it adds `re:` to history" do
|
||||
message = %{
|
||||
"type" => "Create",
|
||||
"object" => %{
|
||||
"summary" => "object-summary",
|
||||
"inReplyTo" => %Activity{object: %Object{data: %{"summary" => "object-summary"}}},
|
||||
"formerRepresentations" => %{
|
||||
"orderedItems" => [
|
||||
%{
|
||||
"summary" => "object-summary",
|
||||
"inReplyTo" => %Activity{object: %Object{data: %{"summary" => "object-summary"}}}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert {:ok, res} = MRF.filter_one(EnsureRePrepended, message)
|
||||
assert res["object"]["summary"] == "re: object-summary"
|
||||
|
||||
assert Enum.at(res["object"]["formerRepresentations"]["orderedItems"], 0)["summary"] ==
|
||||
"re: object-summary"
|
||||
end
|
||||
|
||||
test "it accepts Updates" do
|
||||
message = %{
|
||||
"type" => "Update",
|
||||
"object" => %{
|
||||
"summary" => "object-summary",
|
||||
"inReplyTo" => %Activity{object: %Object{data: %{"summary" => "object-summary"}}},
|
||||
"formerRepresentations" => %{
|
||||
"orderedItems" => [
|
||||
%{
|
||||
"summary" => "object-summary",
|
||||
"inReplyTo" => %Activity{object: %Object{data: %{"summary" => "object-summary"}}}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert {:ok, res} = MRF.filter_one(EnsureRePrepended, message)
|
||||
assert res["object"]["summary"] == "re: object-summary"
|
||||
|
||||
assert Enum.at(res["object"]["formerRepresentations"]["orderedItems"], 0)["summary"] ==
|
||||
"re: object-summary"
|
||||
end
|
||||
end
|
||||
|
||||
describe "skip filter" do
|
||||
test "it skip if type isn't 'Create'" do
|
||||
test "it skip if type isn't 'Create' or 'Update'" do
|
||||
message = %{
|
||||
"type" => "Annotation",
|
||||
"object" => %{"summary" => "object-summary"}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.ForceMentionsInContentTest do
|
|||
|
||||
alias Pleroma.Constants
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Web.ActivityPub.MRF
|
||||
alias Pleroma.Web.ActivityPub.MRF.ForceMentionsInContent
|
||||
alias Pleroma.Web.CommonAPI
|
||||
|
||||
|
|
@ -161,4 +162,98 @@ defmodule Pleroma.Web.ActivityPub.MRF.ForceMentionsInContentTest do
|
|||
assert filtered ==
|
||||
"<p><span class=\"recipients-inline\"><span class=\"h-card\"><a class=\"u-url mention\" data-user=\"#{luigi.id}\" href=\"#{luigi.ap_id}\" rel=\"ugc\">@<span>luigi</span></a></span> </span>I'ma tired...</p>"
|
||||
end
|
||||
|
||||
test "aware of history" do
|
||||
mario = insert(:user, nickname: "mario")
|
||||
wario = insert(:user, nickname: "wario")
|
||||
|
||||
{:ok, post1} = CommonAPI.post(mario, %{status: "Letsa go!"})
|
||||
|
||||
activity = %{
|
||||
"type" => "Create",
|
||||
"actor" => wario.ap_id,
|
||||
"object" => %{
|
||||
"type" => "Note",
|
||||
"actor" => wario.ap_id,
|
||||
"content" => "WHA-HA!",
|
||||
"to" => [
|
||||
mario.ap_id,
|
||||
Constants.as_public()
|
||||
],
|
||||
"inReplyTo" => post1.object.data["id"],
|
||||
"formerRepresentations" => %{
|
||||
"orderedItems" => [
|
||||
%{
|
||||
"type" => "Note",
|
||||
"actor" => wario.ap_id,
|
||||
"content" => "WHA-HA!",
|
||||
"to" => [
|
||||
mario.ap_id,
|
||||
Constants.as_public()
|
||||
],
|
||||
"inReplyTo" => post1.object.data["id"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expected =
|
||||
"<span class=\"recipients-inline\"><span class=\"h-card\"><a class=\"u-url mention\" data-user=\"#{mario.id}\" href=\"#{mario.ap_id}\" rel=\"ugc\">@<span>mario</span></a></span> </span>WHA-HA!"
|
||||
|
||||
assert {:ok,
|
||||
%{
|
||||
"object" => %{
|
||||
"content" => ^expected,
|
||||
"formerRepresentations" => %{"orderedItems" => [%{"content" => ^expected}]}
|
||||
}
|
||||
}} = MRF.filter_one(ForceMentionsInContent, activity)
|
||||
end
|
||||
|
||||
test "works with Updates" do
|
||||
mario = insert(:user, nickname: "mario")
|
||||
wario = insert(:user, nickname: "wario")
|
||||
|
||||
{:ok, post1} = CommonAPI.post(mario, %{status: "Letsa go!"})
|
||||
|
||||
activity = %{
|
||||
"type" => "Update",
|
||||
"actor" => wario.ap_id,
|
||||
"object" => %{
|
||||
"type" => "Note",
|
||||
"actor" => wario.ap_id,
|
||||
"content" => "WHA-HA!",
|
||||
"to" => [
|
||||
mario.ap_id,
|
||||
Constants.as_public()
|
||||
],
|
||||
"inReplyTo" => post1.object.data["id"],
|
||||
"formerRepresentations" => %{
|
||||
"orderedItems" => [
|
||||
%{
|
||||
"type" => "Note",
|
||||
"actor" => wario.ap_id,
|
||||
"content" => "WHA-HA!",
|
||||
"to" => [
|
||||
mario.ap_id,
|
||||
Constants.as_public()
|
||||
],
|
||||
"inReplyTo" => post1.object.data["id"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expected =
|
||||
"<span class=\"recipients-inline\"><span class=\"h-card\"><a class=\"u-url mention\" data-user=\"#{mario.id}\" href=\"#{mario.ap_id}\" rel=\"ugc\">@<span>mario</span></a></span> </span>WHA-HA!"
|
||||
|
||||
assert {:ok,
|
||||
%{
|
||||
"object" => %{
|
||||
"content" => ^expected,
|
||||
"formerRepresentations" => %{"orderedItems" => [%{"content" => ^expected}]}
|
||||
}
|
||||
}} = MRF.filter_one(ForceMentionsInContent, activity)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -20,6 +20,76 @@ defmodule Pleroma.Web.ActivityPub.MRF.HashtagPolicyTest do
|
|||
assert modified["object"]["sensitive"]
|
||||
end
|
||||
|
||||
test "it is history-aware" do
|
||||
activity = %{
|
||||
"type" => "Create",
|
||||
"object" => %{
|
||||
"content" => "hey",
|
||||
"tag" => []
|
||||
}
|
||||
}
|
||||
|
||||
activity_data =
|
||||
activity
|
||||
|> put_in(
|
||||
["object", "formerRepresentations"],
|
||||
%{
|
||||
"type" => "OrderedCollection",
|
||||
"orderedItems" => [
|
||||
Map.put(
|
||||
activity["object"],
|
||||
"tag",
|
||||
[%{"type" => "Hashtag", "name" => "#nsfw"}]
|
||||
)
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
{:ok, modified} =
|
||||
Pleroma.Web.ActivityPub.MRF.filter_one(
|
||||
Pleroma.Web.ActivityPub.MRF.HashtagPolicy,
|
||||
activity_data
|
||||
)
|
||||
|
||||
refute modified["object"]["sensitive"]
|
||||
assert Enum.at(modified["object"]["formerRepresentations"]["orderedItems"], 0)["sensitive"]
|
||||
end
|
||||
|
||||
test "it works with Update" do
|
||||
activity = %{
|
||||
"type" => "Update",
|
||||
"object" => %{
|
||||
"content" => "hey",
|
||||
"tag" => []
|
||||
}
|
||||
}
|
||||
|
||||
activity_data =
|
||||
activity
|
||||
|> put_in(
|
||||
["object", "formerRepresentations"],
|
||||
%{
|
||||
"type" => "OrderedCollection",
|
||||
"orderedItems" => [
|
||||
Map.put(
|
||||
activity["object"],
|
||||
"tag",
|
||||
[%{"type" => "Hashtag", "name" => "#nsfw"}]
|
||||
)
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
{:ok, modified} =
|
||||
Pleroma.Web.ActivityPub.MRF.filter_one(
|
||||
Pleroma.Web.ActivityPub.MRF.HashtagPolicy,
|
||||
activity_data
|
||||
)
|
||||
|
||||
refute modified["object"]["sensitive"]
|
||||
assert Enum.at(modified["object"]["formerRepresentations"]["orderedItems"], 0)["sensitive"]
|
||||
end
|
||||
|
||||
test "it doesn't sets the sensitive property with irrelevant hashtags" do
|
||||
user = insert(:user)
|
||||
|
||||
|
|
|
|||
|
|
@ -79,6 +79,54 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicyTest do
|
|||
KeywordPolicy.filter(message)
|
||||
end)
|
||||
end
|
||||
|
||||
test "rejects if string matches in history" do
|
||||
clear_config([:mrf_keyword, :reject], ["pun"])
|
||||
|
||||
message = %{
|
||||
"type" => "Create",
|
||||
"object" => %{
|
||||
"content" => "just a daily reminder that compLAINer is a good",
|
||||
"summary" => "",
|
||||
"formerRepresentations" => %{
|
||||
"type" => "OrderedCollection",
|
||||
"orderedItems" => [
|
||||
%{
|
||||
"content" => "just a daily reminder that compLAINer is a good pun",
|
||||
"summary" => ""
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert {:reject, "[KeywordPolicy] Matches with rejected keyword"} =
|
||||
KeywordPolicy.filter(message)
|
||||
end
|
||||
|
||||
test "rejects Updates" do
|
||||
clear_config([:mrf_keyword, :reject], ["pun"])
|
||||
|
||||
message = %{
|
||||
"type" => "Update",
|
||||
"object" => %{
|
||||
"content" => "just a daily reminder that compLAINer is a good",
|
||||
"summary" => "",
|
||||
"formerRepresentations" => %{
|
||||
"type" => "OrderedCollection",
|
||||
"orderedItems" => [
|
||||
%{
|
||||
"content" => "just a daily reminder that compLAINer is a good pun",
|
||||
"summary" => ""
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert {:reject, "[KeywordPolicy] Matches with rejected keyword"} =
|
||||
KeywordPolicy.filter(message)
|
||||
end
|
||||
end
|
||||
|
||||
describe "delisting from ftl based on keywords" do
|
||||
|
|
@ -157,6 +205,31 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicyTest do
|
|||
not (["https://www.w3.org/ns/activitystreams#Public"] == result["to"])
|
||||
end)
|
||||
end
|
||||
|
||||
test "delists if string matches in history" do
|
||||
clear_config([:mrf_keyword, :federated_timeline_removal], ["pun"])
|
||||
|
||||
message = %{
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"type" => "Create",
|
||||
"object" => %{
|
||||
"content" => "just a daily reminder that compLAINer is a good",
|
||||
"summary" => "",
|
||||
"formerRepresentations" => %{
|
||||
"orderedItems" => [
|
||||
%{
|
||||
"content" => "just a daily reminder that compLAINer is a good pun",
|
||||
"summary" => ""
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{:ok, result} = KeywordPolicy.filter(message)
|
||||
assert ["https://www.w3.org/ns/activitystreams#Public"] == result["cc"]
|
||||
refute ["https://www.w3.org/ns/activitystreams#Public"] == result["to"]
|
||||
end
|
||||
end
|
||||
|
||||
describe "replacing keywords" do
|
||||
|
|
@ -221,5 +294,63 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicyTest do
|
|||
result == "ZFS is free software"
|
||||
end)
|
||||
end
|
||||
|
||||
test "replaces keyword if string matches in history" do
|
||||
clear_config([:mrf_keyword, :replace], [{"opensource", "free software"}])
|
||||
|
||||
message = %{
|
||||
"type" => "Create",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"object" => %{
|
||||
"content" => "ZFS is opensource",
|
||||
"summary" => "",
|
||||
"formerRepresentations" => %{
|
||||
"type" => "OrderedCollection",
|
||||
"orderedItems" => [
|
||||
%{"content" => "ZFS is opensource mew mew", "summary" => ""}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{:ok,
|
||||
%{
|
||||
"object" => %{
|
||||
"content" => "ZFS is free software",
|
||||
"formerRepresentations" => %{
|
||||
"orderedItems" => [%{"content" => "ZFS is free software mew mew"}]
|
||||
}
|
||||
}
|
||||
}} = KeywordPolicy.filter(message)
|
||||
end
|
||||
|
||||
test "replaces keyword in Updates" do
|
||||
clear_config([:mrf_keyword, :replace], [{"opensource", "free software"}])
|
||||
|
||||
message = %{
|
||||
"type" => "Update",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"object" => %{
|
||||
"content" => "ZFS is opensource",
|
||||
"summary" => "",
|
||||
"formerRepresentations" => %{
|
||||
"type" => "OrderedCollection",
|
||||
"orderedItems" => [
|
||||
%{"content" => "ZFS is opensource mew mew", "summary" => ""}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{:ok,
|
||||
%{
|
||||
"object" => %{
|
||||
"content" => "ZFS is free software",
|
||||
"formerRepresentations" => %{
|
||||
"orderedItems" => [%{"content" => "ZFS is free software mew mew"}]
|
||||
}
|
||||
}
|
||||
}} = KeywordPolicy.filter(message)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicyTest do
|
|||
use Pleroma.Tests.Helpers
|
||||
|
||||
alias Pleroma.HTTP
|
||||
alias Pleroma.Web.ActivityPub.MRF
|
||||
alias Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy
|
||||
|
||||
import Mock
|
||||
|
|
@ -22,6 +23,25 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicyTest do
|
|||
}
|
||||
}
|
||||
|
||||
@message_with_history %{
|
||||
"type" => "Create",
|
||||
"object" => %{
|
||||
"type" => "Note",
|
||||
"content" => "content",
|
||||
"formerRepresentations" => %{
|
||||
"orderedItems" => [
|
||||
%{
|
||||
"type" => "Note",
|
||||
"content" => "content",
|
||||
"attachment" => [
|
||||
%{"url" => [%{"href" => "http://example.com/image.jpg"}]}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setup do: clear_config([:media_proxy, :enabled], true)
|
||||
|
||||
test "it prefetches media proxy URIs" do
|
||||
|
|
@ -50,4 +70,28 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicyTest do
|
|||
refute called(HTTP.get(:_, :_, :_))
|
||||
end
|
||||
end
|
||||
|
||||
test "history-aware" do
|
||||
Tesla.Mock.mock(fn %{method: :get, url: "http://example.com/image.jpg"} ->
|
||||
{:ok, %Tesla.Env{status: 200, body: ""}}
|
||||
end)
|
||||
|
||||
with_mock HTTP, get: fn _, _, _ -> {:ok, []} end do
|
||||
MRF.filter_one(MediaProxyWarmingPolicy, @message_with_history)
|
||||
|
||||
assert called(HTTP.get(:_, :_, :_))
|
||||
end
|
||||
end
|
||||
|
||||
test "works with Updates" do
|
||||
Tesla.Mock.mock(fn %{method: :get, url: "http://example.com/image.jpg"} ->
|
||||
{:ok, %Tesla.Env{status: 200, body: ""}}
|
||||
end)
|
||||
|
||||
with_mock HTTP, get: fn _, _, _ -> {:ok, []} end do
|
||||
MRF.filter_one(MediaProxyWarmingPolicy, @message_with_history |> Map.put("type", "Update"))
|
||||
|
||||
assert called(HTTP.get(:_, :_, :_))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -151,4 +151,27 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoEmptyPolicyTest do
|
|||
|
||||
assert NoEmptyPolicy.filter(message) == {:reject, "[NoEmptyPolicy]"}
|
||||
end
|
||||
|
||||
test "works with Update" do
|
||||
message = %{
|
||||
"actor" => "http://localhost:4001/users/testuser",
|
||||
"cc" => ["http://localhost:4001/users/testuser/followers"],
|
||||
"object" => %{
|
||||
"actor" => "http://localhost:4001/users/testuser",
|
||||
"attachment" => [],
|
||||
"cc" => ["http://localhost:4001/users/testuser/followers"],
|
||||
"source" => "",
|
||||
"to" => [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"type" => "Note"
|
||||
},
|
||||
"to" => [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"type" => "Update"
|
||||
}
|
||||
|
||||
assert NoEmptyPolicy.filter(message) == {:reject, "[NoEmptyPolicy]"}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
defmodule Pleroma.Web.ActivityPub.MRF.NoPlaceholderTextPolicyTest do
|
||||
use Pleroma.DataCase, async: true
|
||||
alias Pleroma.Web.ActivityPub.MRF
|
||||
alias Pleroma.Web.ActivityPub.MRF.NoPlaceholderTextPolicy
|
||||
|
||||
test "it clears content object" do
|
||||
|
|
@ -20,6 +21,46 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoPlaceholderTextPolicyTest do
|
|||
assert res["object"]["content"] == ""
|
||||
end
|
||||
|
||||
test "history-aware" do
|
||||
message = %{
|
||||
"type" => "Create",
|
||||
"object" => %{
|
||||
"content" => ".",
|
||||
"attachment" => "image",
|
||||
"formerRepresentations" => %{
|
||||
"orderedItems" => [%{"content" => ".", "attachment" => "image"}]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert {:ok, res} = MRF.filter_one(NoPlaceholderTextPolicy, message)
|
||||
|
||||
assert %{
|
||||
"content" => "",
|
||||
"formerRepresentations" => %{"orderedItems" => [%{"content" => ""}]}
|
||||
} = res["object"]
|
||||
end
|
||||
|
||||
test "works with Updates" do
|
||||
message = %{
|
||||
"type" => "Update",
|
||||
"object" => %{
|
||||
"content" => ".",
|
||||
"attachment" => "image",
|
||||
"formerRepresentations" => %{
|
||||
"orderedItems" => [%{"content" => ".", "attachment" => "image"}]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert {:ok, res} = MRF.filter_one(NoPlaceholderTextPolicy, message)
|
||||
|
||||
assert %{
|
||||
"content" => "",
|
||||
"formerRepresentations" => %{"orderedItems" => [%{"content" => ""}]}
|
||||
} = res["object"]
|
||||
end
|
||||
|
||||
@messages [
|
||||
%{
|
||||
"type" => "Create",
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
defmodule Pleroma.Web.ActivityPub.MRF.NormalizeMarkupTest do
|
||||
use Pleroma.DataCase, async: true
|
||||
alias Pleroma.Web.ActivityPub.MRF
|
||||
alias Pleroma.Web.ActivityPub.MRF.NormalizeMarkup
|
||||
|
||||
@html_sample """
|
||||
|
|
@ -16,24 +17,58 @@ defmodule Pleroma.Web.ActivityPub.MRF.NormalizeMarkupTest do
|
|||
<script>alert('hacked')</script>
|
||||
"""
|
||||
|
||||
test "it filter html tags" do
|
||||
expected = """
|
||||
<b>this is in bold</b>
|
||||
<p>this is a paragraph</p>
|
||||
this is a linebreak<br/>
|
||||
this is a link with allowed "rel" attribute: <a href="http://example.com/" rel="tag">example.com</a>
|
||||
this is a link with not allowed "rel" attribute: <a href="http://example.com/">example.com</a>
|
||||
this is an image: <img src="http://example.com/image.jpg"/><br/>
|
||||
alert('hacked')
|
||||
"""
|
||||
@expected """
|
||||
<b>this is in bold</b>
|
||||
<p>this is a paragraph</p>
|
||||
this is a linebreak<br/>
|
||||
this is a link with allowed "rel" attribute: <a href="http://example.com/" rel="tag">example.com</a>
|
||||
this is a link with not allowed "rel" attribute: <a href="http://example.com/">example.com</a>
|
||||
this is an image: <img src="http://example.com/image.jpg"/><br/>
|
||||
alert('hacked')
|
||||
"""
|
||||
|
||||
test "it filter html tags" do
|
||||
message = %{"type" => "Create", "object" => %{"content" => @html_sample}}
|
||||
|
||||
assert {:ok, res} = NormalizeMarkup.filter(message)
|
||||
assert res["object"]["content"] == expected
|
||||
assert res["object"]["content"] == @expected
|
||||
end
|
||||
|
||||
test "it skips filter if type isn't `Create`" do
|
||||
test "history-aware" do
|
||||
message = %{
|
||||
"type" => "Create",
|
||||
"object" => %{
|
||||
"content" => @html_sample,
|
||||
"formerRepresentations" => %{"orderedItems" => [%{"content" => @html_sample}]}
|
||||
}
|
||||
}
|
||||
|
||||
assert {:ok, res} = MRF.filter_one(NormalizeMarkup, message)
|
||||
|
||||
assert %{
|
||||
"content" => @expected,
|
||||
"formerRepresentations" => %{"orderedItems" => [%{"content" => @expected}]}
|
||||
} = res["object"]
|
||||
end
|
||||
|
||||
test "works with Updates" do
|
||||
message = %{
|
||||
"type" => "Update",
|
||||
"object" => %{
|
||||
"content" => @html_sample,
|
||||
"formerRepresentations" => %{"orderedItems" => [%{"content" => @html_sample}]}
|
||||
}
|
||||
}
|
||||
|
||||
assert {:ok, res} = MRF.filter_one(NormalizeMarkup, message)
|
||||
|
||||
assert %{
|
||||
"content" => @expected,
|
||||
"formerRepresentations" => %{"orderedItems" => [%{"content" => @expected}]}
|
||||
} = res["object"]
|
||||
end
|
||||
|
||||
test "it skips filter if type isn't `Create` or `Update`" do
|
||||
message = %{"type" => "Note", "object" => %{}}
|
||||
|
||||
assert {:ok, res} = NormalizeMarkup.filter(message)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidatorTest do
|
||||
use Pleroma.DataCase, async: true
|
||||
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidator
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
|
||||
|
|
@ -31,6 +32,54 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidatorTest
|
|||
test "a basic note validates", %{note: note} do
|
||||
%{valid?: true} = ArticleNotePageValidator.cast_and_validate(note)
|
||||
end
|
||||
|
||||
test "a note from factory validates" do
|
||||
note = insert(:note)
|
||||
%{valid?: true} = ArticleNotePageValidator.cast_and_validate(note.data)
|
||||
end
|
||||
end
|
||||
|
||||
describe "Note with history" do
|
||||
setup do
|
||||
user = insert(:user)
|
||||
{:ok, activity} = Pleroma.Web.CommonAPI.post(user, %{status: "mew mew :dinosaur:"})
|
||||
{:ok, edit} = Pleroma.Web.CommonAPI.update(user, activity, %{status: "edited :blank:"})
|
||||
|
||||
{:ok, %{"object" => external_rep}} =
|
||||
Pleroma.Web.ActivityPub.Transmogrifier.prepare_outgoing(edit.data)
|
||||
|
||||
%{external_rep: external_rep}
|
||||
end
|
||||
|
||||
test "edited note", %{external_rep: external_rep} do
|
||||
assert %{"formerRepresentations" => %{"orderedItems" => [%{"tag" => [_]}]}} = external_rep
|
||||
|
||||
{:ok, validate_res, []} = ObjectValidator.validate(external_rep, [])
|
||||
|
||||
assert %{"formerRepresentations" => %{"orderedItems" => [%{"emoji" => %{"dinosaur" => _}}]}} =
|
||||
validate_res
|
||||
end
|
||||
|
||||
test "edited note, badly-formed formerRepresentations", %{external_rep: external_rep} do
|
||||
external_rep = Map.put(external_rep, "formerRepresentations", %{})
|
||||
|
||||
assert {:error, _} = ObjectValidator.validate(external_rep, [])
|
||||
end
|
||||
|
||||
test "edited note, badly-formed history item", %{external_rep: external_rep} do
|
||||
history_item =
|
||||
Enum.at(external_rep["formerRepresentations"]["orderedItems"], 0)
|
||||
|> Map.put("type", "Foo")
|
||||
|
||||
external_rep =
|
||||
put_in(
|
||||
external_rep,
|
||||
["formerRepresentations", "orderedItems"],
|
||||
[history_item]
|
||||
)
|
||||
|
||||
assert {:error, _} = ObjectValidator.validate(external_rep, [])
|
||||
end
|
||||
end
|
||||
|
||||
test "a Note from Roadhouse validates" do
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.UpdateHandlingTest do
|
|||
test "returns an error if the object can't be updated by the actor", %{
|
||||
valid_update: valid_update
|
||||
} do
|
||||
other_user = insert(:user)
|
||||
other_user = insert(:user, local: false)
|
||||
|
||||
update =
|
||||
valid_update
|
||||
|
|
@ -40,5 +40,129 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.UpdateHandlingTest do
|
|||
|
||||
assert {:error, _cng} = ObjectValidator.validate(update, [])
|
||||
end
|
||||
|
||||
test "validates as long as the object is same-origin with the actor", %{
|
||||
valid_update: valid_update
|
||||
} do
|
||||
other_user = insert(:user)
|
||||
|
||||
update =
|
||||
valid_update
|
||||
|> Map.put("actor", other_user.ap_id)
|
||||
|
||||
assert {:ok, _update, []} = ObjectValidator.validate(update, [])
|
||||
end
|
||||
|
||||
test "validates if the object is not of an Actor type" do
|
||||
note = insert(:note)
|
||||
updated_note = note.data |> Map.put("content", "edited content")
|
||||
other_user = insert(:user)
|
||||
|
||||
{:ok, update, _} = Builder.update(other_user, updated_note)
|
||||
|
||||
assert {:ok, _update, _} = ObjectValidator.validate(update, [])
|
||||
end
|
||||
end
|
||||
|
||||
describe "update note" do
|
||||
test "converts object into Pleroma's format" do
|
||||
mastodon_tags = [
|
||||
%{
|
||||
"icon" => %{
|
||||
"mediaType" => "image/png",
|
||||
"type" => "Image",
|
||||
"url" => "https://somewhere.org/emoji/url/1.png"
|
||||
},
|
||||
"id" => "https://somewhere.org/emoji/1",
|
||||
"name" => ":some_emoji:",
|
||||
"type" => "Emoji",
|
||||
"updated" => "2021-04-07T11:00:00Z"
|
||||
}
|
||||
]
|
||||
|
||||
user = insert(:user)
|
||||
note = insert(:note, user: user)
|
||||
|
||||
updated_note =
|
||||
note.data
|
||||
|> Map.put("content", "edited content")
|
||||
|> Map.put("tag", mastodon_tags)
|
||||
|
||||
{:ok, update, _} = Builder.update(user, updated_note)
|
||||
|
||||
assert {:ok, _update, meta} = ObjectValidator.validate(update, [])
|
||||
|
||||
assert %{"emoji" => %{"some_emoji" => "https://somewhere.org/emoji/url/1.png"}} =
|
||||
meta[:object_data]
|
||||
end
|
||||
|
||||
test "returns no object_data in meta for a local Update" do
|
||||
user = insert(:user)
|
||||
note = insert(:note, user: user)
|
||||
|
||||
updated_note =
|
||||
note.data
|
||||
|> Map.put("content", "edited content")
|
||||
|
||||
{:ok, update, _} = Builder.update(user, updated_note)
|
||||
|
||||
assert {:ok, _update, meta} = ObjectValidator.validate(update, local: true)
|
||||
assert is_nil(meta[:object_data])
|
||||
end
|
||||
|
||||
test "returns object_data in meta for a remote Update" do
|
||||
user = insert(:user)
|
||||
note = insert(:note, user: user)
|
||||
|
||||
updated_note =
|
||||
note.data
|
||||
|> Map.put("content", "edited content")
|
||||
|
||||
{:ok, update, _} = Builder.update(user, updated_note)
|
||||
|
||||
assert {:ok, _update, meta} = ObjectValidator.validate(update, local: false)
|
||||
assert meta[:object_data]
|
||||
|
||||
assert {:ok, _update, meta} = ObjectValidator.validate(update, [])
|
||||
assert meta[:object_data]
|
||||
end
|
||||
end
|
||||
|
||||
describe "update with history" do
|
||||
setup do
|
||||
user = insert(:user)
|
||||
{:ok, activity} = Pleroma.Web.CommonAPI.post(user, %{status: "mew mew :dinosaur:"})
|
||||
{:ok, edit} = Pleroma.Web.CommonAPI.update(user, activity, %{status: "edited :blank:"})
|
||||
{:ok, external_rep} = Pleroma.Web.ActivityPub.Transmogrifier.prepare_outgoing(edit.data)
|
||||
%{external_rep: external_rep}
|
||||
end
|
||||
|
||||
test "edited note", %{external_rep: external_rep} do
|
||||
{:ok, _validate_res, meta} = ObjectValidator.validate(external_rep, [])
|
||||
|
||||
assert %{"formerRepresentations" => %{"orderedItems" => [%{"emoji" => %{"dinosaur" => _}}]}} =
|
||||
meta[:object_data]
|
||||
end
|
||||
|
||||
test "edited note, badly-formed formerRepresentations", %{external_rep: external_rep} do
|
||||
external_rep = put_in(external_rep, ["object", "formerRepresentations"], %{})
|
||||
|
||||
assert {:error, _} = ObjectValidator.validate(external_rep, [])
|
||||
end
|
||||
|
||||
test "edited note, badly-formed history item", %{external_rep: external_rep} do
|
||||
history_item =
|
||||
Enum.at(external_rep["object"]["formerRepresentations"]["orderedItems"], 0)
|
||||
|> Map.put("type", "Foo")
|
||||
|
||||
external_rep =
|
||||
put_in(
|
||||
external_rep,
|
||||
["object", "formerRepresentations", "orderedItems"],
|
||||
[history_item]
|
||||
)
|
||||
|
||||
assert {:error, _} = ObjectValidator.validate(external_rep, [])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -118,7 +118,10 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do
|
|||
describe "update users" do
|
||||
setup do
|
||||
user = insert(:user, local: false)
|
||||
{:ok, update_data, []} = Builder.update(user, %{"id" => user.ap_id, "name" => "new name!"})
|
||||
|
||||
{:ok, update_data, []} =
|
||||
Builder.update(user, %{"id" => user.ap_id, "type" => "Person", "name" => "new name!"})
|
||||
|
||||
{:ok, update, _meta} = ActivityPub.persist(update_data, local: true)
|
||||
|
||||
%{user: user, update_data: update_data, update: update}
|
||||
|
|
@ -140,6 +143,298 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do
|
|||
end
|
||||
end
|
||||
|
||||
describe "update notes" do
|
||||
setup do
|
||||
make_time = fn ->
|
||||
Pleroma.Web.ActivityPub.Utils.make_date()
|
||||
end
|
||||
|
||||
user = insert(:user)
|
||||
note = insert(:note, user: user, data: %{"published" => make_time.()})
|
||||
_note_activity = insert(:note_activity, note: note)
|
||||
|
||||
updated_note =
|
||||
note.data
|
||||
|> Map.put("summary", "edited summary")
|
||||
|> Map.put("content", "edited content")
|
||||
|> Map.put("updated", make_time.())
|
||||
|
||||
{:ok, update_data, []} = Builder.update(user, updated_note)
|
||||
{:ok, update, _meta} = ActivityPub.persist(update_data, local: true)
|
||||
|
||||
%{
|
||||
user: user,
|
||||
note: note,
|
||||
object_id: note.id,
|
||||
update_data: update_data,
|
||||
update: update,
|
||||
updated_note: updated_note
|
||||
}
|
||||
end
|
||||
|
||||
test "it updates the note", %{
|
||||
object_id: object_id,
|
||||
update: update,
|
||||
updated_note: updated_note
|
||||
} do
|
||||
{:ok, _, _} = SideEffects.handle(update, object_data: updated_note)
|
||||
updated_time = updated_note["updated"]
|
||||
|
||||
new_note = Pleroma.Object.get_by_id(object_id)
|
||||
|
||||
assert %{
|
||||
"summary" => "edited summary",
|
||||
"content" => "edited content",
|
||||
"updated" => ^updated_time
|
||||
} = new_note.data
|
||||
end
|
||||
|
||||
test "it rejects updates with no updated attribute in object", %{
|
||||
object_id: object_id,
|
||||
update: update,
|
||||
updated_note: updated_note
|
||||
} do
|
||||
old_note = Pleroma.Object.get_by_id(object_id)
|
||||
updated_note = Map.drop(updated_note, ["updated"])
|
||||
{:ok, _, _} = SideEffects.handle(update, object_data: updated_note)
|
||||
new_note = Pleroma.Object.get_by_id(object_id)
|
||||
assert old_note.data == new_note.data
|
||||
end
|
||||
|
||||
test "it rejects updates with updated attribute older than what we have in the original object",
|
||||
%{
|
||||
object_id: object_id,
|
||||
update: update,
|
||||
updated_note: updated_note
|
||||
} do
|
||||
old_note = Pleroma.Object.get_by_id(object_id)
|
||||
{:ok, creation_time, _} = DateTime.from_iso8601(old_note.data["published"])
|
||||
|
||||
updated_note =
|
||||
Map.put(updated_note, "updated", DateTime.to_iso8601(DateTime.add(creation_time, -10)))
|
||||
|
||||
{:ok, _, _} = SideEffects.handle(update, object_data: updated_note)
|
||||
new_note = Pleroma.Object.get_by_id(object_id)
|
||||
assert old_note.data == new_note.data
|
||||
end
|
||||
|
||||
test "it rejects updates with updated attribute older than the last Update", %{
|
||||
object_id: object_id,
|
||||
update: update,
|
||||
updated_note: updated_note
|
||||
} do
|
||||
old_note = Pleroma.Object.get_by_id(object_id)
|
||||
{:ok, creation_time, _} = DateTime.from_iso8601(old_note.data["published"])
|
||||
|
||||
updated_note =
|
||||
Map.put(updated_note, "updated", DateTime.to_iso8601(DateTime.add(creation_time, +10)))
|
||||
|
||||
{:ok, _, _} = SideEffects.handle(update, object_data: updated_note)
|
||||
|
||||
old_note = Pleroma.Object.get_by_id(object_id)
|
||||
{:ok, update_time, _} = DateTime.from_iso8601(old_note.data["updated"])
|
||||
|
||||
updated_note =
|
||||
Map.put(updated_note, "updated", DateTime.to_iso8601(DateTime.add(update_time, -5)))
|
||||
|
||||
{:ok, _, _} = SideEffects.handle(update, object_data: updated_note)
|
||||
|
||||
new_note = Pleroma.Object.get_by_id(object_id)
|
||||
assert old_note.data == new_note.data
|
||||
end
|
||||
|
||||
test "it updates using object_data", %{
|
||||
object_id: object_id,
|
||||
update: update,
|
||||
updated_note: updated_note
|
||||
} do
|
||||
updated_note = Map.put(updated_note, "summary", "mew mew")
|
||||
{:ok, _, _} = SideEffects.handle(update, object_data: updated_note)
|
||||
new_note = Pleroma.Object.get_by_id(object_id)
|
||||
assert %{"summary" => "mew mew", "content" => "edited content"} = new_note.data
|
||||
end
|
||||
|
||||
test "it records the original note in formerRepresentations", %{
|
||||
note: note,
|
||||
object_id: object_id,
|
||||
update: update,
|
||||
updated_note: updated_note
|
||||
} do
|
||||
{:ok, _, _} = SideEffects.handle(update, object_data: updated_note)
|
||||
%{data: new_note} = Pleroma.Object.get_by_id(object_id)
|
||||
assert %{"summary" => "edited summary", "content" => "edited content"} = new_note
|
||||
|
||||
assert [Map.drop(note.data, ["id", "formerRepresentations"])] ==
|
||||
new_note["formerRepresentations"]["orderedItems"]
|
||||
|
||||
assert new_note["formerRepresentations"]["totalItems"] == 1
|
||||
end
|
||||
|
||||
test "it puts the original note at the front of formerRepresentations", %{
|
||||
user: user,
|
||||
note: note,
|
||||
object_id: object_id,
|
||||
update: update,
|
||||
updated_note: updated_note
|
||||
} do
|
||||
{:ok, _, _} = SideEffects.handle(update, object_data: updated_note)
|
||||
%{data: first_edit} = Pleroma.Object.get_by_id(object_id)
|
||||
|
||||
second_updated_note =
|
||||
note.data
|
||||
|> Map.put("summary", "edited summary 2")
|
||||
|> Map.put("content", "edited content 2")
|
||||
|> Map.put(
|
||||
"updated",
|
||||
first_edit["updated"]
|
||||
|> DateTime.from_iso8601()
|
||||
|> elem(1)
|
||||
|> DateTime.add(10)
|
||||
|> DateTime.to_iso8601()
|
||||
)
|
||||
|
||||
{:ok, second_update_data, []} = Builder.update(user, second_updated_note)
|
||||
{:ok, update, _meta} = ActivityPub.persist(second_update_data, local: true)
|
||||
{:ok, _, _} = SideEffects.handle(update, object_data: second_updated_note)
|
||||
%{data: new_note} = Pleroma.Object.get_by_id(object_id)
|
||||
assert %{"summary" => "edited summary 2", "content" => "edited content 2"} = new_note
|
||||
|
||||
original_version = Map.drop(note.data, ["id", "formerRepresentations"])
|
||||
first_edit = Map.drop(first_edit, ["id", "formerRepresentations"])
|
||||
|
||||
assert [first_edit, original_version] ==
|
||||
new_note["formerRepresentations"]["orderedItems"]
|
||||
|
||||
assert new_note["formerRepresentations"]["totalItems"] == 2
|
||||
end
|
||||
|
||||
test "it does not prepend to formerRepresentations if no actual changes are made", %{
|
||||
note: note,
|
||||
object_id: object_id,
|
||||
update: update,
|
||||
updated_note: updated_note
|
||||
} do
|
||||
{:ok, _, _} = SideEffects.handle(update, object_data: updated_note)
|
||||
%{data: first_edit} = Pleroma.Object.get_by_id(object_id)
|
||||
|
||||
updated_note =
|
||||
updated_note
|
||||
|> Map.put(
|
||||
"updated",
|
||||
first_edit["updated"]
|
||||
|> DateTime.from_iso8601()
|
||||
|> elem(1)
|
||||
|> DateTime.add(10)
|
||||
|> DateTime.to_iso8601()
|
||||
)
|
||||
|
||||
{:ok, _, _} = SideEffects.handle(update, object_data: updated_note)
|
||||
%{data: new_note} = Pleroma.Object.get_by_id(object_id)
|
||||
assert %{"summary" => "edited summary", "content" => "edited content"} = new_note
|
||||
|
||||
original_version = Map.drop(note.data, ["id", "formerRepresentations"])
|
||||
|
||||
assert [original_version] ==
|
||||
new_note["formerRepresentations"]["orderedItems"]
|
||||
|
||||
assert new_note["formerRepresentations"]["totalItems"] == 1
|
||||
end
|
||||
end
|
||||
|
||||
describe "update questions" do
|
||||
setup do
|
||||
user = insert(:user)
|
||||
|
||||
question =
|
||||
insert(:question,
|
||||
user: user,
|
||||
data: %{"published" => Pleroma.Web.ActivityPub.Utils.make_date()}
|
||||
)
|
||||
|
||||
%{user: user, data: question.data, id: question.id}
|
||||
end
|
||||
|
||||
test "allows updating choice count without generating edit history", %{
|
||||
user: user,
|
||||
data: data,
|
||||
id: id
|
||||
} do
|
||||
new_choices =
|
||||
data["oneOf"]
|
||||
|> Enum.map(fn choice -> put_in(choice, ["replies", "totalItems"], 5) end)
|
||||
|
||||
updated_question =
|
||||
data
|
||||
|> Map.put("oneOf", new_choices)
|
||||
|> Map.put("updated", Pleroma.Web.ActivityPub.Utils.make_date())
|
||||
|
||||
{:ok, update_data, []} = Builder.update(user, updated_question)
|
||||
{:ok, update, _meta} = ActivityPub.persist(update_data, local: true)
|
||||
|
||||
{:ok, _, _} = SideEffects.handle(update, object_data: updated_question)
|
||||
|
||||
%{data: new_question} = Pleroma.Object.get_by_id(id)
|
||||
|
||||
assert [%{"replies" => %{"totalItems" => 5}}, %{"replies" => %{"totalItems" => 5}}] =
|
||||
new_question["oneOf"]
|
||||
|
||||
refute Map.has_key?(new_question, "formerRepresentations")
|
||||
end
|
||||
|
||||
test "allows updating choice count without updated field", %{
|
||||
user: user,
|
||||
data: data,
|
||||
id: id
|
||||
} do
|
||||
new_choices =
|
||||
data["oneOf"]
|
||||
|> Enum.map(fn choice -> put_in(choice, ["replies", "totalItems"], 5) end)
|
||||
|
||||
updated_question =
|
||||
data
|
||||
|> Map.put("oneOf", new_choices)
|
||||
|
||||
{:ok, update_data, []} = Builder.update(user, updated_question)
|
||||
{:ok, update, _meta} = ActivityPub.persist(update_data, local: true)
|
||||
|
||||
{:ok, _, _} = SideEffects.handle(update, object_data: updated_question)
|
||||
|
||||
%{data: new_question} = Pleroma.Object.get_by_id(id)
|
||||
|
||||
assert [%{"replies" => %{"totalItems" => 5}}, %{"replies" => %{"totalItems" => 5}}] =
|
||||
new_question["oneOf"]
|
||||
|
||||
refute Map.has_key?(new_question, "formerRepresentations")
|
||||
end
|
||||
|
||||
test "allows updating choice count with updated field same as the creation date", %{
|
||||
user: user,
|
||||
data: data,
|
||||
id: id
|
||||
} do
|
||||
new_choices =
|
||||
data["oneOf"]
|
||||
|> Enum.map(fn choice -> put_in(choice, ["replies", "totalItems"], 5) end)
|
||||
|
||||
updated_question =
|
||||
data
|
||||
|> Map.put("oneOf", new_choices)
|
||||
|> Map.put("updated", data["published"])
|
||||
|
||||
{:ok, update_data, []} = Builder.update(user, updated_question)
|
||||
{:ok, update, _meta} = ActivityPub.persist(update_data, local: true)
|
||||
|
||||
{:ok, _, _} = SideEffects.handle(update, object_data: updated_question)
|
||||
|
||||
%{data: new_question} = Pleroma.Object.get_by_id(id)
|
||||
|
||||
assert [%{"replies" => %{"totalItems" => 5}}, %{"replies" => %{"totalItems" => 5}}] =
|
||||
new_question["oneOf"]
|
||||
|
||||
refute Map.has_key?(new_question, "formerRepresentations")
|
||||
end
|
||||
end
|
||||
|
||||
describe "EmojiReact objects" do
|
||||
setup do
|
||||
poster = insert(:user)
|
||||
|
|
|
|||
|
|
@ -312,6 +312,28 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
|||
|
||||
assert url == "http://localhost:4001/emoji/dino%20walking.gif"
|
||||
end
|
||||
|
||||
test "Updates of Notes are handled" do
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "everybody do the dinosaur :dinosaur:"})
|
||||
{:ok, update} = CommonAPI.update(user, activity, %{status: "mew mew :blank:"})
|
||||
|
||||
{:ok, prepared} = Transmogrifier.prepare_outgoing(update.data)
|
||||
|
||||
assert %{
|
||||
"content" => "mew mew :blank:",
|
||||
"tag" => [%{"name" => ":blank:", "type" => "Emoji"}],
|
||||
"formerRepresentations" => %{
|
||||
"orderedItems" => [
|
||||
%{
|
||||
"content" => "everybody do the dinosaur :dinosaur:",
|
||||
"tag" => [%{"name" => ":dinosaur:", "type" => "Emoji"}]
|
||||
}
|
||||
]
|
||||
}
|
||||
} = prepared["object"]
|
||||
end
|
||||
end
|
||||
|
||||
describe "user upgrade" do
|
||||
|
|
@ -575,4 +597,43 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
|||
assert Transmogrifier.fix_attachments(object) == expected
|
||||
end
|
||||
end
|
||||
|
||||
describe "prepare_object/1" do
|
||||
test "it processes history" do
|
||||
original = %{
|
||||
"formerRepresentations" => %{
|
||||
"orderedItems" => [
|
||||
%{
|
||||
"generator" => %{},
|
||||
"emoji" => %{"blobcat" => "http://localhost:4001/emoji/blobcat.png"}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
processed = Transmogrifier.prepare_object(original)
|
||||
|
||||
history_item = Enum.at(processed["formerRepresentations"]["orderedItems"], 0)
|
||||
|
||||
refute Map.has_key?(history_item, "generator")
|
||||
|
||||
assert [%{"name" => ":blobcat:"}] = history_item["tag"]
|
||||
end
|
||||
|
||||
test "it works when there is no or bad history" do
|
||||
original = %{
|
||||
"formerRepresentations" => %{
|
||||
"items" => [
|
||||
%{
|
||||
"generator" => %{},
|
||||
"emoji" => %{"blobcat" => "http://localhost:4001/emoji/blobcat.png"}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
processed = Transmogrifier.prepare_object(original)
|
||||
assert processed["formerRepresentations"] == original["formerRepresentations"]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -596,7 +596,7 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
object = Object.normalize(activity, fetch: false)
|
||||
|
||||
assert object.data["content"] == "<p><b>2hu</b></p>alert('xss')"
|
||||
assert object.data["source"] == post
|
||||
assert object.data["source"]["content"] == post
|
||||
end
|
||||
|
||||
test "it filters out obviously bad tags when accepting a post as Markdown" do
|
||||
|
|
@ -613,7 +613,7 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
object = Object.normalize(activity, fetch: false)
|
||||
|
||||
assert object.data["content"] == "<p><b>2hu</b></p>"
|
||||
assert object.data["source"] == post
|
||||
assert object.data["source"]["content"] == post
|
||||
end
|
||||
|
||||
test "it does not allow replies to direct messages that are not direct messages themselves" do
|
||||
|
|
@ -1551,4 +1551,128 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "update/3" do
|
||||
test "updates a post" do
|
||||
user = insert(:user)
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "foo1", spoiler_text: "title 1"})
|
||||
|
||||
{:ok, updated} = CommonAPI.update(user, activity, %{status: "updated 2"})
|
||||
|
||||
updated_object = Object.normalize(updated)
|
||||
assert updated_object.data["content"] == "updated 2"
|
||||
assert Map.get(updated_object.data, "summary", "") == ""
|
||||
assert Map.has_key?(updated_object.data, "updated")
|
||||
end
|
||||
|
||||
test "does not change visibility" do
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.post(user, %{status: "foo1", spoiler_text: "title 1", visibility: "private"})
|
||||
|
||||
{:ok, updated} = CommonAPI.update(user, activity, %{status: "updated 2"})
|
||||
|
||||
updated_object = Object.normalize(updated)
|
||||
assert updated_object.data["content"] == "updated 2"
|
||||
assert Map.get(updated_object.data, "summary", "") == ""
|
||||
assert Visibility.get_visibility(updated_object) == "private"
|
||||
assert Visibility.get_visibility(updated) == "private"
|
||||
end
|
||||
|
||||
test "updates a post with emoji" do
|
||||
[{emoji1, _}, {emoji2, _} | _] = Pleroma.Emoji.get_all()
|
||||
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.post(user, %{status: "foo1", spoiler_text: "title 1 :#{emoji1}:"})
|
||||
|
||||
{:ok, updated} = CommonAPI.update(user, activity, %{status: "updated 2 :#{emoji2}:"})
|
||||
|
||||
updated_object = Object.normalize(updated)
|
||||
assert updated_object.data["content"] == "updated 2 :#{emoji2}:"
|
||||
assert %{^emoji2 => _} = updated_object.data["emoji"]
|
||||
end
|
||||
|
||||
test "updates a post with emoji and federate properly" do
|
||||
[{emoji1, _}, {emoji2, _} | _] = Pleroma.Emoji.get_all()
|
||||
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.post(user, %{status: "foo1", spoiler_text: "title 1 :#{emoji1}:"})
|
||||
|
||||
clear_config([:instance, :federating], true)
|
||||
|
||||
with_mock Pleroma.Web.Federator,
|
||||
publish: fn _p -> nil end do
|
||||
{:ok, updated} = CommonAPI.update(user, activity, %{status: "updated 2 :#{emoji2}:"})
|
||||
|
||||
assert updated.data["object"]["content"] == "updated 2 :#{emoji2}:"
|
||||
assert %{^emoji2 => _} = updated.data["object"]["emoji"]
|
||||
|
||||
assert called(Pleroma.Web.Federator.publish(updated))
|
||||
end
|
||||
end
|
||||
|
||||
test "editing a post that copied a remote title with remote emoji should keep that emoji" do
|
||||
remote_emoji_uri = "https://remote.org/emoji.png"
|
||||
|
||||
note =
|
||||
insert(
|
||||
:note,
|
||||
data: %{
|
||||
"summary" => ":remoteemoji:",
|
||||
"emoji" => %{
|
||||
"remoteemoji" => remote_emoji_uri
|
||||
},
|
||||
"tag" => [
|
||||
%{
|
||||
"type" => "Emoji",
|
||||
"name" => "remoteemoji",
|
||||
"icon" => %{"url" => remote_emoji_uri}
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
note_activity = insert(:note_activity, note: note)
|
||||
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, reply} =
|
||||
CommonAPI.post(user, %{
|
||||
status: "reply",
|
||||
spoiler_text: ":remoteemoji:",
|
||||
in_reply_to_id: note_activity.id
|
||||
})
|
||||
|
||||
assert reply.object.data["emoji"]["remoteemoji"] == remote_emoji_uri
|
||||
|
||||
{:ok, edit} =
|
||||
CommonAPI.update(user, reply, %{status: "reply mew mew", spoiler_text: ":remoteemoji:"})
|
||||
|
||||
edited_note = Pleroma.Object.normalize(edit)
|
||||
|
||||
assert edited_note.data["emoji"]["remoteemoji"] == remote_emoji_uri
|
||||
end
|
||||
|
||||
test "respects MRF" do
|
||||
user = insert(:user)
|
||||
|
||||
clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.KeywordPolicy])
|
||||
clear_config([:mrf_keyword, :replace], [{"updated", "mewmew"}])
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "foo1", spoiler_text: "updated 1"})
|
||||
assert Object.normalize(activity).data["summary"] == "mewmew 1"
|
||||
|
||||
{:ok, updated} = CommonAPI.update(user, activity, %{status: "updated 2"})
|
||||
|
||||
updated_object = Object.normalize(updated)
|
||||
assert updated_object.data["content"] == "mewmew 2"
|
||||
assert Map.get(updated_object.data, "summary", "") == ""
|
||||
assert Map.has_key?(updated_object.data, "updated")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2017,4 +2017,178 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
|
|||
} = result
|
||||
end
|
||||
end
|
||||
|
||||
describe "get status history" do
|
||||
setup do
|
||||
%{conn: build_conn()}
|
||||
end
|
||||
|
||||
test "unedited post", %{conn: conn} do
|
||||
activity = insert(:note_activity)
|
||||
|
||||
conn = get(conn, "/api/v1/statuses/#{activity.id}/history")
|
||||
|
||||
assert [_] = json_response_and_validate_schema(conn, 200)
|
||||
end
|
||||
|
||||
test "edited post", %{conn: conn} do
|
||||
note =
|
||||
insert(
|
||||
:note,
|
||||
data: %{
|
||||
"formerRepresentations" => %{
|
||||
"type" => "OrderedCollection",
|
||||
"orderedItems" => [
|
||||
%{
|
||||
"type" => "Note",
|
||||
"content" => "mew mew 2",
|
||||
"summary" => "title 2"
|
||||
},
|
||||
%{
|
||||
"type" => "Note",
|
||||
"content" => "mew mew 1",
|
||||
"summary" => "title 1"
|
||||
}
|
||||
],
|
||||
"totalItems" => 2
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
activity = insert(:note_activity, note: note)
|
||||
|
||||
conn = get(conn, "/api/v1/statuses/#{activity.id}/history")
|
||||
|
||||
assert [%{"spoiler_text" => "title 1"}, %{"spoiler_text" => "title 2"}, _] =
|
||||
json_response_and_validate_schema(conn, 200)
|
||||
end
|
||||
end
|
||||
|
||||
describe "get status source" do
|
||||
setup do
|
||||
%{conn: build_conn()}
|
||||
end
|
||||
|
||||
test "it returns the source", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "mew mew #abc", spoiler_text: "#def"})
|
||||
|
||||
conn = get(conn, "/api/v1/statuses/#{activity.id}/source")
|
||||
|
||||
id = activity.id
|
||||
|
||||
assert %{"id" => ^id, "text" => "mew mew #abc", "spoiler_text" => "#def"} =
|
||||
json_response_and_validate_schema(conn, 200)
|
||||
end
|
||||
end
|
||||
|
||||
describe "update status" do
|
||||
setup do
|
||||
oauth_access(["write:statuses"])
|
||||
end
|
||||
|
||||
test "it updates the status" do
|
||||
%{conn: conn, user: user} = oauth_access(["write:statuses", "read:statuses"])
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "mew mew #abc", spoiler_text: "#def"})
|
||||
|
||||
conn
|
||||
|> get("/api/v1/statuses/#{activity.id}")
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
response =
|
||||
conn
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> put("/api/v1/statuses/#{activity.id}", %{
|
||||
"status" => "edited",
|
||||
"spoiler_text" => "lol"
|
||||
})
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
assert response["content"] == "edited"
|
||||
assert response["spoiler_text"] == "lol"
|
||||
|
||||
response =
|
||||
conn
|
||||
|> get("/api/v1/statuses/#{activity.id}")
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
assert response["content"] == "edited"
|
||||
assert response["spoiler_text"] == "lol"
|
||||
end
|
||||
|
||||
test "it updates the attachments", %{conn: conn, user: user} do
|
||||
attachment = insert(:attachment, user: user)
|
||||
attachment_id = to_string(attachment.id)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "mew mew #abc", spoiler_text: "#def"})
|
||||
|
||||
response =
|
||||
conn
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> put("/api/v1/statuses/#{activity.id}", %{
|
||||
"status" => "mew mew #abc",
|
||||
"spoiler_text" => "#def",
|
||||
"media_ids" => [attachment_id]
|
||||
})
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
assert [%{"id" => ^attachment_id}] = response["media_attachments"]
|
||||
end
|
||||
|
||||
test "it does not update visibility", %{conn: conn, user: user} do
|
||||
{:ok, activity} =
|
||||
CommonAPI.post(user, %{
|
||||
status: "mew mew #abc",
|
||||
spoiler_text: "#def",
|
||||
visibility: "private"
|
||||
})
|
||||
|
||||
response =
|
||||
conn
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> put("/api/v1/statuses/#{activity.id}", %{
|
||||
"status" => "edited",
|
||||
"spoiler_text" => "lol"
|
||||
})
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
assert response["visibility"] == "private"
|
||||
end
|
||||
|
||||
test "it refuses to update when original post is not by the user", %{conn: conn} do
|
||||
another_user = insert(:user)
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.post(another_user, %{status: "mew mew #abc", spoiler_text: "#def"})
|
||||
|
||||
conn
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> put("/api/v1/statuses/#{activity.id}", %{
|
||||
"status" => "edited",
|
||||
"spoiler_text" => "lol"
|
||||
})
|
||||
|> json_response_and_validate_schema(:forbidden)
|
||||
end
|
||||
|
||||
test "it returns 404 if the user cannot see the post", %{conn: conn} do
|
||||
another_user = insert(:user)
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.post(another_user, %{
|
||||
status: "mew mew #abc",
|
||||
spoiler_text: "#def",
|
||||
visibility: "private"
|
||||
})
|
||||
|
||||
conn
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> put("/api/v1/statuses/#{activity.id}", %{
|
||||
"status" => "edited",
|
||||
"spoiler_text" => "lol"
|
||||
})
|
||||
|> json_response_and_validate_schema(:not_found)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -237,6 +237,32 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do
|
|||
test_notifications_rendering([notification], moderator_user, [expected])
|
||||
end
|
||||
|
||||
test "Edit notification" do
|
||||
user = insert(:user)
|
||||
repeat_user = insert(:user)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "mew"})
|
||||
{:ok, _} = CommonAPI.repeat(activity.id, repeat_user)
|
||||
{:ok, update} = CommonAPI.update(user, activity, %{status: "mew mew"})
|
||||
|
||||
user = Pleroma.User.get_by_ap_id(user.ap_id)
|
||||
activity = Pleroma.Activity.normalize(activity)
|
||||
update = Pleroma.Activity.normalize(update)
|
||||
|
||||
{:ok, [notification]} = Notification.create_notifications(update)
|
||||
|
||||
expected = %{
|
||||
id: to_string(notification.id),
|
||||
pleroma: %{is_seen: false, is_muted: false},
|
||||
type: "update",
|
||||
account: AccountView.render("show.json", %{user: user, for: repeat_user}),
|
||||
created_at: Utils.to_masto_date(notification.inserted_at),
|
||||
status: StatusView.render("show.json", %{activity: activity, for: repeat_user})
|
||||
}
|
||||
|
||||
test_notifications_rendering([notification], repeat_user, [expected])
|
||||
end
|
||||
|
||||
test "muted notification" do
|
||||
user = insert(:user)
|
||||
another_user = insert(:user)
|
||||
|
|
|
|||
|
|
@ -246,6 +246,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
|
|||
content: HTML.filter_tags(object_data["content"]),
|
||||
text: nil,
|
||||
created_at: created_at,
|
||||
edited_at: nil,
|
||||
reblogs_count: 0,
|
||||
replies_count: 0,
|
||||
favourites_count: 0,
|
||||
|
|
@ -708,4 +709,55 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
|
|||
status = StatusView.render("show.json", activity: visible, for: poster)
|
||||
assert status.pleroma.parent_visible
|
||||
end
|
||||
|
||||
test "it shows edited_at" do
|
||||
poster = insert(:user)
|
||||
|
||||
{:ok, post} = CommonAPI.post(poster, %{status: "hey"})
|
||||
|
||||
status = StatusView.render("show.json", activity: post)
|
||||
refute status.edited_at
|
||||
|
||||
{:ok, _} = CommonAPI.update(poster, post, %{status: "mew mew"})
|
||||
edited = Pleroma.Activity.normalize(post)
|
||||
|
||||
status = StatusView.render("show.json", activity: edited)
|
||||
assert status.edited_at
|
||||
end
|
||||
|
||||
test "with a source object" do
|
||||
note =
|
||||
insert(:note,
|
||||
data: %{"source" => %{"content" => "object source", "mediaType" => "text/markdown"}}
|
||||
)
|
||||
|
||||
activity = insert(:note_activity, note: note)
|
||||
|
||||
status = StatusView.render("show.json", activity: activity, with_source: true)
|
||||
assert status.text == "object source"
|
||||
end
|
||||
|
||||
describe "source.json" do
|
||||
test "with a source object, renders both source and content type" do
|
||||
note =
|
||||
insert(:note,
|
||||
data: %{"source" => %{"content" => "object source", "mediaType" => "text/markdown"}}
|
||||
)
|
||||
|
||||
activity = insert(:note_activity, note: note)
|
||||
|
||||
status = StatusView.render("source.json", activity: activity)
|
||||
assert status.text == "object source"
|
||||
assert status.content_type == "text/markdown"
|
||||
end
|
||||
|
||||
test "with a source string, renders source and put text/plain as the content type" do
|
||||
note = insert(:note, data: %{"source" => "string source"})
|
||||
activity = insert(:note_activity, note: note)
|
||||
|
||||
status = StatusView.render("source.json", activity: activity)
|
||||
assert status.text == "string source"
|
||||
assert status.content_type == "text/plain"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Metadata.UtilsTest do
|
||||
use Pleroma.DataCase, async: true
|
||||
use Pleroma.DataCase, async: false
|
||||
import Pleroma.Factory
|
||||
alias Pleroma.Web.Metadata.Utils
|
||||
|
||||
|
|
@ -22,6 +22,20 @@ defmodule Pleroma.Web.Metadata.UtilsTest do
|
|||
|
||||
assert Utils.scrub_html_and_truncate(note) == "Pleroma's really cool!"
|
||||
end
|
||||
|
||||
test "it does not return old content after editing" do
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, activity} = Pleroma.Web.CommonAPI.post(user, %{status: "mew mew #def"})
|
||||
|
||||
object = Pleroma.Object.normalize(activity)
|
||||
assert Utils.scrub_html_and_truncate(object) == "mew mew #def"
|
||||
|
||||
{:ok, update} = Pleroma.Web.CommonAPI.update(user, activity, %{status: "mew mew #abc"})
|
||||
update = Pleroma.Activity.normalize(update)
|
||||
object = Pleroma.Object.normalize(update)
|
||||
assert Utils.scrub_html_and_truncate(object) == "mew mew #abc"
|
||||
end
|
||||
end
|
||||
|
||||
describe "scrub_html_and_truncate/2" do
|
||||
|
|
|
|||
|
|
@ -442,6 +442,31 @@ defmodule Pleroma.Web.StreamerTest do
|
|||
"state" => "follow_accept"
|
||||
} = Jason.decode!(payload)
|
||||
end
|
||||
|
||||
test "it streams edits in the 'user' stream", %{user: user, token: oauth_token} do
|
||||
sender = insert(:user)
|
||||
{:ok, _, _, _} = CommonAPI.follow(user, sender)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(sender, %{status: "hey"})
|
||||
|
||||
Streamer.get_topic_and_add_socket("user", user, oauth_token)
|
||||
{:ok, edited} = CommonAPI.update(sender, activity, %{status: "mew mew"})
|
||||
create = Pleroma.Activity.get_create_by_object_ap_id_with_object(activity.object.data["id"])
|
||||
|
||||
assert_receive {:render_with_user, _, "status_update.json", ^create}
|
||||
refute Streamer.filtered_by_user?(user, edited)
|
||||
end
|
||||
|
||||
test "it streams own edits in the 'user' stream", %{user: user, token: oauth_token} do
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "hey"})
|
||||
|
||||
Streamer.get_topic_and_add_socket("user", user, oauth_token)
|
||||
{:ok, edited} = CommonAPI.update(user, activity, %{status: "mew mew"})
|
||||
create = Pleroma.Activity.get_create_by_object_ap_id_with_object(activity.object.data["id"])
|
||||
|
||||
assert_receive {:render_with_user, _, "status_update.json", ^create}
|
||||
refute Streamer.filtered_by_user?(user, edited)
|
||||
end
|
||||
end
|
||||
|
||||
describe "public streams" do
|
||||
|
|
@ -484,6 +509,54 @@ defmodule Pleroma.Web.StreamerTest do
|
|||
assert_receive {:text, event}
|
||||
assert %{"event" => "delete", "payload" => ^activity_id} = Jason.decode!(event)
|
||||
end
|
||||
|
||||
test "it streams edits in the 'public' stream" do
|
||||
sender = insert(:user)
|
||||
|
||||
Streamer.get_topic_and_add_socket("public", nil, nil)
|
||||
{:ok, activity} = CommonAPI.post(sender, %{status: "hey"})
|
||||
assert_receive {:text, _}
|
||||
|
||||
{:ok, edited} = CommonAPI.update(sender, activity, %{status: "mew mew"})
|
||||
|
||||
edited = Pleroma.Activity.normalize(edited)
|
||||
|
||||
%{id: activity_id} = Pleroma.Activity.get_create_by_object_ap_id(edited.object.data["id"])
|
||||
|
||||
assert_receive {:text, event}
|
||||
assert %{"event" => "status.update", "payload" => payload} = Jason.decode!(event)
|
||||
assert %{"id" => ^activity_id} = Jason.decode!(payload)
|
||||
refute Streamer.filtered_by_user?(sender, edited)
|
||||
end
|
||||
|
||||
test "it streams multiple edits in the 'public' stream correctly" do
|
||||
sender = insert(:user)
|
||||
|
||||
Streamer.get_topic_and_add_socket("public", nil, nil)
|
||||
{:ok, activity} = CommonAPI.post(sender, %{status: "hey"})
|
||||
assert_receive {:text, _}
|
||||
|
||||
{:ok, edited} = CommonAPI.update(sender, activity, %{status: "mew mew"})
|
||||
|
||||
edited = Pleroma.Activity.normalize(edited)
|
||||
|
||||
%{id: activity_id} = Pleroma.Activity.get_create_by_object_ap_id(edited.object.data["id"])
|
||||
|
||||
assert_receive {:text, event}
|
||||
assert %{"event" => "status.update", "payload" => payload} = Jason.decode!(event)
|
||||
assert %{"id" => ^activity_id} = Jason.decode!(payload)
|
||||
refute Streamer.filtered_by_user?(sender, edited)
|
||||
|
||||
{:ok, edited} = CommonAPI.update(sender, activity, %{status: "mew mew 2"})
|
||||
|
||||
edited = Pleroma.Activity.normalize(edited)
|
||||
|
||||
%{id: activity_id} = Pleroma.Activity.get_create_by_object_ap_id(edited.object.data["id"])
|
||||
assert_receive {:text, event}
|
||||
assert %{"event" => "status.update", "payload" => payload} = Jason.decode!(event)
|
||||
assert %{"id" => ^activity_id, "content" => "mew mew 2"} = Jason.decode!(payload)
|
||||
refute Streamer.filtered_by_user?(sender, edited)
|
||||
end
|
||||
end
|
||||
|
||||
describe "thread_containment/2" do
|
||||
|
|
|
|||
|
|
@ -111,6 +111,18 @@ defmodule Pleroma.Factory do
|
|||
}
|
||||
end
|
||||
|
||||
def attachment_factory(attrs \\ %{}) do
|
||||
user = attrs[:user] || insert(:user)
|
||||
|
||||
data =
|
||||
attachment_data(user.ap_id, nil)
|
||||
|> Map.put("id", Pleroma.Web.ActivityPub.Utils.generate_object_id())
|
||||
|
||||
%Pleroma.Object{
|
||||
data: merge_attributes(data, Map.get(attrs, :data, %{}))
|
||||
}
|
||||
end
|
||||
|
||||
def attachment_note_factory(attrs \\ %{}) do
|
||||
user = attrs[:user] || insert(:user)
|
||||
{length, attrs} = Map.pop(attrs, :length, 1)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue