Page MenuHomePhorge

No OneTemporary

Authored By
Unknown
Size
329 KB
Referenced Files
None
Subscribers
None
This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/test/pleroma/object_test.exs b/test/pleroma/object_test.exs
index 198d3b1cf..99caba336 100644
--- a/test/pleroma/object_test.exs
+++ b/test/pleroma/object_test.exs
@@ -1,402 +1,402 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.ObjectTest do
use Pleroma.DataCase
use Oban.Testing, repo: Pleroma.Repo
import ExUnit.CaptureLog
import Pleroma.Factory
import Tesla.Mock
alias Pleroma.Activity
alias Pleroma.Object
alias Pleroma.Repo
alias Pleroma.Tests.ObanHelpers
alias Pleroma.Web.CommonAPI
setup do
mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
:ok
end
test "returns an object by it's AP id" do
object = insert(:note)
found_object = Object.get_by_ap_id(object.data["id"])
assert object == found_object
end
describe "generic changeset" do
test "it ensures uniqueness of the id" do
object = insert(:note)
cs = Object.change(%Object{}, %{data: %{id: object.data["id"]}})
assert cs.valid?
{:error, _result} = Repo.insert(cs)
end
end
describe "deletion function" do
test "deletes an object" do
object = insert(:note)
found_object = Object.get_by_ap_id(object.data["id"])
assert object == found_object
Object.delete(found_object)
found_object = Object.get_by_ap_id(object.data["id"])
refute object == found_object
assert found_object.data["type"] == "Tombstone"
end
test "ensures cache is cleared for the object" do
object = insert(:note)
cached_object = Object.get_cached_by_ap_id(object.data["id"])
assert object == cached_object
Cachex.put(:web_resp_cache, URI.parse(object.data["id"]).path, "cofe")
Object.delete(cached_object)
{:ok, nil} = Cachex.get(:object_cache, "object:#{object.data["id"]}")
{:ok, nil} = Cachex.get(:web_resp_cache, URI.parse(object.data["id"]).path)
cached_object = Object.get_cached_by_ap_id(object.data["id"])
refute object == cached_object
assert cached_object.data["type"] == "Tombstone"
end
end
describe "delete attachments" do
setup do: clear_config([Pleroma.Upload])
setup do: clear_config([:instance, :cleanup_attachments])
test "Disabled via config" do
Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)
Pleroma.Config.put([:instance, :cleanup_attachments], false)
file = %Plug.Upload{
- content_type: "image/jpg",
+ content_type: "image/jpeg",
path: Path.absname("test/fixtures/image.jpg"),
filename: "an_image.jpg"
}
user = insert(:user)
{:ok, %Object{} = attachment} =
Pleroma.Web.ActivityPub.ActivityPub.upload(file, actor: user.ap_id)
%{data: %{"attachment" => [%{"url" => [%{"href" => href}]}]}} =
note = insert(:note, %{user: user, data: %{"attachment" => [attachment.data]}})
uploads_dir = Pleroma.Config.get!([Pleroma.Uploaders.Local, :uploads])
path = href |> Path.dirname() |> Path.basename()
assert {:ok, ["an_image.jpg"]} == File.ls("#{uploads_dir}/#{path}")
Object.delete(note)
ObanHelpers.perform(all_enqueued(worker: Pleroma.Workers.AttachmentsCleanupWorker))
assert Object.get_by_id(note.id).data["deleted"]
refute Object.get_by_id(attachment.id) == nil
assert {:ok, ["an_image.jpg"]} == File.ls("#{uploads_dir}/#{path}")
end
test "in subdirectories" do
Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)
Pleroma.Config.put([:instance, :cleanup_attachments], true)
file = %Plug.Upload{
- content_type: "image/jpg",
+ content_type: "image/jpeg",
path: Path.absname("test/fixtures/image.jpg"),
filename: "an_image.jpg"
}
user = insert(:user)
{:ok, %Object{} = attachment} =
Pleroma.Web.ActivityPub.ActivityPub.upload(file, actor: user.ap_id)
%{data: %{"attachment" => [%{"url" => [%{"href" => href}]}]}} =
note = insert(:note, %{user: user, data: %{"attachment" => [attachment.data]}})
uploads_dir = Pleroma.Config.get!([Pleroma.Uploaders.Local, :uploads])
path = href |> Path.dirname() |> Path.basename()
assert {:ok, ["an_image.jpg"]} == File.ls("#{uploads_dir}/#{path}")
Object.delete(note)
ObanHelpers.perform(all_enqueued(worker: Pleroma.Workers.AttachmentsCleanupWorker))
assert Object.get_by_id(note.id).data["deleted"]
assert Object.get_by_id(attachment.id) == nil
assert {:ok, []} == File.ls("#{uploads_dir}/#{path}")
end
test "with dedupe enabled" do
Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)
Pleroma.Config.put([Pleroma.Upload, :filters], [Pleroma.Upload.Filter.Dedupe])
Pleroma.Config.put([:instance, :cleanup_attachments], true)
uploads_dir = Pleroma.Config.get!([Pleroma.Uploaders.Local, :uploads])
File.mkdir_p!(uploads_dir)
file = %Plug.Upload{
- content_type: "image/jpg",
+ content_type: "image/jpeg",
path: Path.absname("test/fixtures/image.jpg"),
filename: "an_image.jpg"
}
user = insert(:user)
{:ok, %Object{} = attachment} =
Pleroma.Web.ActivityPub.ActivityPub.upload(file, actor: user.ap_id)
%{data: %{"attachment" => [%{"url" => [%{"href" => href}]}]}} =
note = insert(:note, %{user: user, data: %{"attachment" => [attachment.data]}})
filename = Path.basename(href)
assert {:ok, files} = File.ls(uploads_dir)
assert filename in files
Object.delete(note)
ObanHelpers.perform(all_enqueued(worker: Pleroma.Workers.AttachmentsCleanupWorker))
assert Object.get_by_id(note.id).data["deleted"]
assert Object.get_by_id(attachment.id) == nil
assert {:ok, files} = File.ls(uploads_dir)
refute filename in files
end
test "with objects that have legacy data.url attribute" do
Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)
Pleroma.Config.put([:instance, :cleanup_attachments], true)
file = %Plug.Upload{
- content_type: "image/jpg",
+ content_type: "image/jpeg",
path: Path.absname("test/fixtures/image.jpg"),
filename: "an_image.jpg"
}
user = insert(:user)
{:ok, %Object{} = attachment} =
Pleroma.Web.ActivityPub.ActivityPub.upload(file, actor: user.ap_id)
{:ok, %Object{}} = Object.create(%{url: "https://google.com", actor: user.ap_id})
%{data: %{"attachment" => [%{"url" => [%{"href" => href}]}]}} =
note = insert(:note, %{user: user, data: %{"attachment" => [attachment.data]}})
uploads_dir = Pleroma.Config.get!([Pleroma.Uploaders.Local, :uploads])
path = href |> Path.dirname() |> Path.basename()
assert {:ok, ["an_image.jpg"]} == File.ls("#{uploads_dir}/#{path}")
Object.delete(note)
ObanHelpers.perform(all_enqueued(worker: Pleroma.Workers.AttachmentsCleanupWorker))
assert Object.get_by_id(note.id).data["deleted"]
assert Object.get_by_id(attachment.id) == nil
assert {:ok, []} == File.ls("#{uploads_dir}/#{path}")
end
test "With custom base_url" do
Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)
Pleroma.Config.put([Pleroma.Upload, :base_url], "https://sub.domain.tld/dir/")
Pleroma.Config.put([:instance, :cleanup_attachments], true)
file = %Plug.Upload{
- content_type: "image/jpg",
+ content_type: "image/jpeg",
path: Path.absname("test/fixtures/image.jpg"),
filename: "an_image.jpg"
}
user = insert(:user)
{:ok, %Object{} = attachment} =
Pleroma.Web.ActivityPub.ActivityPub.upload(file, actor: user.ap_id)
%{data: %{"attachment" => [%{"url" => [%{"href" => href}]}]}} =
note = insert(:note, %{user: user, data: %{"attachment" => [attachment.data]}})
uploads_dir = Pleroma.Config.get!([Pleroma.Uploaders.Local, :uploads])
path = href |> Path.dirname() |> Path.basename()
assert {:ok, ["an_image.jpg"]} == File.ls("#{uploads_dir}/#{path}")
Object.delete(note)
ObanHelpers.perform(all_enqueued(worker: Pleroma.Workers.AttachmentsCleanupWorker))
assert Object.get_by_id(note.id).data["deleted"]
assert Object.get_by_id(attachment.id) == nil
assert {:ok, []} == File.ls("#{uploads_dir}/#{path}")
end
end
describe "normalizer" do
test "fetches unknown objects by default" do
%Object{} =
object = Object.normalize("http://mastodon.example.org/@admin/99541947525187367")
assert object.data["url"] == "http://mastodon.example.org/@admin/99541947525187367"
end
test "fetches unknown objects when fetch_remote is explicitly true" do
%Object{} =
object = Object.normalize("http://mastodon.example.org/@admin/99541947525187367", true)
assert object.data["url"] == "http://mastodon.example.org/@admin/99541947525187367"
end
test "does not fetch unknown objects when fetch_remote is false" do
assert is_nil(
Object.normalize("http://mastodon.example.org/@admin/99541947525187367", false)
)
end
end
describe "get_by_id_and_maybe_refetch" do
setup do
mock(fn
%{method: :get, url: "https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d"} ->
%Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/poll_original.json")}
env ->
apply(HttpRequestMock, :request, [env])
end)
mock_modified = fn resp ->
mock(fn
%{method: :get, url: "https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d"} ->
resp
env ->
apply(HttpRequestMock, :request, [env])
end)
end
on_exit(fn -> mock(fn env -> apply(HttpRequestMock, :request, [env]) end) end)
[mock_modified: mock_modified]
end
test "refetches if the time since the last refetch is greater than the interval", %{
mock_modified: mock_modified
} do
%Object{} =
object = Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d")
Object.set_cache(object)
assert Enum.at(object.data["oneOf"], 0)["replies"]["totalItems"] == 4
assert Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 0
mock_modified.(%Tesla.Env{
status: 200,
body: File.read!("test/fixtures/tesla_mock/poll_modified.json")
})
updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: -1)
object_in_cache = Object.get_cached_by_ap_id(object.data["id"])
assert updated_object == object_in_cache
assert Enum.at(updated_object.data["oneOf"], 0)["replies"]["totalItems"] == 8
assert Enum.at(updated_object.data["oneOf"], 1)["replies"]["totalItems"] == 3
end
test "returns the old object if refetch fails", %{mock_modified: mock_modified} do
%Object{} =
object = Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d")
Object.set_cache(object)
assert Enum.at(object.data["oneOf"], 0)["replies"]["totalItems"] == 4
assert Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 0
assert capture_log(fn ->
mock_modified.(%Tesla.Env{status: 404, body: ""})
updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: -1)
object_in_cache = Object.get_cached_by_ap_id(object.data["id"])
assert updated_object == object_in_cache
assert Enum.at(updated_object.data["oneOf"], 0)["replies"]["totalItems"] == 4
assert Enum.at(updated_object.data["oneOf"], 1)["replies"]["totalItems"] == 0
end) =~
"[error] Couldn't refresh https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d"
end
test "does not refetch if the time since the last refetch is greater than the interval", %{
mock_modified: mock_modified
} do
%Object{} =
object = Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d")
Object.set_cache(object)
assert Enum.at(object.data["oneOf"], 0)["replies"]["totalItems"] == 4
assert Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 0
mock_modified.(%Tesla.Env{
status: 200,
body: File.read!("test/fixtures/tesla_mock/poll_modified.json")
})
updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: 100)
object_in_cache = Object.get_cached_by_ap_id(object.data["id"])
assert updated_object == object_in_cache
assert Enum.at(updated_object.data["oneOf"], 0)["replies"]["totalItems"] == 4
assert Enum.at(updated_object.data["oneOf"], 1)["replies"]["totalItems"] == 0
end
test "preserves internal fields on refetch", %{mock_modified: mock_modified} do
%Object{} =
object = Object.normalize("https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d")
Object.set_cache(object)
assert Enum.at(object.data["oneOf"], 0)["replies"]["totalItems"] == 4
assert Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 0
user = insert(:user)
activity = Activity.get_create_by_object_ap_id(object.data["id"])
{:ok, activity} = CommonAPI.favorite(user, activity.id)
object = Object.get_by_ap_id(activity.data["object"])
assert object.data["like_count"] == 1
mock_modified.(%Tesla.Env{
status: 200,
body: File.read!("test/fixtures/tesla_mock/poll_modified.json")
})
updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: -1)
object_in_cache = Object.get_cached_by_ap_id(object.data["id"])
assert updated_object == object_in_cache
assert Enum.at(updated_object.data["oneOf"], 0)["replies"]["totalItems"] == 8
assert Enum.at(updated_object.data["oneOf"], 1)["replies"]["totalItems"] == 3
assert updated_object.data["like_count"] == 1
end
end
end
diff --git a/test/pleroma/upload/filter/anonymize_filename_test.exs b/test/pleroma/upload/filter/anonymize_filename_test.exs
index 19b915cc8..7ef01ce91 100644
--- a/test/pleroma/upload/filter/anonymize_filename_test.exs
+++ b/test/pleroma/upload/filter/anonymize_filename_test.exs
@@ -1,42 +1,42 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Upload.Filter.AnonymizeFilenameTest do
use Pleroma.DataCase
alias Pleroma.Config
alias Pleroma.Upload
setup do
File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg")
upload_file = %Upload{
name: "an… image.jpg",
- content_type: "image/jpg",
+ content_type: "image/jpeg",
path: Path.absname("test/fixtures/image_tmp.jpg")
}
%{upload_file: upload_file}
end
setup do: clear_config([Pleroma.Upload.Filter.AnonymizeFilename, :text])
test "it replaces filename on pre-defined text", %{upload_file: upload_file} do
Config.put([Upload.Filter.AnonymizeFilename, :text], "custom-file.png")
{:ok, :filtered, %Upload{name: name}} = Upload.Filter.AnonymizeFilename.filter(upload_file)
assert name == "custom-file.png"
end
test "it replaces filename on pre-defined text expression", %{upload_file: upload_file} do
Config.put([Upload.Filter.AnonymizeFilename, :text], "custom-file.{extension}")
{:ok, :filtered, %Upload{name: name}} = Upload.Filter.AnonymizeFilename.filter(upload_file)
assert name == "custom-file.jpg"
end
test "it replaces filename on random text", %{upload_file: upload_file} do
{:ok, :filtered, %Upload{name: name}} = Upload.Filter.AnonymizeFilename.filter(upload_file)
assert <<_::bytes-size(14)>> <> ".jpg" = name
refute name == "an… image.jpg"
end
end
diff --git a/test/pleroma/upload/filter/dedupe_test.exs b/test/pleroma/upload/filter/dedupe_test.exs
index 75c7198e1..92a3d7df3 100644
--- a/test/pleroma/upload/filter/dedupe_test.exs
+++ b/test/pleroma/upload/filter/dedupe_test.exs
@@ -1,32 +1,32 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Upload.Filter.DedupeTest do
use Pleroma.DataCase
alias Pleroma.Upload
alias Pleroma.Upload.Filter.Dedupe
@shasum "e30397b58d226d6583ab5b8b3c5defb0c682bda5c31ef07a9f57c1c4986e3781"
test "adds shasum" do
File.cp!(
"test/fixtures/image.jpg",
"test/fixtures/image_tmp.jpg"
)
upload = %Upload{
name: "an… image.jpg",
- content_type: "image/jpg",
+ content_type: "image/jpeg",
path: Path.absname("test/fixtures/image_tmp.jpg"),
tempfile: Path.absname("test/fixtures/image_tmp.jpg")
}
assert {
:ok,
:filtered,
%Pleroma.Upload{id: @shasum, path: @shasum <> ".jpg"}
} = Dedupe.filter(upload)
end
end
diff --git a/test/pleroma/upload/filter/exiftool_test.exs b/test/pleroma/upload/filter/exiftool_test.exs
index d4cd4ba11..6b978b64c 100644
--- a/test/pleroma/upload/filter/exiftool_test.exs
+++ b/test/pleroma/upload/filter/exiftool_test.exs
@@ -1,42 +1,42 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Upload.Filter.ExiftoolTest do
use Pleroma.DataCase
alias Pleroma.Upload.Filter
test "apply exiftool filter" do
assert Pleroma.Utils.command_available?("exiftool")
File.cp!(
"test/fixtures/DSCN0010.jpg",
"test/fixtures/DSCN0010_tmp.jpg"
)
upload = %Pleroma.Upload{
name: "image_with_GPS_data.jpg",
- content_type: "image/jpg",
+ content_type: "image/jpeg",
path: Path.absname("test/fixtures/DSCN0010.jpg"),
tempfile: Path.absname("test/fixtures/DSCN0010_tmp.jpg")
}
assert Filter.Exiftool.filter(upload) == {:ok, :filtered}
{exif_original, 0} = System.cmd("exiftool", ["test/fixtures/DSCN0010.jpg"])
{exif_filtered, 0} = System.cmd("exiftool", ["test/fixtures/DSCN0010_tmp.jpg"])
refute exif_original == exif_filtered
assert String.match?(exif_original, ~r/GPS/)
refute String.match?(exif_filtered, ~r/GPS/)
end
test "verify webp files are skipped" do
upload = %Pleroma.Upload{
name: "sample.webp",
content_type: "image/webp"
}
assert Filter.Exiftool.filter(upload) == {:ok, :noop}
end
end
diff --git a/test/pleroma/upload/filter/mogrifun_test.exs b/test/pleroma/upload/filter/mogrifun_test.exs
index dc1e9e78f..fc2f68276 100644
--- a/test/pleroma/upload/filter/mogrifun_test.exs
+++ b/test/pleroma/upload/filter/mogrifun_test.exs
@@ -1,44 +1,44 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Upload.Filter.MogrifunTest do
use Pleroma.DataCase
import Mock
alias Pleroma.Upload
alias Pleroma.Upload.Filter
test "apply mogrify filter" do
File.cp!(
"test/fixtures/image.jpg",
"test/fixtures/image_tmp.jpg"
)
upload = %Upload{
name: "an… image.jpg",
- content_type: "image/jpg",
+ content_type: "image/jpeg",
path: Path.absname("test/fixtures/image_tmp.jpg"),
tempfile: Path.absname("test/fixtures/image_tmp.jpg")
}
task =
Task.async(fn ->
assert_receive {:apply_filter, {}}, 4_000
end)
with_mocks([
{Mogrify, [],
[
open: fn _f -> %Mogrify.Image{} end,
custom: fn _m, _a -> send(task.pid, {:apply_filter, {}}) end,
custom: fn _m, _a, _o -> send(task.pid, {:apply_filter, {}}) end,
save: fn _f, _o -> :ok end
]}
]) do
assert Filter.Mogrifun.filter(upload) == {:ok, :filtered}
end
Task.await(task)
end
end
diff --git a/test/pleroma/upload/filter/mogrify_test.exs b/test/pleroma/upload/filter/mogrify_test.exs
index bf64b96b3..6dee02e8b 100644
--- a/test/pleroma/upload/filter/mogrify_test.exs
+++ b/test/pleroma/upload/filter/mogrify_test.exs
@@ -1,41 +1,41 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Upload.Filter.MogrifyTest do
use Pleroma.DataCase
import Mock
alias Pleroma.Upload.Filter
test "apply mogrify filter" do
clear_config(Filter.Mogrify, args: [{"tint", "40"}])
File.cp!(
"test/fixtures/image.jpg",
"test/fixtures/image_tmp.jpg"
)
upload = %Pleroma.Upload{
name: "an… image.jpg",
- content_type: "image/jpg",
+ content_type: "image/jpeg",
path: Path.absname("test/fixtures/image_tmp.jpg"),
tempfile: Path.absname("test/fixtures/image_tmp.jpg")
}
task =
Task.async(fn ->
assert_receive {:apply_filter, {_, "tint", "40"}}, 4_000
end)
with_mock Mogrify,
open: fn _f -> %Mogrify.Image{} end,
custom: fn _m, _a -> :ok end,
custom: fn m, a, o -> send(task.pid, {:apply_filter, {m, a, o}}) end,
save: fn _f, _o -> :ok end do
assert Filter.Mogrify.filter(upload) == {:ok, :filtered}
end
Task.await(task)
end
end
diff --git a/test/pleroma/upload/filter_test.exs b/test/pleroma/upload/filter_test.exs
index 352b66402..09394929c 100644
--- a/test/pleroma/upload/filter_test.exs
+++ b/test/pleroma/upload/filter_test.exs
@@ -1,33 +1,33 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Upload.FilterTest do
use Pleroma.DataCase
alias Pleroma.Config
alias Pleroma.Upload.Filter
setup do: clear_config([Pleroma.Upload.Filter.AnonymizeFilename, :text])
test "applies filters" do
Config.put([Pleroma.Upload.Filter.AnonymizeFilename, :text], "custom-file.png")
File.cp!(
"test/fixtures/image.jpg",
"test/fixtures/image_tmp.jpg"
)
upload = %Pleroma.Upload{
name: "an… image.jpg",
- content_type: "image/jpg",
+ content_type: "image/jpeg",
path: Path.absname("test/fixtures/image_tmp.jpg"),
tempfile: Path.absname("test/fixtures/image_tmp.jpg")
}
assert Filter.filter([], upload) == {:ok, upload}
assert {:ok, upload} = Filter.filter([Pleroma.Upload.Filter.AnonymizeFilename], upload)
assert upload.name == "custom-file.png"
end
end
diff --git a/test/pleroma/upload_test.exs b/test/pleroma/upload_test.exs
index 4280bfcac..f52d4dff6 100644
--- a/test/pleroma/upload_test.exs
+++ b/test/pleroma/upload_test.exs
@@ -1,255 +1,255 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.UploadTest do
use Pleroma.DataCase
import ExUnit.CaptureLog
alias Pleroma.Upload
alias Pleroma.Uploaders.Uploader
@upload_file %Plug.Upload{
content_type: "image/jpeg",
path: Path.absname("test/fixtures/image_tmp.jpg"),
filename: "image.jpg"
}
defmodule TestUploaderBase do
def put_file(%{path: path} = _upload, module_name) do
task_pid =
Task.async(fn ->
:timer.sleep(10)
{Uploader, path}
|> :global.whereis_name()
|> send({Uploader, self(), {:test}, %{}})
assert_receive {Uploader, {:test}}, 4_000
end)
Agent.start(fn -> task_pid end, name: module_name)
:wait_callback
end
end
describe "Tried storing a file when http callback response success result" do
defmodule TestUploaderSuccess do
def http_callback(conn, _params),
do: {:ok, conn, {:file, "post-process-file.jpg"}}
def put_file(upload), do: TestUploaderBase.put_file(upload, __MODULE__)
end
setup do: [uploader: TestUploaderSuccess]
setup [:ensure_local_uploader]
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"
}
]
}}
Task.await(Agent.get(TestUploaderSuccess, fn task_pid -> task_pid end))
end
end
describe "Tried storing a file when http callback response error" do
defmodule TestUploaderError do
def http_callback(conn, _params), do: {:error, conn, "Errors"}
def put_file(upload), do: TestUploaderBase.put_file(upload, __MODULE__)
end
setup do: [uploader: TestUploaderError]
setup [:ensure_local_uploader]
test "it returns error" do
File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg")
assert capture_log(fn ->
assert Upload.store(@upload_file) == {:error, "Errors"}
Task.await(Agent.get(TestUploaderError, fn task_pid -> task_pid end))
end) =~
"[error] Elixir.Pleroma.Upload store (using Pleroma.UploadTest.TestUploaderError) failed: \"Errors\""
end
end
describe "Tried storing a file when http callback doesn't response by timeout" do
defmodule(TestUploader, do: def(put_file(_upload), do: :wait_callback))
setup do: [uploader: TestUploader]
setup [:ensure_local_uploader]
test "it returns error" do
File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg")
assert capture_log(fn ->
assert Upload.store(@upload_file) == {:error, "Uploader callback timeout"}
end) =~
"[error] Elixir.Pleroma.Upload store (using Pleroma.UploadTest.TestUploader) failed: \"Uploader callback timeout\""
end
end
describe "Storing a file with the Local uploader" do
setup [:ensure_local_uploader]
test "does not allow descriptions longer than the post limit" do
clear_config([:instance, :description_limit], 2)
File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg")
file = %Plug.Upload{
- content_type: "image/jpg",
+ content_type: "image/jpeg",
path: Path.absname("test/fixtures/image_tmp.jpg"),
filename: "image.jpg"
}
{:error, :description_too_long} = Upload.store(file, description: "123")
end
test "returns a media url" do
File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg")
file = %Plug.Upload{
content_type: "image/jpeg",
path: Path.absname("test/fixtures/image_tmp.jpg"),
filename: "image.jpg"
}
{:ok, data} = Upload.store(file)
assert %{"url" => [%{"href" => url}]} = data
assert String.starts_with?(url, Pleroma.Web.base_url() <> "/media/")
end
test "copies the file to the configured folder with deduping" do
File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg")
file = %Plug.Upload{
content_type: "image/jpeg",
path: Path.absname("test/fixtures/image_tmp.jpg"),
filename: "an [image.jpg"
}
{:ok, data} = Upload.store(file, filters: [Pleroma.Upload.Filter.Dedupe])
assert List.first(data["url"])["href"] ==
Pleroma.Web.base_url() <>
"/media/e30397b58d226d6583ab5b8b3c5defb0c682bda5c31ef07a9f57c1c4986e3781.jpg"
end
test "copies the file to the configured folder without deduping" do
File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg")
file = %Plug.Upload{
content_type: "image/jpeg",
path: Path.absname("test/fixtures/image_tmp.jpg"),
filename: "an [image.jpg"
}
{:ok, data} = Upload.store(file)
assert data["name"] == "an [image.jpg"
end
test "fixes incorrect content type when base64 is given" do
params = %{
img: "data:image/png;base64,#{Base.encode64(File.read!("test/fixtures/image.jpg"))}"
}
{:ok, data} = Upload.store(params)
assert hd(data["url"])["mediaType"] == "image/jpeg"
end
test "adds extension when base64 is given" do
File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg")
params = %{
img: "data:image/png;base64,#{Base.encode64(File.read!("test/fixtures/image.jpg"))}"
}
{:ok, data} = Upload.store(params)
assert String.ends_with?(data["name"], ".jpg")
end
test "copies the file to the configured folder with anonymizing filename" do
File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg")
file = %Plug.Upload{
content_type: "image/jpeg",
path: Path.absname("test/fixtures/image_tmp.jpg"),
filename: "an [image.jpg"
}
{:ok, data} = Upload.store(file, filters: [Pleroma.Upload.Filter.AnonymizeFilename])
refute data["name"] == "an [image.jpg"
end
test "escapes invalid characters in url" do
File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg")
file = %Plug.Upload{
content_type: "image/jpeg",
path: Path.absname("test/fixtures/image_tmp.jpg"),
filename: "an… image.jpg"
}
{:ok, data} = Upload.store(file)
[attachment_url | _] = data["url"]
assert Path.basename(attachment_url["href"]) == "an%E2%80%A6%20image.jpg"
end
test "escapes reserved uri characters" do
File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg")
file = %Plug.Upload{
content_type: "image/jpeg",
path: Path.absname("test/fixtures/image_tmp.jpg"),
filename: ":?#[]@!$&\\'()*+,;=.jpg"
}
{:ok, data} = Upload.store(file)
[attachment_url | _] = data["url"]
assert Path.basename(attachment_url["href"]) ==
"%3A%3F%23%5B%5D%40%21%24%26%5C%27%28%29%2A%2B%2C%3B%3D.jpg"
end
end
describe "Setting a custom base_url for uploaded media" do
setup do: clear_config([Pleroma.Upload, :base_url], "https://cache.pleroma.social")
test "returns a media url with configured base_url" do
base_url = Pleroma.Config.get([Pleroma.Upload, :base_url])
File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg")
file = %Plug.Upload{
content_type: "image/jpeg",
path: Path.absname("test/fixtures/image_tmp.jpg"),
filename: "image.jpg"
}
{:ok, data} = Upload.store(file, base_url: base_url)
assert %{"url" => [%{"href" => url}]} = data
refute String.starts_with?(url, base_url <> "/media/")
end
end
end
diff --git a/test/pleroma/uploaders/local_test.exs b/test/pleroma/uploaders/local_test.exs
index 18122ff6c..1ce7be485 100644
--- a/test/pleroma/uploaders/local_test.exs
+++ b/test/pleroma/uploaders/local_test.exs
@@ -1,55 +1,55 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Uploaders.LocalTest do
use Pleroma.DataCase
alias Pleroma.Uploaders.Local
describe "get_file/1" do
test "it returns path to local folder for files" do
assert Local.get_file("") == {:ok, {:static_dir, "test/uploads"}}
end
end
describe "put_file/1" do
test "put file to local folder" do
File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg")
file_path = "local_upload/files/image.jpg"
file = %Pleroma.Upload{
name: "image.jpg",
- content_type: "image/jpg",
+ content_type: "image/jpeg",
path: file_path,
tempfile: Path.absname("test/fixtures/image_tmp.jpg")
}
assert Local.put_file(file) == :ok
assert Path.join([Local.upload_path(), file_path])
|> File.exists?()
end
end
describe "delete_file/1" do
test "deletes local file" do
File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg")
file_path = "local_upload/files/image.jpg"
file = %Pleroma.Upload{
name: "image.jpg",
- content_type: "image/jpg",
+ content_type: "image/jpeg",
path: file_path,
tempfile: Path.absname("test/fixtures/image_tmp.jpg")
}
:ok = Local.put_file(file)
local_path = Path.join([Local.upload_path(), file_path])
assert File.exists?(local_path)
Local.delete_file(file_path)
refute File.exists?(local_path)
end
end
end
diff --git a/test/pleroma/uploaders/s3_test.exs b/test/pleroma/uploaders/s3_test.exs
index d949c90a5..e7a013dd8 100644
--- a/test/pleroma/uploaders/s3_test.exs
+++ b/test/pleroma/uploaders/s3_test.exs
@@ -1,88 +1,88 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Uploaders.S3Test do
use Pleroma.DataCase
alias Pleroma.Config
alias Pleroma.Uploaders.S3
import Mock
import ExUnit.CaptureLog
setup do:
clear_config(Pleroma.Uploaders.S3,
bucket: "test_bucket",
public_endpoint: "https://s3.amazonaws.com"
)
describe "get_file/1" do
test "it returns path to local folder for files" do
assert S3.get_file("test_image.jpg") == {
:ok,
{:url, "https://s3.amazonaws.com/test_bucket/test_image.jpg"}
}
end
test "it returns path without bucket when truncated_namespace set to ''" do
Config.put([Pleroma.Uploaders.S3],
bucket: "test_bucket",
public_endpoint: "https://s3.amazonaws.com",
truncated_namespace: ""
)
assert S3.get_file("test_image.jpg") == {
:ok,
{:url, "https://s3.amazonaws.com/test_image.jpg"}
}
end
test "it returns path with bucket namespace when namespace is set" do
Config.put([Pleroma.Uploaders.S3],
bucket: "test_bucket",
public_endpoint: "https://s3.amazonaws.com",
bucket_namespace: "family"
)
assert S3.get_file("test_image.jpg") == {
:ok,
{:url, "https://s3.amazonaws.com/family:test_bucket/test_image.jpg"}
}
end
end
describe "put_file/1" do
setup do
file_upload = %Pleroma.Upload{
name: "image-tet.jpg",
- content_type: "image/jpg",
+ content_type: "image/jpeg",
path: "test_folder/image-tet.jpg",
tempfile: Path.absname("test/instance_static/add/shortcode.png")
}
[file_upload: file_upload]
end
test "save file", %{file_upload: file_upload} do
with_mock ExAws, request: fn _ -> {:ok, :ok} end do
assert S3.put_file(file_upload) == {:ok, {:file, "test_folder/image-tet.jpg"}}
end
end
test "returns error", %{file_upload: file_upload} do
with_mock ExAws, request: fn _ -> {:error, "S3 Upload failed"} end do
assert capture_log(fn ->
assert S3.put_file(file_upload) == {:error, "S3 Upload failed"}
end) =~ "Elixir.Pleroma.Uploaders.S3: {:error, \"S3 Upload failed\"}"
end
end
end
describe "delete_file/1" do
test_with_mock "deletes file", ExAws, request: fn _req -> {:ok, %{status_code: 204}} end do
assert :ok = S3.delete_file("image.jpg")
assert_called(ExAws.request(:_))
end
end
end
diff --git a/test/pleroma/web/activity_pub/activity_pub_test.exs b/test/pleroma/web/activity_pub/activity_pub_test.exs
index 804305a13..8f53d0599 100644
--- a/test/pleroma/web/activity_pub/activity_pub_test.exs
+++ b/test/pleroma/web/activity_pub/activity_pub_test.exs
@@ -1,2260 +1,2260 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
use Pleroma.DataCase
use Oban.Testing, repo: Pleroma.Repo
alias Pleroma.Activity
alias Pleroma.Builders.ActivityBuilder
alias Pleroma.Config
alias Pleroma.Notification
alias Pleroma.Object
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.AdminAPI.AccountView
alias Pleroma.Web.CommonAPI
import ExUnit.CaptureLog
import Mock
import Pleroma.Factory
import Tesla.Mock
setup do
mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
:ok
end
setup do: clear_config([:instance, :federating])
describe "streaming out participations" do
test "it streams them out" do
user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"})
{:ok, conversation} = Pleroma.Conversation.create_or_bump_for(activity)
participations =
conversation.participations
|> Repo.preload(:user)
with_mock Pleroma.Web.Streamer,
stream: fn _, _ -> nil end do
ActivityPub.stream_out_participations(conversation.participations)
assert called(Pleroma.Web.Streamer.stream("participation", participations))
end
end
test "streams them out on activity creation" do
user_one = insert(:user)
user_two = insert(:user)
with_mock Pleroma.Web.Streamer,
stream: fn _, _ -> nil end do
{:ok, activity} =
CommonAPI.post(user_one, %{
status: "@#{user_two.nickname}",
visibility: "direct"
})
conversation =
activity.data["context"]
|> Pleroma.Conversation.get_for_ap_id()
|> Repo.preload(participations: :user)
assert called(Pleroma.Web.Streamer.stream("participation", conversation.participations))
end
end
end
describe "fetching restricted by visibility" do
test "it restricts by the appropriate visibility" do
user = insert(:user)
{:ok, public_activity} = CommonAPI.post(user, %{status: ".", visibility: "public"})
{:ok, direct_activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"})
{:ok, unlisted_activity} = CommonAPI.post(user, %{status: ".", visibility: "unlisted"})
{:ok, private_activity} = CommonAPI.post(user, %{status: ".", visibility: "private"})
activities = ActivityPub.fetch_activities([], %{visibility: "direct", actor_id: user.ap_id})
assert activities == [direct_activity]
activities =
ActivityPub.fetch_activities([], %{visibility: "unlisted", actor_id: user.ap_id})
assert activities == [unlisted_activity]
activities =
ActivityPub.fetch_activities([], %{visibility: "private", actor_id: user.ap_id})
assert activities == [private_activity]
activities = ActivityPub.fetch_activities([], %{visibility: "public", actor_id: user.ap_id})
assert activities == [public_activity]
activities =
ActivityPub.fetch_activities([], %{
visibility: ~w[private public],
actor_id: user.ap_id
})
assert activities == [public_activity, private_activity]
end
end
describe "fetching excluded by visibility" do
test "it excludes by the appropriate visibility" do
user = insert(:user)
{:ok, public_activity} = CommonAPI.post(user, %{status: ".", visibility: "public"})
{:ok, direct_activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"})
{:ok, unlisted_activity} = CommonAPI.post(user, %{status: ".", visibility: "unlisted"})
{:ok, private_activity} = CommonAPI.post(user, %{status: ".", visibility: "private"})
activities =
ActivityPub.fetch_activities([], %{
exclude_visibilities: "direct",
actor_id: user.ap_id
})
assert public_activity in activities
assert unlisted_activity in activities
assert private_activity in activities
refute direct_activity in activities
activities =
ActivityPub.fetch_activities([], %{
exclude_visibilities: "unlisted",
actor_id: user.ap_id
})
assert public_activity in activities
refute unlisted_activity in activities
assert private_activity in activities
assert direct_activity in activities
activities =
ActivityPub.fetch_activities([], %{
exclude_visibilities: "private",
actor_id: user.ap_id
})
assert public_activity in activities
assert unlisted_activity in activities
refute private_activity in activities
assert direct_activity in activities
activities =
ActivityPub.fetch_activities([], %{
exclude_visibilities: "public",
actor_id: user.ap_id
})
refute public_activity in activities
assert unlisted_activity in activities
assert private_activity in activities
assert direct_activity in activities
end
end
describe "building a user from his ap id" do
test "it returns a user" do
user_id = "http://mastodon.example.org/users/admin"
{:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
assert user.ap_id == user_id
assert user.nickname == "admin@mastodon.example.org"
assert user.ap_enabled
assert user.follower_address == "http://mastodon.example.org/users/admin/followers"
end
test "it returns a user that is invisible" do
user_id = "http://mastodon.example.org/users/relay"
{:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
assert User.invisible?(user)
end
test "it returns a user that accepts chat messages" do
user_id = "http://mastodon.example.org/users/admin"
{:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
assert user.accepts_chat_messages
end
end
test "it fetches the appropriate tag-restricted posts" do
user = insert(:user)
{:ok, status_one} = CommonAPI.post(user, %{status: ". #test"})
{:ok, status_two} = CommonAPI.post(user, %{status: ". #essais"})
{:ok, status_three} = CommonAPI.post(user, %{status: ". #test #reject"})
fetch_one = ActivityPub.fetch_activities([], %{type: "Create", tag: "test"})
fetch_two = ActivityPub.fetch_activities([], %{type: "Create", tag: ["test", "essais"]})
fetch_three =
ActivityPub.fetch_activities([], %{
type: "Create",
tag: ["test", "essais"],
tag_reject: ["reject"]
})
fetch_four =
ActivityPub.fetch_activities([], %{
type: "Create",
tag: ["test"],
tag_all: ["test", "reject"]
})
assert fetch_one == [status_one, status_three]
assert fetch_two == [status_one, status_two, status_three]
assert fetch_three == [status_one, status_two]
assert fetch_four == [status_three]
end
describe "insertion" do
test "drops activities beyond a certain limit" do
limit = Config.get([:instance, :remote_limit])
random_text =
:crypto.strong_rand_bytes(limit + 1)
|> Base.encode64()
|> binary_part(0, limit + 1)
data = %{
"ok" => true,
"object" => %{
"content" => random_text
}
}
assert {:error, :remote_limit} = ActivityPub.insert(data)
end
test "doesn't drop activities with content being null" do
user = insert(:user)
data = %{
"actor" => user.ap_id,
"to" => [],
"object" => %{
"actor" => user.ap_id,
"to" => [],
"type" => "Note",
"content" => nil
}
}
assert {:ok, _} = ActivityPub.insert(data)
end
test "returns the activity if one with the same id is already in" do
activity = insert(:note_activity)
{:ok, new_activity} = ActivityPub.insert(activity.data)
assert activity.id == new_activity.id
end
test "inserts a given map into the activity database, giving it an id if it has none." do
user = insert(:user)
data = %{
"actor" => user.ap_id,
"to" => [],
"object" => %{
"actor" => user.ap_id,
"to" => [],
"type" => "Note",
"content" => "hey"
}
}
{:ok, %Activity{} = activity} = ActivityPub.insert(data)
assert activity.data["ok"] == data["ok"]
assert is_binary(activity.data["id"])
given_id = "bla"
data = %{
"id" => given_id,
"actor" => user.ap_id,
"to" => [],
"context" => "blabla",
"object" => %{
"actor" => user.ap_id,
"to" => [],
"type" => "Note",
"content" => "hey"
}
}
{:ok, %Activity{} = activity} = ActivityPub.insert(data)
assert activity.data["ok"] == data["ok"]
assert activity.data["id"] == given_id
assert activity.data["context"] == "blabla"
assert activity.data["context_id"]
end
test "adds a context when none is there" do
user = insert(:user)
data = %{
"actor" => user.ap_id,
"to" => [],
"object" => %{
"actor" => user.ap_id,
"to" => [],
"type" => "Note",
"content" => "hey"
}
}
{:ok, %Activity{} = activity} = ActivityPub.insert(data)
object = Pleroma.Object.normalize(activity)
assert is_binary(activity.data["context"])
assert is_binary(object.data["context"])
assert activity.data["context_id"]
assert object.data["context_id"]
end
test "adds an id to a given object if it lacks one and is a note and inserts it to the object database" do
user = insert(:user)
data = %{
"actor" => user.ap_id,
"to" => [],
"object" => %{
"actor" => user.ap_id,
"to" => [],
"type" => "Note",
"content" => "hey"
}
}
{:ok, %Activity{} = activity} = ActivityPub.insert(data)
assert object = Object.normalize(activity)
assert is_binary(object.data["id"])
end
end
describe "listen activities" do
test "does not increase user note count" do
user = insert(:user)
{:ok, activity} =
ActivityPub.listen(%{
to: ["https://www.w3.org/ns/activitystreams#Public"],
actor: user,
context: "",
object: %{
"actor" => user.ap_id,
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
"artist" => "lain",
"title" => "lain radio episode 1",
"length" => 180_000,
"type" => "Audio"
}
})
assert activity.actor == user.ap_id
user = User.get_cached_by_id(user.id)
assert user.note_count == 0
end
test "can be fetched into a timeline" do
_listen_activity_1 = insert(:listen)
_listen_activity_2 = insert(:listen)
_listen_activity_3 = insert(:listen)
timeline = ActivityPub.fetch_activities([], %{type: ["Listen"]})
assert length(timeline) == 3
end
end
describe "create activities" do
setup do
[user: insert(:user)]
end
test "it reverts create", %{user: user} do
with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
assert {:error, :reverted} =
ActivityPub.create(%{
to: ["user1", "user2"],
actor: user,
context: "",
object: %{
"to" => ["user1", "user2"],
"type" => "Note",
"content" => "testing"
}
})
end
assert Repo.aggregate(Activity, :count, :id) == 0
assert Repo.aggregate(Object, :count, :id) == 0
end
test "creates activity if expiration is not configured and expires_at is not passed", %{
user: user
} do
clear_config([Pleroma.Workers.PurgeExpiredActivity, :enabled], false)
assert {:ok, _} =
ActivityPub.create(%{
to: ["user1", "user2"],
actor: user,
context: "",
object: %{
"to" => ["user1", "user2"],
"type" => "Note",
"content" => "testing"
}
})
end
test "rejects activity if expires_at present but expiration is not configured", %{user: user} do
clear_config([Pleroma.Workers.PurgeExpiredActivity, :enabled], false)
assert {:error, :expired_activities_disabled} =
ActivityPub.create(%{
to: ["user1", "user2"],
actor: user,
context: "",
object: %{
"to" => ["user1", "user2"],
"type" => "Note",
"content" => "testing"
},
additional: %{
"expires_at" => DateTime.utc_now()
}
})
assert Repo.aggregate(Activity, :count, :id) == 0
assert Repo.aggregate(Object, :count, :id) == 0
end
test "removes doubled 'to' recipients", %{user: user} do
{:ok, activity} =
ActivityPub.create(%{
to: ["user1", "user1", "user2"],
actor: user,
context: "",
object: %{
"to" => ["user1", "user1", "user2"],
"type" => "Note",
"content" => "testing"
}
})
assert activity.data["to"] == ["user1", "user2"]
assert activity.actor == user.ap_id
assert activity.recipients == ["user1", "user2", user.ap_id]
end
test "increases user note count only for public activities", %{user: user} do
{:ok, _} =
CommonAPI.post(User.get_cached_by_id(user.id), %{
status: "1",
visibility: "public"
})
{:ok, _} =
CommonAPI.post(User.get_cached_by_id(user.id), %{
status: "2",
visibility: "unlisted"
})
{:ok, _} =
CommonAPI.post(User.get_cached_by_id(user.id), %{
status: "2",
visibility: "private"
})
{:ok, _} =
CommonAPI.post(User.get_cached_by_id(user.id), %{
status: "3",
visibility: "direct"
})
user = User.get_cached_by_id(user.id)
assert user.note_count == 2
end
test "increases replies count", %{user: user} do
user2 = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{status: "1", visibility: "public"})
ap_id = activity.data["id"]
reply_data = %{status: "1", in_reply_to_status_id: activity.id}
# public
{:ok, _} = CommonAPI.post(user2, Map.put(reply_data, :visibility, "public"))
assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
assert object.data["repliesCount"] == 1
# unlisted
{:ok, _} = CommonAPI.post(user2, Map.put(reply_data, :visibility, "unlisted"))
assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
assert object.data["repliesCount"] == 2
# private
{:ok, _} = CommonAPI.post(user2, Map.put(reply_data, :visibility, "private"))
assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
assert object.data["repliesCount"] == 2
# direct
{:ok, _} = CommonAPI.post(user2, Map.put(reply_data, :visibility, "direct"))
assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
assert object.data["repliesCount"] == 2
end
end
describe "fetch activities for recipients" do
test "retrieve the activities for certain recipients" do
{:ok, activity_one} = ActivityBuilder.insert(%{"to" => ["someone"]})
{:ok, activity_two} = ActivityBuilder.insert(%{"to" => ["someone_else"]})
{:ok, _activity_three} = ActivityBuilder.insert(%{"to" => ["noone"]})
activities = ActivityPub.fetch_activities(["someone", "someone_else"])
assert length(activities) == 2
assert activities == [activity_one, activity_two]
end
end
describe "fetch activities in context" do
test "retrieves activities that have a given context" do
{:ok, activity} = ActivityBuilder.insert(%{"type" => "Create", "context" => "2hu"})
{:ok, activity_two} = ActivityBuilder.insert(%{"type" => "Create", "context" => "2hu"})
{:ok, _activity_three} = ActivityBuilder.insert(%{"type" => "Create", "context" => "3hu"})
{:ok, _activity_four} = ActivityBuilder.insert(%{"type" => "Announce", "context" => "2hu"})
activity_five = insert(:note_activity)
user = insert(:user)
{:ok, _user_relationship} = User.block(user, %{ap_id: activity_five.data["actor"]})
activities = ActivityPub.fetch_activities_for_context("2hu", %{blocking_user: user})
assert activities == [activity_two, activity]
end
test "doesn't return activities with filtered words" do
user = insert(:user)
user_two = insert(:user)
insert(:filter, user: user, phrase: "test", hide: true)
{:ok, %{id: id1, data: %{"context" => context}}} = CommonAPI.post(user, %{status: "1"})
{:ok, %{id: id2}} = CommonAPI.post(user_two, %{status: "2", in_reply_to_status_id: id1})
{:ok, %{id: id3} = user_activity} =
CommonAPI.post(user, %{status: "3 test?", in_reply_to_status_id: id2})
{:ok, %{id: id4} = filtered_activity} =
CommonAPI.post(user_two, %{status: "4 test!", in_reply_to_status_id: id3})
{:ok, _} = CommonAPI.post(user, %{status: "5", in_reply_to_status_id: id4})
activities =
context
|> ActivityPub.fetch_activities_for_context(%{user: user})
|> Enum.map(& &1.id)
assert length(activities) == 4
assert user_activity.id in activities
refute filtered_activity.id in activities
end
end
test "doesn't return blocked activities" do
activity_one = insert(:note_activity)
activity_two = insert(:note_activity)
activity_three = insert(:note_activity)
user = insert(:user)
booster = insert(:user)
{:ok, _user_relationship} = User.block(user, %{ap_id: activity_one.data["actor"]})
activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
assert Enum.member?(activities, activity_two)
assert Enum.member?(activities, activity_three)
refute Enum.member?(activities, activity_one)
{:ok, _user_block} = User.unblock(user, %{ap_id: activity_one.data["actor"]})
activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
assert Enum.member?(activities, activity_two)
assert Enum.member?(activities, activity_three)
assert Enum.member?(activities, activity_one)
{:ok, _user_relationship} = User.block(user, %{ap_id: activity_three.data["actor"]})
{:ok, %{data: %{"object" => id}}} = CommonAPI.repeat(activity_three.id, booster)
%Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
activity_three = Activity.get_by_id(activity_three.id)
activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
assert Enum.member?(activities, activity_two)
refute Enum.member?(activities, activity_three)
refute Enum.member?(activities, boost_activity)
assert Enum.member?(activities, activity_one)
activities = ActivityPub.fetch_activities([], %{blocking_user: nil, skip_preload: true})
assert Enum.member?(activities, activity_two)
assert Enum.member?(activities, activity_three)
assert Enum.member?(activities, boost_activity)
assert Enum.member?(activities, activity_one)
end
test "doesn't return transitive interactions concerning blocked users" do
blocker = insert(:user)
blockee = insert(:user)
friend = insert(:user)
{:ok, _user_relationship} = User.block(blocker, blockee)
{:ok, activity_one} = CommonAPI.post(friend, %{status: "hey!"})
{:ok, activity_two} = CommonAPI.post(friend, %{status: "hey! @#{blockee.nickname}"})
{:ok, activity_three} = CommonAPI.post(blockee, %{status: "hey! @#{friend.nickname}"})
{:ok, activity_four} = CommonAPI.post(blockee, %{status: "hey! @#{blocker.nickname}"})
activities = ActivityPub.fetch_activities([], %{blocking_user: blocker})
assert Enum.member?(activities, activity_one)
refute Enum.member?(activities, activity_two)
refute Enum.member?(activities, activity_three)
refute Enum.member?(activities, activity_four)
end
test "doesn't return announce activities with blocked users in 'to'" do
blocker = insert(:user)
blockee = insert(:user)
friend = insert(:user)
{:ok, _user_relationship} = User.block(blocker, blockee)
{:ok, activity_one} = CommonAPI.post(friend, %{status: "hey!"})
{:ok, activity_two} = CommonAPI.post(blockee, %{status: "hey! @#{friend.nickname}"})
{:ok, activity_three} = CommonAPI.repeat(activity_two.id, friend)
activities =
ActivityPub.fetch_activities([], %{blocking_user: blocker})
|> Enum.map(fn act -> act.id end)
assert Enum.member?(activities, activity_one.id)
refute Enum.member?(activities, activity_two.id)
refute Enum.member?(activities, activity_three.id)
end
test "doesn't return announce activities with blocked users in 'cc'" do
blocker = insert(:user)
blockee = insert(:user)
friend = insert(:user)
{:ok, _user_relationship} = User.block(blocker, blockee)
{:ok, activity_one} = CommonAPI.post(friend, %{status: "hey!"})
{:ok, activity_two} = CommonAPI.post(blockee, %{status: "hey! @#{friend.nickname}"})
assert object = Pleroma.Object.normalize(activity_two)
data = %{
"actor" => friend.ap_id,
"object" => object.data["id"],
"context" => object.data["context"],
"type" => "Announce",
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
"cc" => [blockee.ap_id]
}
assert {:ok, activity_three} = ActivityPub.insert(data)
activities =
ActivityPub.fetch_activities([], %{blocking_user: blocker})
|> Enum.map(fn act -> act.id end)
assert Enum.member?(activities, activity_one.id)
refute Enum.member?(activities, activity_two.id)
refute Enum.member?(activities, activity_three.id)
end
test "doesn't return activities from blocked domains" do
domain = "dogwhistle.zone"
domain_user = insert(:user, %{ap_id: "https://#{domain}/@pundit"})
note = insert(:note, %{data: %{"actor" => domain_user.ap_id}})
activity = insert(:note_activity, %{note: note})
user = insert(:user)
{:ok, user} = User.block_domain(user, domain)
activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
refute activity in activities
followed_user = insert(:user)
CommonAPI.follow(user, followed_user)
{:ok, repeat_activity} = CommonAPI.repeat(activity.id, followed_user)
activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true})
refute repeat_activity in activities
end
test "does return activities from followed users on blocked domains" do
domain = "meanies.social"
domain_user = insert(:user, %{ap_id: "https://#{domain}/@pundit"})
blocker = insert(:user)
{:ok, blocker} = User.follow(blocker, domain_user)
{:ok, blocker} = User.block_domain(blocker, domain)
assert User.following?(blocker, domain_user)
assert User.blocks_domain?(blocker, domain_user)
refute User.blocks?(blocker, domain_user)
note = insert(:note, %{data: %{"actor" => domain_user.ap_id}})
activity = insert(:note_activity, %{note: note})
activities = ActivityPub.fetch_activities([], %{blocking_user: blocker, skip_preload: true})
assert activity in activities
# And check that if the guy we DO follow boosts someone else from their domain,
# that should be hidden
another_user = insert(:user, %{ap_id: "https://#{domain}/@meanie2"})
bad_note = insert(:note, %{data: %{"actor" => another_user.ap_id}})
bad_activity = insert(:note_activity, %{note: bad_note})
{:ok, repeat_activity} = CommonAPI.repeat(bad_activity.id, domain_user)
activities = ActivityPub.fetch_activities([], %{blocking_user: blocker, skip_preload: true})
refute repeat_activity in activities
end
test "doesn't return muted activities" do
activity_one = insert(:note_activity)
activity_two = insert(:note_activity)
activity_three = insert(:note_activity)
user = insert(:user)
booster = insert(:user)
activity_one_actor = User.get_by_ap_id(activity_one.data["actor"])
{:ok, _user_relationships} = User.mute(user, activity_one_actor)
activities = ActivityPub.fetch_activities([], %{muting_user: user, skip_preload: true})
assert Enum.member?(activities, activity_two)
assert Enum.member?(activities, activity_three)
refute Enum.member?(activities, activity_one)
# Calling with 'with_muted' will deliver muted activities, too.
activities =
ActivityPub.fetch_activities([], %{
muting_user: user,
with_muted: true,
skip_preload: true
})
assert Enum.member?(activities, activity_two)
assert Enum.member?(activities, activity_three)
assert Enum.member?(activities, activity_one)
{:ok, _user_mute} = User.unmute(user, activity_one_actor)
activities = ActivityPub.fetch_activities([], %{muting_user: user, skip_preload: true})
assert Enum.member?(activities, activity_two)
assert Enum.member?(activities, activity_three)
assert Enum.member?(activities, activity_one)
activity_three_actor = User.get_by_ap_id(activity_three.data["actor"])
{:ok, _user_relationships} = User.mute(user, activity_three_actor)
{:ok, %{data: %{"object" => id}}} = CommonAPI.repeat(activity_three.id, booster)
%Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
activity_three = Activity.get_by_id(activity_three.id)
activities = ActivityPub.fetch_activities([], %{muting_user: user, skip_preload: true})
assert Enum.member?(activities, activity_two)
refute Enum.member?(activities, activity_three)
refute Enum.member?(activities, boost_activity)
assert Enum.member?(activities, activity_one)
activities = ActivityPub.fetch_activities([], %{muting_user: nil, skip_preload: true})
assert Enum.member?(activities, activity_two)
assert Enum.member?(activities, activity_three)
assert Enum.member?(activities, boost_activity)
assert Enum.member?(activities, activity_one)
end
test "doesn't return thread muted activities" do
user = insert(:user)
_activity_one = insert(:note_activity)
note_two = insert(:note, data: %{"context" => "suya.."})
activity_two = insert(:note_activity, note: note_two)
{:ok, _activity_two} = CommonAPI.add_mute(user, activity_two)
assert [_activity_one] = ActivityPub.fetch_activities([], %{muting_user: user})
end
test "returns thread muted activities when with_muted is set" do
user = insert(:user)
_activity_one = insert(:note_activity)
note_two = insert(:note, data: %{"context" => "suya.."})
activity_two = insert(:note_activity, note: note_two)
{:ok, _activity_two} = CommonAPI.add_mute(user, activity_two)
assert [_activity_two, _activity_one] =
ActivityPub.fetch_activities([], %{muting_user: user, with_muted: true})
end
test "does include announces on request" do
activity_three = insert(:note_activity)
user = insert(:user)
booster = insert(:user)
{:ok, user} = User.follow(user, booster)
{:ok, announce} = CommonAPI.repeat(activity_three.id, booster)
[announce_activity] = ActivityPub.fetch_activities([user.ap_id | User.following(user)])
assert announce_activity.id == announce.id
end
test "excludes reblogs on request" do
user = insert(:user)
{:ok, expected_activity} = ActivityBuilder.insert(%{"type" => "Create"}, %{:user => user})
{:ok, _} = ActivityBuilder.insert(%{"type" => "Announce"}, %{:user => user})
[activity] = ActivityPub.fetch_user_activities(user, nil, %{exclude_reblogs: true})
assert activity == expected_activity
end
describe "irreversible filters" do
setup do
user = insert(:user)
user_two = insert(:user)
insert(:filter, user: user_two, phrase: "cofe", hide: true)
insert(:filter, user: user_two, phrase: "ok boomer", hide: true)
insert(:filter, user: user_two, phrase: "test", hide: false)
params = %{
type: ["Create", "Announce"],
user: user_two
}
{:ok, %{user: user, user_two: user_two, params: params}}
end
test "it returns statuses if they don't contain exact filter words", %{
user: user,
params: params
} do
{:ok, _} = CommonAPI.post(user, %{status: "hey"})
{:ok, _} = CommonAPI.post(user, %{status: "got cofefe?"})
{:ok, _} = CommonAPI.post(user, %{status: "I am not a boomer"})
{:ok, _} = CommonAPI.post(user, %{status: "ok boomers"})
{:ok, _} = CommonAPI.post(user, %{status: "ccofee is not a word"})
{:ok, _} = CommonAPI.post(user, %{status: "this is a test"})
activities = ActivityPub.fetch_activities([], params)
assert Enum.count(activities) == 6
end
test "it does not filter user's own statuses", %{user_two: user_two, params: params} do
{:ok, _} = CommonAPI.post(user_two, %{status: "Give me some cofe!"})
{:ok, _} = CommonAPI.post(user_two, %{status: "ok boomer"})
activities = ActivityPub.fetch_activities([], params)
assert Enum.count(activities) == 2
end
test "it excludes statuses with filter words", %{user: user, params: params} do
{:ok, _} = CommonAPI.post(user, %{status: "Give me some cofe!"})
{:ok, _} = CommonAPI.post(user, %{status: "ok boomer"})
{:ok, _} = CommonAPI.post(user, %{status: "is it a cOfE?"})
{:ok, _} = CommonAPI.post(user, %{status: "cofe is all I need"})
{:ok, _} = CommonAPI.post(user, %{status: "— ok BOOMER\n"})
activities = ActivityPub.fetch_activities([], params)
assert Enum.empty?(activities)
end
test "it returns all statuses if user does not have any filters" do
another_user = insert(:user)
{:ok, _} = CommonAPI.post(another_user, %{status: "got cofe?"})
{:ok, _} = CommonAPI.post(another_user, %{status: "test!"})
activities =
ActivityPub.fetch_activities([], %{
type: ["Create", "Announce"],
user: another_user
})
assert Enum.count(activities) == 2
end
end
describe "public fetch activities" do
test "doesn't retrieve unlisted activities" do
user = insert(:user)
{:ok, _unlisted_activity} = CommonAPI.post(user, %{status: "yeah", visibility: "unlisted"})
{:ok, listed_activity} = CommonAPI.post(user, %{status: "yeah"})
[activity] = ActivityPub.fetch_public_activities()
assert activity == listed_activity
end
test "retrieves public activities" do
_activities = ActivityPub.fetch_public_activities()
%{public: public} = ActivityBuilder.public_and_non_public()
activities = ActivityPub.fetch_public_activities()
assert length(activities) == 1
assert Enum.at(activities, 0) == public
end
test "retrieves a maximum of 20 activities" do
ActivityBuilder.insert_list(10)
expected_activities = ActivityBuilder.insert_list(20)
activities = ActivityPub.fetch_public_activities()
assert collect_ids(activities) == collect_ids(expected_activities)
assert length(activities) == 20
end
test "retrieves ids starting from a since_id" do
activities = ActivityBuilder.insert_list(30)
expected_activities = ActivityBuilder.insert_list(10)
since_id = List.last(activities).id
activities = ActivityPub.fetch_public_activities(%{since_id: since_id})
assert collect_ids(activities) == collect_ids(expected_activities)
assert length(activities) == 10
end
test "retrieves ids up to max_id" do
ActivityBuilder.insert_list(10)
expected_activities = ActivityBuilder.insert_list(20)
%{id: max_id} =
10
|> ActivityBuilder.insert_list()
|> List.first()
activities = ActivityPub.fetch_public_activities(%{max_id: max_id})
assert length(activities) == 20
assert collect_ids(activities) == collect_ids(expected_activities)
end
test "paginates via offset/limit" do
_first_part_activities = ActivityBuilder.insert_list(10)
second_part_activities = ActivityBuilder.insert_list(10)
later_activities = ActivityBuilder.insert_list(10)
activities = ActivityPub.fetch_public_activities(%{page: "2", page_size: "20"}, :offset)
assert length(activities) == 20
assert collect_ids(activities) ==
collect_ids(second_part_activities) ++ collect_ids(later_activities)
end
test "doesn't return reblogs for users for whom reblogs have been muted" do
activity = insert(:note_activity)
user = insert(:user)
booster = insert(:user)
{:ok, _reblog_mute} = CommonAPI.hide_reblogs(user, booster)
{:ok, activity} = CommonAPI.repeat(activity.id, booster)
activities = ActivityPub.fetch_activities([], %{muting_user: user})
refute Enum.any?(activities, fn %{id: id} -> id == activity.id end)
end
test "returns reblogs for users for whom reblogs have not been muted" do
activity = insert(:note_activity)
user = insert(:user)
booster = insert(:user)
{:ok, _reblog_mute} = CommonAPI.hide_reblogs(user, booster)
{:ok, _reblog_mute} = CommonAPI.show_reblogs(user, booster)
{:ok, activity} = CommonAPI.repeat(activity.id, booster)
activities = ActivityPub.fetch_activities([], %{muting_user: user})
assert Enum.any?(activities, fn %{id: id} -> id == activity.id end)
end
end
describe "uploading files" do
setup do
test_file = %Plug.Upload{
- content_type: "image/jpg",
+ content_type: "image/jpeg",
path: Path.absname("test/fixtures/image.jpg"),
filename: "an_image.jpg"
}
%{test_file: test_file}
end
test "sets a description if given", %{test_file: file} do
{:ok, %Object{} = object} = ActivityPub.upload(file, description: "a cool file")
assert object.data["name"] == "a cool file"
end
test "it sets the default description depending on the configuration", %{test_file: file} do
clear_config([Pleroma.Upload, :default_description])
Pleroma.Config.put([Pleroma.Upload, :default_description], nil)
{:ok, %Object{} = object} = ActivityPub.upload(file)
assert object.data["name"] == ""
Pleroma.Config.put([Pleroma.Upload, :default_description], :filename)
{:ok, %Object{} = object} = ActivityPub.upload(file)
assert object.data["name"] == "an_image.jpg"
Pleroma.Config.put([Pleroma.Upload, :default_description], "unnamed attachment")
{:ok, %Object{} = object} = ActivityPub.upload(file)
assert object.data["name"] == "unnamed attachment"
end
test "copies the file to the configured folder", %{test_file: file} do
clear_config([Pleroma.Upload, :default_description], :filename)
{:ok, %Object{} = object} = ActivityPub.upload(file)
assert object.data["name"] == "an_image.jpg"
end
test "works with base64 encoded images" do
file = %{
img: data_uri()
}
{:ok, %Object{}} = ActivityPub.upload(file)
end
end
describe "fetch the latest Follow" do
test "fetches the latest Follow activity" do
%Activity{data: %{"type" => "Follow"}} = activity = insert(:follow_activity)
follower = Repo.get_by(User, ap_id: activity.data["actor"])
followed = Repo.get_by(User, ap_id: activity.data["object"])
assert activity == Utils.fetch_latest_follow(follower, followed)
end
end
describe "unfollowing" do
test "it reverts unfollow activity" do
follower = insert(:user)
followed = insert(:user)
{:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed)
with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
assert {:error, :reverted} = ActivityPub.unfollow(follower, followed)
end
activity = Activity.get_by_id(follow_activity.id)
assert activity.data["type"] == "Follow"
assert activity.data["actor"] == follower.ap_id
assert activity.data["object"] == followed.ap_id
end
test "creates an undo activity for the last follow" do
follower = insert(:user)
followed = insert(:user)
{:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed)
{:ok, activity} = ActivityPub.unfollow(follower, followed)
assert activity.data["type"] == "Undo"
assert activity.data["actor"] == follower.ap_id
embedded_object = activity.data["object"]
assert is_map(embedded_object)
assert embedded_object["type"] == "Follow"
assert embedded_object["object"] == followed.ap_id
assert embedded_object["id"] == follow_activity.data["id"]
end
test "creates an undo activity for a pending follow request" do
follower = insert(:user)
followed = insert(:user, %{locked: true})
{:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed)
{:ok, activity} = ActivityPub.unfollow(follower, followed)
assert activity.data["type"] == "Undo"
assert activity.data["actor"] == follower.ap_id
embedded_object = activity.data["object"]
assert is_map(embedded_object)
assert embedded_object["type"] == "Follow"
assert embedded_object["object"] == followed.ap_id
assert embedded_object["id"] == follow_activity.data["id"]
end
end
describe "timeline post-processing" do
test "it filters broken threads" do
user1 = insert(:user)
user2 = insert(:user)
user3 = insert(:user)
{:ok, user1} = User.follow(user1, user3)
assert User.following?(user1, user3)
{:ok, user2} = User.follow(user2, user3)
assert User.following?(user2, user3)
{:ok, user3} = User.follow(user3, user2)
assert User.following?(user3, user2)
{:ok, public_activity} = CommonAPI.post(user3, %{status: "hi 1"})
{:ok, private_activity_1} = CommonAPI.post(user3, %{status: "hi 2", visibility: "private"})
{:ok, private_activity_2} =
CommonAPI.post(user2, %{
status: "hi 3",
visibility: "private",
in_reply_to_status_id: private_activity_1.id
})
{:ok, private_activity_3} =
CommonAPI.post(user3, %{
status: "hi 4",
visibility: "private",
in_reply_to_status_id: private_activity_2.id
})
activities =
ActivityPub.fetch_activities([user1.ap_id | User.following(user1)])
|> Enum.map(fn a -> a.id end)
private_activity_1 = Activity.get_by_ap_id_with_object(private_activity_1.data["id"])
assert [public_activity.id, private_activity_1.id, private_activity_3.id] == activities
assert length(activities) == 3
activities =
ActivityPub.fetch_activities([user1.ap_id | User.following(user1)], %{user: user1})
|> Enum.map(fn a -> a.id end)
assert [public_activity.id, private_activity_1.id] == activities
assert length(activities) == 2
end
end
describe "flag/1" do
setup do
reporter = insert(:user)
target_account = insert(:user)
content = "foobar"
{:ok, activity} = CommonAPI.post(target_account, %{status: content})
context = Utils.generate_context_id()
reporter_ap_id = reporter.ap_id
target_ap_id = target_account.ap_id
activity_ap_id = activity.data["id"]
activity_with_object = Activity.get_by_ap_id_with_object(activity_ap_id)
{:ok,
%{
reporter: reporter,
context: context,
target_account: target_account,
reported_activity: activity,
content: content,
activity_ap_id: activity_ap_id,
activity_with_object: activity_with_object,
reporter_ap_id: reporter_ap_id,
target_ap_id: target_ap_id
}}
end
test "it can create a Flag activity",
%{
reporter: reporter,
context: context,
target_account: target_account,
reported_activity: reported_activity,
content: content,
activity_ap_id: activity_ap_id,
activity_with_object: activity_with_object,
reporter_ap_id: reporter_ap_id,
target_ap_id: target_ap_id
} do
assert {:ok, activity} =
ActivityPub.flag(%{
actor: reporter,
context: context,
account: target_account,
statuses: [reported_activity],
content: content
})
note_obj = %{
"type" => "Note",
"id" => activity_ap_id,
"content" => content,
"published" => activity_with_object.object.data["published"],
"actor" =>
AccountView.render("show.json", %{user: target_account, skip_visibility_check: true})
}
assert %Activity{
actor: ^reporter_ap_id,
data: %{
"type" => "Flag",
"content" => ^content,
"context" => ^context,
"object" => [^target_ap_id, ^note_obj]
}
} = activity
end
test_with_mock "strips status data from Flag, before federating it",
%{
reporter: reporter,
context: context,
target_account: target_account,
reported_activity: reported_activity,
content: content
},
Utils,
[:passthrough],
[] do
{:ok, activity} =
ActivityPub.flag(%{
actor: reporter,
context: context,
account: target_account,
statuses: [reported_activity],
content: content
})
new_data =
put_in(activity.data, ["object"], [target_account.ap_id, reported_activity.data["id"]])
assert_called(Utils.maybe_federate(%{activity | data: new_data}))
end
end
test "fetch_activities/2 returns activities addressed to a list " do
user = insert(:user)
member = insert(:user)
{:ok, list} = Pleroma.List.create("foo", user)
{:ok, list} = Pleroma.List.follow(list, member)
{:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})
activity = Repo.preload(activity, :bookmark)
activity = %Activity{activity | thread_muted?: !!activity.thread_muted?}
assert ActivityPub.fetch_activities([], %{user: user}) == [activity]
end
def data_uri do
File.read!("test/fixtures/avatar_data_uri")
end
describe "fetch_activities_bounded" do
test "fetches private posts for followed users" do
user = insert(:user)
{:ok, activity} =
CommonAPI.post(user, %{
status: "thought I looked cute might delete later :3",
visibility: "private"
})
[result] = ActivityPub.fetch_activities_bounded([user.follower_address], [])
assert result.id == activity.id
end
test "fetches only public posts for other users" do
user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{status: "#cofe", visibility: "public"})
{:ok, _private_activity} =
CommonAPI.post(user, %{
status: "why is tenshi eating a corndog so cute?",
visibility: "private"
})
[result] = ActivityPub.fetch_activities_bounded([], [user.follower_address])
assert result.id == activity.id
end
end
describe "fetch_follow_information_for_user" do
test "syncronizes following/followers counters" do
user =
insert(:user,
local: false,
follower_address: "http://localhost:4001/users/fuser2/followers",
following_address: "http://localhost:4001/users/fuser2/following"
)
{:ok, info} = ActivityPub.fetch_follow_information_for_user(user)
assert info.follower_count == 527
assert info.following_count == 267
end
test "detects hidden followers" do
mock(fn env ->
case env.url do
"http://localhost:4001/users/masto_closed/followers?page=1" ->
%Tesla.Env{status: 403, body: ""}
_ ->
apply(HttpRequestMock, :request, [env])
end
end)
user =
insert(:user,
local: false,
follower_address: "http://localhost:4001/users/masto_closed/followers",
following_address: "http://localhost:4001/users/masto_closed/following"
)
{:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
assert follow_info.hide_followers == true
assert follow_info.hide_follows == false
end
test "detects hidden follows" do
mock(fn env ->
case env.url do
"http://localhost:4001/users/masto_closed/following?page=1" ->
%Tesla.Env{status: 403, body: ""}
_ ->
apply(HttpRequestMock, :request, [env])
end
end)
user =
insert(:user,
local: false,
follower_address: "http://localhost:4001/users/masto_closed/followers",
following_address: "http://localhost:4001/users/masto_closed/following"
)
{:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
assert follow_info.hide_followers == false
assert follow_info.hide_follows == true
end
test "detects hidden follows/followers for friendica" do
user =
insert(:user,
local: false,
follower_address: "http://localhost:8080/followers/fuser3",
following_address: "http://localhost:8080/following/fuser3"
)
{:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
assert follow_info.hide_followers == true
assert follow_info.follower_count == 296
assert follow_info.following_count == 32
assert follow_info.hide_follows == true
end
test "doesn't crash when follower and following counters are hidden" do
mock(fn env ->
case env.url do
"http://localhost:4001/users/masto_hidden_counters/following" ->
json(%{
"@context" => "https://www.w3.org/ns/activitystreams",
"id" => "http://localhost:4001/users/masto_hidden_counters/followers"
})
"http://localhost:4001/users/masto_hidden_counters/following?page=1" ->
%Tesla.Env{status: 403, body: ""}
"http://localhost:4001/users/masto_hidden_counters/followers" ->
json(%{
"@context" => "https://www.w3.org/ns/activitystreams",
"id" => "http://localhost:4001/users/masto_hidden_counters/following"
})
"http://localhost:4001/users/masto_hidden_counters/followers?page=1" ->
%Tesla.Env{status: 403, body: ""}
end
end)
user =
insert(:user,
local: false,
follower_address: "http://localhost:4001/users/masto_hidden_counters/followers",
following_address: "http://localhost:4001/users/masto_hidden_counters/following"
)
{:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
assert follow_info.hide_followers == true
assert follow_info.follower_count == 0
assert follow_info.hide_follows == true
assert follow_info.following_count == 0
end
end
describe "fetch_favourites/3" do
test "returns a favourite activities sorted by adds to favorite" do
user = insert(:user)
other_user = insert(:user)
user1 = insert(:user)
user2 = insert(:user)
{:ok, a1} = CommonAPI.post(user1, %{status: "bla"})
{:ok, _a2} = CommonAPI.post(user2, %{status: "traps are happy"})
{:ok, a3} = CommonAPI.post(user2, %{status: "Trees Are "})
{:ok, a4} = CommonAPI.post(user2, %{status: "Agent Smith "})
{:ok, a5} = CommonAPI.post(user1, %{status: "Red or Blue "})
{:ok, _} = CommonAPI.favorite(user, a4.id)
{:ok, _} = CommonAPI.favorite(other_user, a3.id)
{:ok, _} = CommonAPI.favorite(user, a3.id)
{:ok, _} = CommonAPI.favorite(other_user, a5.id)
{:ok, _} = CommonAPI.favorite(user, a5.id)
{:ok, _} = CommonAPI.favorite(other_user, a4.id)
{:ok, _} = CommonAPI.favorite(user, a1.id)
{:ok, _} = CommonAPI.favorite(other_user, a1.id)
result = ActivityPub.fetch_favourites(user)
assert Enum.map(result, & &1.id) == [a1.id, a5.id, a3.id, a4.id]
result = ActivityPub.fetch_favourites(user, %{limit: 2})
assert Enum.map(result, & &1.id) == [a1.id, a5.id]
end
end
describe "Move activity" do
test "create" do
%{ap_id: old_ap_id} = old_user = insert(:user)
%{ap_id: new_ap_id} = new_user = insert(:user, also_known_as: [old_ap_id])
follower = insert(:user)
follower_move_opted_out = insert(:user, allow_following_move: false)
User.follow(follower, old_user)
User.follow(follower_move_opted_out, old_user)
assert User.following?(follower, old_user)
assert User.following?(follower_move_opted_out, old_user)
assert {:ok, activity} = ActivityPub.move(old_user, new_user)
assert %Activity{
actor: ^old_ap_id,
data: %{
"actor" => ^old_ap_id,
"object" => ^old_ap_id,
"target" => ^new_ap_id,
"type" => "Move"
},
local: true
} = activity
params = %{
"op" => "move_following",
"origin_id" => old_user.id,
"target_id" => new_user.id
}
assert_enqueued(worker: Pleroma.Workers.BackgroundWorker, args: params)
Pleroma.Workers.BackgroundWorker.perform(%Oban.Job{args: params})
refute User.following?(follower, old_user)
assert User.following?(follower, new_user)
assert User.following?(follower_move_opted_out, old_user)
refute User.following?(follower_move_opted_out, new_user)
activity = %Activity{activity | object: nil}
assert [%Notification{activity: ^activity}] = Notification.for_user(follower)
assert [%Notification{activity: ^activity}] = Notification.for_user(follower_move_opted_out)
end
test "old user must be in the new user's `also_known_as` list" do
old_user = insert(:user)
new_user = insert(:user)
assert {:error, "Target account must have the origin in `alsoKnownAs`"} =
ActivityPub.move(old_user, new_user)
end
end
test "doesn't retrieve replies activities with exclude_replies" do
user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{status: "yeah"})
{:ok, _reply} = CommonAPI.post(user, %{status: "yeah", in_reply_to_status_id: activity.id})
[result] = ActivityPub.fetch_public_activities(%{exclude_replies: true})
assert result.id == activity.id
assert length(ActivityPub.fetch_public_activities()) == 2
end
describe "replies filtering with public messages" do
setup :public_messages
test "public timeline", %{users: %{u1: user}} do
activities_ids =
%{}
|> Map.put(:type, ["Create", "Announce"])
|> Map.put(:local_only, false)
|> Map.put(:blocking_user, user)
|> Map.put(:muting_user, user)
|> Map.put(:reply_filtering_user, user)
|> ActivityPub.fetch_public_activities()
|> Enum.map(& &1.id)
assert length(activities_ids) == 16
end
test "public timeline with reply_visibility `following`", %{
users: %{u1: user},
u1: u1,
u2: u2,
u3: u3,
u4: u4,
activities: activities
} do
activities_ids =
%{}
|> Map.put(:type, ["Create", "Announce"])
|> Map.put(:local_only, false)
|> Map.put(:blocking_user, user)
|> Map.put(:muting_user, user)
|> Map.put(:reply_visibility, "following")
|> Map.put(:reply_filtering_user, user)
|> ActivityPub.fetch_public_activities()
|> Enum.map(& &1.id)
assert length(activities_ids) == 14
visible_ids =
Map.values(u1) ++ Map.values(u2) ++ Map.values(u4) ++ Map.values(activities) ++ [u3[:r1]]
assert Enum.all?(visible_ids, &(&1 in activities_ids))
end
test "public timeline with reply_visibility `self`", %{
users: %{u1: user},
u1: u1,
u2: u2,
u3: u3,
u4: u4,
activities: activities
} do
activities_ids =
%{}
|> Map.put(:type, ["Create", "Announce"])
|> Map.put(:local_only, false)
|> Map.put(:blocking_user, user)
|> Map.put(:muting_user, user)
|> Map.put(:reply_visibility, "self")
|> Map.put(:reply_filtering_user, user)
|> ActivityPub.fetch_public_activities()
|> Enum.map(& &1.id)
assert length(activities_ids) == 10
visible_ids = Map.values(u1) ++ [u2[:r1], u3[:r1], u4[:r1]] ++ Map.values(activities)
assert Enum.all?(visible_ids, &(&1 in activities_ids))
end
test "home timeline", %{
users: %{u1: user},
activities: activities,
u1: u1,
u2: u2,
u3: u3,
u4: u4
} do
params =
%{}
|> Map.put(:type, ["Create", "Announce"])
|> Map.put(:blocking_user, user)
|> Map.put(:muting_user, user)
|> Map.put(:user, user)
|> Map.put(:reply_filtering_user, user)
activities_ids =
ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
|> Enum.map(& &1.id)
assert length(activities_ids) == 13
visible_ids =
Map.values(u1) ++
Map.values(u3) ++
[
activities[:a1],
activities[:a2],
activities[:a4],
u2[:r1],
u2[:r3],
u4[:r1],
u4[:r2]
]
assert Enum.all?(visible_ids, &(&1 in activities_ids))
end
test "home timeline with reply_visibility `following`", %{
users: %{u1: user},
activities: activities,
u1: u1,
u2: u2,
u3: u3,
u4: u4
} do
params =
%{}
|> Map.put(:type, ["Create", "Announce"])
|> Map.put(:blocking_user, user)
|> Map.put(:muting_user, user)
|> Map.put(:user, user)
|> Map.put(:reply_visibility, "following")
|> Map.put(:reply_filtering_user, user)
activities_ids =
ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
|> Enum.map(& &1.id)
assert length(activities_ids) == 11
visible_ids =
Map.values(u1) ++
[
activities[:a1],
activities[:a2],
activities[:a4],
u2[:r1],
u2[:r3],
u3[:r1],
u4[:r1],
u4[:r2]
]
assert Enum.all?(visible_ids, &(&1 in activities_ids))
end
test "home timeline with reply_visibility `self`", %{
users: %{u1: user},
activities: activities,
u1: u1,
u2: u2,
u3: u3,
u4: u4
} do
params =
%{}
|> Map.put(:type, ["Create", "Announce"])
|> Map.put(:blocking_user, user)
|> Map.put(:muting_user, user)
|> Map.put(:user, user)
|> Map.put(:reply_visibility, "self")
|> Map.put(:reply_filtering_user, user)
activities_ids =
ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
|> Enum.map(& &1.id)
assert length(activities_ids) == 9
visible_ids =
Map.values(u1) ++
[
activities[:a1],
activities[:a2],
activities[:a4],
u2[:r1],
u3[:r1],
u4[:r1]
]
assert Enum.all?(visible_ids, &(&1 in activities_ids))
end
test "filtering out announces where the user is the actor of the announced message" do
user = insert(:user)
other_user = insert(:user)
third_user = insert(:user)
User.follow(user, other_user)
{:ok, post} = CommonAPI.post(user, %{status: "yo"})
{:ok, other_post} = CommonAPI.post(third_user, %{status: "yo"})
{:ok, _announce} = CommonAPI.repeat(post.id, other_user)
{:ok, _announce} = CommonAPI.repeat(post.id, third_user)
{:ok, announce} = CommonAPI.repeat(other_post.id, other_user)
params = %{
type: ["Announce"]
}
results =
[user.ap_id | User.following(user)]
|> ActivityPub.fetch_activities(params)
assert length(results) == 3
params = %{
type: ["Announce"],
announce_filtering_user: user
}
[result] =
[user.ap_id | User.following(user)]
|> ActivityPub.fetch_activities(params)
assert result.id == announce.id
end
end
describe "replies filtering with private messages" do
setup :private_messages
test "public timeline", %{users: %{u1: user}} do
activities_ids =
%{}
|> Map.put(:type, ["Create", "Announce"])
|> Map.put(:local_only, false)
|> Map.put(:blocking_user, user)
|> Map.put(:muting_user, user)
|> Map.put(:user, user)
|> ActivityPub.fetch_public_activities()
|> Enum.map(& &1.id)
assert activities_ids == []
end
test "public timeline with default reply_visibility `following`", %{users: %{u1: user}} do
activities_ids =
%{}
|> Map.put(:type, ["Create", "Announce"])
|> Map.put(:local_only, false)
|> Map.put(:blocking_user, user)
|> Map.put(:muting_user, user)
|> Map.put(:reply_visibility, "following")
|> Map.put(:reply_filtering_user, user)
|> Map.put(:user, user)
|> ActivityPub.fetch_public_activities()
|> Enum.map(& &1.id)
assert activities_ids == []
end
test "public timeline with default reply_visibility `self`", %{users: %{u1: user}} do
activities_ids =
%{}
|> Map.put(:type, ["Create", "Announce"])
|> Map.put(:local_only, false)
|> Map.put(:blocking_user, user)
|> Map.put(:muting_user, user)
|> Map.put(:reply_visibility, "self")
|> Map.put(:reply_filtering_user, user)
|> Map.put(:user, user)
|> ActivityPub.fetch_public_activities()
|> Enum.map(& &1.id)
assert activities_ids == []
activities_ids =
%{}
|> Map.put(:reply_visibility, "self")
|> Map.put(:reply_filtering_user, nil)
|> ActivityPub.fetch_public_activities()
assert activities_ids == []
end
test "home timeline", %{users: %{u1: user}} do
params =
%{}
|> Map.put(:type, ["Create", "Announce"])
|> Map.put(:blocking_user, user)
|> Map.put(:muting_user, user)
|> Map.put(:user, user)
activities_ids =
ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
|> Enum.map(& &1.id)
assert length(activities_ids) == 12
end
test "home timeline with default reply_visibility `following`", %{users: %{u1: user}} do
params =
%{}
|> Map.put(:type, ["Create", "Announce"])
|> Map.put(:blocking_user, user)
|> Map.put(:muting_user, user)
|> Map.put(:user, user)
|> Map.put(:reply_visibility, "following")
|> Map.put(:reply_filtering_user, user)
activities_ids =
ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
|> Enum.map(& &1.id)
assert length(activities_ids) == 12
end
test "home timeline with default reply_visibility `self`", %{
users: %{u1: user},
activities: activities,
u1: u1,
u2: u2,
u3: u3,
u4: u4
} do
params =
%{}
|> Map.put(:type, ["Create", "Announce"])
|> Map.put(:blocking_user, user)
|> Map.put(:muting_user, user)
|> Map.put(:user, user)
|> Map.put(:reply_visibility, "self")
|> Map.put(:reply_filtering_user, user)
activities_ids =
ActivityPub.fetch_activities([user.ap_id | User.following(user)], params)
|> Enum.map(& &1.id)
assert length(activities_ids) == 10
visible_ids =
Map.values(u1) ++ Map.values(u4) ++ [u2[:r1], u3[:r1]] ++ Map.values(activities)
assert Enum.all?(visible_ids, &(&1 in activities_ids))
end
end
defp public_messages(_) do
[u1, u2, u3, u4] = insert_list(4, :user)
{:ok, u1} = User.follow(u1, u2)
{:ok, u2} = User.follow(u2, u1)
{:ok, u1} = User.follow(u1, u4)
{:ok, u4} = User.follow(u4, u1)
{:ok, u2} = User.follow(u2, u3)
{:ok, u3} = User.follow(u3, u2)
{:ok, a1} = CommonAPI.post(u1, %{status: "Status"})
{:ok, r1_1} =
CommonAPI.post(u2, %{
status: "@#{u1.nickname} reply from u2 to u1",
in_reply_to_status_id: a1.id
})
{:ok, r1_2} =
CommonAPI.post(u3, %{
status: "@#{u1.nickname} reply from u3 to u1",
in_reply_to_status_id: a1.id
})
{:ok, r1_3} =
CommonAPI.post(u4, %{
status: "@#{u1.nickname} reply from u4 to u1",
in_reply_to_status_id: a1.id
})
{:ok, a2} = CommonAPI.post(u2, %{status: "Status"})
{:ok, r2_1} =
CommonAPI.post(u1, %{
status: "@#{u2.nickname} reply from u1 to u2",
in_reply_to_status_id: a2.id
})
{:ok, r2_2} =
CommonAPI.post(u3, %{
status: "@#{u2.nickname} reply from u3 to u2",
in_reply_to_status_id: a2.id
})
{:ok, r2_3} =
CommonAPI.post(u4, %{
status: "@#{u2.nickname} reply from u4 to u2",
in_reply_to_status_id: a2.id
})
{:ok, a3} = CommonAPI.post(u3, %{status: "Status"})
{:ok, r3_1} =
CommonAPI.post(u1, %{
status: "@#{u3.nickname} reply from u1 to u3",
in_reply_to_status_id: a3.id
})
{:ok, r3_2} =
CommonAPI.post(u2, %{
status: "@#{u3.nickname} reply from u2 to u3",
in_reply_to_status_id: a3.id
})
{:ok, r3_3} =
CommonAPI.post(u4, %{
status: "@#{u3.nickname} reply from u4 to u3",
in_reply_to_status_id: a3.id
})
{:ok, a4} = CommonAPI.post(u4, %{status: "Status"})
{:ok, r4_1} =
CommonAPI.post(u1, %{
status: "@#{u4.nickname} reply from u1 to u4",
in_reply_to_status_id: a4.id
})
{:ok, r4_2} =
CommonAPI.post(u2, %{
status: "@#{u4.nickname} reply from u2 to u4",
in_reply_to_status_id: a4.id
})
{:ok, r4_3} =
CommonAPI.post(u3, %{
status: "@#{u4.nickname} reply from u3 to u4",
in_reply_to_status_id: a4.id
})
{:ok,
users: %{u1: u1, u2: u2, u3: u3, u4: u4},
activities: %{a1: a1.id, a2: a2.id, a3: a3.id, a4: a4.id},
u1: %{r1: r1_1.id, r2: r1_2.id, r3: r1_3.id},
u2: %{r1: r2_1.id, r2: r2_2.id, r3: r2_3.id},
u3: %{r1: r3_1.id, r2: r3_2.id, r3: r3_3.id},
u4: %{r1: r4_1.id, r2: r4_2.id, r3: r4_3.id}}
end
defp private_messages(_) do
[u1, u2, u3, u4] = insert_list(4, :user)
{:ok, u1} = User.follow(u1, u2)
{:ok, u2} = User.follow(u2, u1)
{:ok, u1} = User.follow(u1, u3)
{:ok, u3} = User.follow(u3, u1)
{:ok, u1} = User.follow(u1, u4)
{:ok, u4} = User.follow(u4, u1)
{:ok, u2} = User.follow(u2, u3)
{:ok, u3} = User.follow(u3, u2)
{:ok, a1} = CommonAPI.post(u1, %{status: "Status", visibility: "private"})
{:ok, r1_1} =
CommonAPI.post(u2, %{
status: "@#{u1.nickname} reply from u2 to u1",
in_reply_to_status_id: a1.id,
visibility: "private"
})
{:ok, r1_2} =
CommonAPI.post(u3, %{
status: "@#{u1.nickname} reply from u3 to u1",
in_reply_to_status_id: a1.id,
visibility: "private"
})
{:ok, r1_3} =
CommonAPI.post(u4, %{
status: "@#{u1.nickname} reply from u4 to u1",
in_reply_to_status_id: a1.id,
visibility: "private"
})
{:ok, a2} = CommonAPI.post(u2, %{status: "Status", visibility: "private"})
{:ok, r2_1} =
CommonAPI.post(u1, %{
status: "@#{u2.nickname} reply from u1 to u2",
in_reply_to_status_id: a2.id,
visibility: "private"
})
{:ok, r2_2} =
CommonAPI.post(u3, %{
status: "@#{u2.nickname} reply from u3 to u2",
in_reply_to_status_id: a2.id,
visibility: "private"
})
{:ok, a3} = CommonAPI.post(u3, %{status: "Status", visibility: "private"})
{:ok, r3_1} =
CommonAPI.post(u1, %{
status: "@#{u3.nickname} reply from u1 to u3",
in_reply_to_status_id: a3.id,
visibility: "private"
})
{:ok, r3_2} =
CommonAPI.post(u2, %{
status: "@#{u3.nickname} reply from u2 to u3",
in_reply_to_status_id: a3.id,
visibility: "private"
})
{:ok, a4} = CommonAPI.post(u4, %{status: "Status", visibility: "private"})
{:ok, r4_1} =
CommonAPI.post(u1, %{
status: "@#{u4.nickname} reply from u1 to u4",
in_reply_to_status_id: a4.id,
visibility: "private"
})
{:ok,
users: %{u1: u1, u2: u2, u3: u3, u4: u4},
activities: %{a1: a1.id, a2: a2.id, a3: a3.id, a4: a4.id},
u1: %{r1: r1_1.id, r2: r1_2.id, r3: r1_3.id},
u2: %{r1: r2_1.id, r2: r2_2.id},
u3: %{r1: r3_1.id, r2: r3_2.id},
u4: %{r1: r4_1.id}}
end
describe "maybe_update_follow_information/1" do
setup do
clear_config([:instance, :external_user_synchronization], true)
user = %{
local: false,
ap_id: "https://gensokyo.2hu/users/raymoo",
following_address: "https://gensokyo.2hu/users/following",
follower_address: "https://gensokyo.2hu/users/followers",
type: "Person"
}
%{user: user}
end
test "logs an error when it can't fetch the info", %{user: user} do
assert capture_log(fn ->
ActivityPub.maybe_update_follow_information(user)
end) =~ "Follower/Following counter update for #{user.ap_id} failed"
end
test "just returns the input if the user type is Application", %{
user: user
} do
user =
user
|> Map.put(:type, "Application")
refute capture_log(fn ->
assert ^user = ActivityPub.maybe_update_follow_information(user)
end) =~ "Follower/Following counter update for #{user.ap_id} failed"
end
test "it just returns the input if the user has no following/follower addresses", %{
user: user
} do
user =
user
|> Map.put(:following_address, nil)
|> Map.put(:follower_address, nil)
refute capture_log(fn ->
assert ^user = ActivityPub.maybe_update_follow_information(user)
end) =~ "Follower/Following counter update for #{user.ap_id} failed"
end
end
describe "global activity expiration" do
test "creates an activity expiration for local Create activities" do
clear_config([:mrf, :policies], Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy)
{:ok, activity} = ActivityBuilder.insert(%{"type" => "Create", "context" => "3hu"})
{:ok, follow} = ActivityBuilder.insert(%{"type" => "Follow", "context" => "3hu"})
assert_enqueued(
worker: Pleroma.Workers.PurgeExpiredActivity,
args: %{activity_id: activity.id},
scheduled_at:
activity.inserted_at
|> DateTime.from_naive!("Etc/UTC")
|> Timex.shift(days: 365)
)
refute_enqueued(
worker: Pleroma.Workers.PurgeExpiredActivity,
args: %{activity_id: follow.id}
)
end
end
describe "handling of clashing nicknames" do
test "renames an existing user with a clashing nickname and a different ap id" do
orig_user =
insert(
:user,
local: false,
nickname: "admin@mastodon.example.org",
ap_id: "http://mastodon.example.org/users/harinezumigari"
)
%{
nickname: orig_user.nickname,
ap_id: orig_user.ap_id <> "part_2"
}
|> ActivityPub.maybe_handle_clashing_nickname()
user = User.get_by_id(orig_user.id)
assert user.nickname == "#{orig_user.id}.admin@mastodon.example.org"
end
test "does nothing with a clashing nickname and the same ap id" do
orig_user =
insert(
:user,
local: false,
nickname: "admin@mastodon.example.org",
ap_id: "http://mastodon.example.org/users/harinezumigari"
)
%{
nickname: orig_user.nickname,
ap_id: orig_user.ap_id
}
|> ActivityPub.maybe_handle_clashing_nickname()
user = User.get_by_id(orig_user.id)
assert user.nickname == orig_user.nickname
end
end
describe "reply filtering" do
test "`following` still contains announcements by friends" do
user = insert(:user)
followed = insert(:user)
not_followed = insert(:user)
User.follow(user, followed)
{:ok, followed_post} = CommonAPI.post(followed, %{status: "Hello"})
{:ok, not_followed_to_followed} =
CommonAPI.post(not_followed, %{
status: "Also hello",
in_reply_to_status_id: followed_post.id
})
{:ok, retoot} = CommonAPI.repeat(not_followed_to_followed.id, followed)
params =
%{}
|> Map.put(:type, ["Create", "Announce"])
|> Map.put(:blocking_user, user)
|> Map.put(:muting_user, user)
|> Map.put(:reply_filtering_user, user)
|> Map.put(:reply_visibility, "following")
|> Map.put(:announce_filtering_user, user)
|> Map.put(:user, user)
activities =
[user.ap_id | User.following(user)]
|> ActivityPub.fetch_activities(params)
followed_post_id = followed_post.id
retoot_id = retoot.id
assert [%{id: ^followed_post_id}, %{id: ^retoot_id}] = activities
assert length(activities) == 2
end
# This test is skipped because, while this is the desired behavior,
# there seems to be no good way to achieve it with the method that
# we currently use for detecting to who a reply is directed.
# This is a TODO and should be fixed by a later rewrite of the code
# in question.
@tag skip: true
test "`following` still contains self-replies by friends" do
user = insert(:user)
followed = insert(:user)
not_followed = insert(:user)
User.follow(user, followed)
{:ok, followed_post} = CommonAPI.post(followed, %{status: "Hello"})
{:ok, not_followed_post} = CommonAPI.post(not_followed, %{status: "Also hello"})
{:ok, _followed_to_not_followed} =
CommonAPI.post(followed, %{status: "sup", in_reply_to_status_id: not_followed_post.id})
{:ok, _followed_self_reply} =
CommonAPI.post(followed, %{status: "Also cofe", in_reply_to_status_id: followed_post.id})
params =
%{}
|> Map.put(:type, ["Create", "Announce"])
|> Map.put(:blocking_user, user)
|> Map.put(:muting_user, user)
|> Map.put(:reply_filtering_user, user)
|> Map.put(:reply_visibility, "following")
|> Map.put(:announce_filtering_user, user)
|> Map.put(:user, user)
activities =
[user.ap_id | User.following(user)]
|> ActivityPub.fetch_activities(params)
assert length(activities) == 2
end
end
end
diff --git a/test/pleroma/web/activity_pub/object_validators/attachment_validator_test.exs b/test/pleroma/web/activity_pub/object_validators/attachment_validator_test.exs
index 558bb3131..760388e80 100644
--- a/test/pleroma/web/activity_pub/object_validators/attachment_validator_test.exs
+++ b/test/pleroma/web/activity_pub/object_validators/attachment_validator_test.exs
@@ -1,74 +1,74 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidatorTest do
use Pleroma.DataCase
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator
import Pleroma.Factory
describe "attachments" do
test "works with honkerific attachments" do
attachment = %{
"mediaType" => "",
"name" => "",
"summary" => "298p3RG7j27tfsZ9RQ.jpg",
"type" => "Document",
"url" => "https://honk.tedunangst.com/d/298p3RG7j27tfsZ9RQ.jpg"
}
assert {:ok, attachment} =
AttachmentValidator.cast_and_validate(attachment)
|> Ecto.Changeset.apply_action(:insert)
assert attachment.mediaType == "application/octet-stream"
end
test "it turns mastodon attachments into our attachments" do
attachment = %{
"url" =>
"http://mastodon.example.org/system/media_attachments/files/000/000/002/original/334ce029e7bfb920.jpg",
"type" => "Document",
"name" => nil,
"mediaType" => "image/jpeg"
}
{:ok, attachment} =
AttachmentValidator.cast_and_validate(attachment)
|> Ecto.Changeset.apply_action(:insert)
assert [
%{
href:
"http://mastodon.example.org/system/media_attachments/files/000/000/002/original/334ce029e7bfb920.jpg",
type: "Link",
mediaType: "image/jpeg"
}
] = attachment.url
assert attachment.mediaType == "image/jpeg"
end
test "it handles our own uploads" do
user = insert(:user)
file = %Plug.Upload{
- content_type: "image/jpg",
+ content_type: "image/jpeg",
path: Path.absname("test/fixtures/image.jpg"),
filename: "an_image.jpg"
}
{:ok, attachment} = ActivityPub.upload(file, actor: user.ap_id)
{:ok, attachment} =
attachment.data
|> AttachmentValidator.cast_and_validate()
|> Ecto.Changeset.apply_action(:insert)
assert attachment.mediaType == "image/jpeg"
end
end
end
diff --git a/test/pleroma/web/activity_pub/object_validators/chat_validation_test.exs b/test/pleroma/web/activity_pub/object_validators/chat_validation_test.exs
index 16e4808e5..d7e299224 100644
--- a/test/pleroma/web/activity_pub/object_validators/chat_validation_test.exs
+++ b/test/pleroma/web/activity_pub/object_validators/chat_validation_test.exs
@@ -1,212 +1,212 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.ObjectValidators.ChatValidationTest do
use Pleroma.DataCase
alias Pleroma.Object
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Builder
alias Pleroma.Web.ActivityPub.ObjectValidator
alias Pleroma.Web.CommonAPI
import Pleroma.Factory
describe "chat message create activities" do
test "it is invalid if the object already exists" do
user = insert(:user)
recipient = insert(:user)
{:ok, activity} = CommonAPI.post_chat_message(user, recipient, "hey")
object = Object.normalize(activity, false)
{:ok, create_data, _} = Builder.create(user, object.data, [recipient.ap_id])
{:error, cng} = ObjectValidator.validate(create_data, [])
assert {:object, {"The object to create already exists", []}} in cng.errors
end
test "it is invalid if the object data has a different `to` or `actor` field" do
user = insert(:user)
recipient = insert(:user)
{:ok, object_data, _} = Builder.chat_message(recipient, user.ap_id, "Hey")
{:ok, create_data, _} = Builder.create(user, object_data, [recipient.ap_id])
{:error, cng} = ObjectValidator.validate(create_data, [])
assert {:to, {"Recipients don't match with object recipients", []}} in cng.errors
assert {:actor, {"Actor doesn't match with object actor", []}} in cng.errors
end
end
describe "chat messages" do
setup do
clear_config([:instance, :remote_limit])
user = insert(:user)
recipient = insert(:user, local: false)
{:ok, valid_chat_message, _} = Builder.chat_message(user, recipient.ap_id, "hey :firefox:")
%{user: user, recipient: recipient, valid_chat_message: valid_chat_message}
end
test "let's through some basic html", %{user: user, recipient: recipient} do
{:ok, valid_chat_message, _} =
Builder.chat_message(
user,
recipient.ap_id,
"hey <a href='https://example.org'>example</a> <script>alert('uguu')</script>"
)
assert {:ok, object, _meta} = ObjectValidator.validate(valid_chat_message, [])
assert object["content"] ==
"hey <a href=\"https://example.org\">example</a> alert(&#39;uguu&#39;)"
end
test "validates for a basic object we build", %{valid_chat_message: valid_chat_message} do
assert {:ok, object, _meta} = ObjectValidator.validate(valid_chat_message, [])
assert Map.put(valid_chat_message, "attachment", nil) == object
assert match?(%{"firefox" => _}, object["emoji"])
end
test "validates for a basic object with an attachment", %{
valid_chat_message: valid_chat_message,
user: user
} do
file = %Plug.Upload{
- content_type: "image/jpg",
+ content_type: "image/jpeg",
path: Path.absname("test/fixtures/image.jpg"),
filename: "an_image.jpg"
}
{:ok, attachment} = ActivityPub.upload(file, actor: user.ap_id)
valid_chat_message =
valid_chat_message
|> Map.put("attachment", attachment.data)
assert {:ok, object, _meta} = ObjectValidator.validate(valid_chat_message, [])
assert object["attachment"]
end
test "validates for a basic object with an attachment in an array", %{
valid_chat_message: valid_chat_message,
user: user
} do
file = %Plug.Upload{
- content_type: "image/jpg",
+ content_type: "image/jpeg",
path: Path.absname("test/fixtures/image.jpg"),
filename: "an_image.jpg"
}
{:ok, attachment} = ActivityPub.upload(file, actor: user.ap_id)
valid_chat_message =
valid_chat_message
|> Map.put("attachment", [attachment.data])
assert {:ok, object, _meta} = ObjectValidator.validate(valid_chat_message, [])
assert object["attachment"]
end
test "validates for a basic object with an attachment but without content", %{
valid_chat_message: valid_chat_message,
user: user
} do
file = %Plug.Upload{
- content_type: "image/jpg",
+ content_type: "image/jpeg",
path: Path.absname("test/fixtures/image.jpg"),
filename: "an_image.jpg"
}
{:ok, attachment} = ActivityPub.upload(file, actor: user.ap_id)
valid_chat_message =
valid_chat_message
|> Map.put("attachment", attachment.data)
|> Map.delete("content")
assert {:ok, object, _meta} = ObjectValidator.validate(valid_chat_message, [])
assert object["attachment"]
end
test "does not validate if the message has no content", %{
valid_chat_message: valid_chat_message
} do
contentless =
valid_chat_message
|> Map.delete("content")
refute match?({:ok, _object, _meta}, ObjectValidator.validate(contentless, []))
end
test "does not validate if the message is longer than the remote_limit", %{
valid_chat_message: valid_chat_message
} do
Pleroma.Config.put([:instance, :remote_limit], 2)
refute match?({:ok, _object, _meta}, ObjectValidator.validate(valid_chat_message, []))
end
test "does not validate if the recipient is blocking the actor", %{
valid_chat_message: valid_chat_message,
user: user,
recipient: recipient
} do
Pleroma.User.block(recipient, user)
refute match?({:ok, _object, _meta}, ObjectValidator.validate(valid_chat_message, []))
end
test "does not validate if the recipient is not accepting chat messages", %{
valid_chat_message: valid_chat_message,
recipient: recipient
} do
recipient
|> Ecto.Changeset.change(%{accepts_chat_messages: false})
|> Pleroma.Repo.update!()
refute match?({:ok, _object, _meta}, ObjectValidator.validate(valid_chat_message, []))
end
test "does not validate if the actor or the recipient is not in our system", %{
valid_chat_message: valid_chat_message
} do
chat_message =
valid_chat_message
|> Map.put("actor", "https://raymoo.com/raymoo")
{:error, _} = ObjectValidator.validate(chat_message, [])
chat_message =
valid_chat_message
|> Map.put("to", ["https://raymoo.com/raymoo"])
{:error, _} = ObjectValidator.validate(chat_message, [])
end
test "does not validate for a message with multiple recipients", %{
valid_chat_message: valid_chat_message,
user: user,
recipient: recipient
} do
chat_message =
valid_chat_message
|> Map.put("to", [user.ap_id, recipient.ap_id])
assert {:error, _} = ObjectValidator.validate(chat_message, [])
end
test "does not validate if it doesn't concern local users" do
user = insert(:user, local: false)
recipient = insert(:user, local: false)
{:ok, valid_chat_message, _} = Builder.chat_message(user, recipient.ap_id, "hey")
assert {:error, _} = ObjectValidator.validate(valid_chat_message, [])
end
end
end
diff --git a/test/pleroma/web/common_api_test.exs b/test/pleroma/web/common_api_test.exs
index e34f5a49b..d19d484b7 100644
--- a/test/pleroma/web/common_api_test.exs
+++ b/test/pleroma/web/common_api_test.exs
@@ -1,1244 +1,1244 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.CommonAPITest do
use Pleroma.DataCase
use Oban.Testing, repo: Pleroma.Repo
alias Pleroma.Activity
alias Pleroma.Chat
alias Pleroma.Conversation.Participation
alias Pleroma.Notification
alias Pleroma.Object
alias Pleroma.Repo
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Transmogrifier
alias Pleroma.Web.ActivityPub.Visibility
alias Pleroma.Web.AdminAPI.AccountView
alias Pleroma.Web.CommonAPI
import Pleroma.Factory
import Mock
import Ecto.Query, only: [from: 2]
require Pleroma.Constants
setup do: clear_config([:instance, :safe_dm_mentions])
setup do: clear_config([:instance, :limit])
setup do: clear_config([:instance, :max_pinned_statuses])
describe "posting polls" do
test "it posts a poll" do
user = insert(:user)
{:ok, activity} =
CommonAPI.post(user, %{
status: "who is the best",
poll: %{expires_in: 600, options: ["reimu", "marisa"]}
})
object = Object.normalize(activity)
assert object.data["type"] == "Question"
assert object.data["oneOf"] |> length() == 2
end
end
describe "blocking" do
setup do
blocker = insert(:user)
blocked = insert(:user)
User.follow(blocker, blocked)
User.follow(blocked, blocker)
%{blocker: blocker, blocked: blocked}
end
test "it blocks and federates", %{blocker: blocker, blocked: blocked} do
clear_config([:instance, :federating], true)
with_mock Pleroma.Web.Federator,
publish: fn _ -> nil end do
assert {:ok, block} = CommonAPI.block(blocker, blocked)
assert block.local
assert User.blocks?(blocker, blocked)
refute User.following?(blocker, blocked)
refute User.following?(blocked, blocker)
assert called(Pleroma.Web.Federator.publish(block))
end
end
test "it blocks and does not federate if outgoing blocks are disabled", %{
blocker: blocker,
blocked: blocked
} do
clear_config([:instance, :federating], true)
clear_config([:activitypub, :outgoing_blocks], false)
with_mock Pleroma.Web.Federator,
publish: fn _ -> nil end do
assert {:ok, block} = CommonAPI.block(blocker, blocked)
assert block.local
assert User.blocks?(blocker, blocked)
refute User.following?(blocker, blocked)
refute User.following?(blocked, blocker)
refute called(Pleroma.Web.Federator.publish(block))
end
end
end
describe "posting chat messages" do
setup do: clear_config([:instance, :chat_limit])
test "it posts a chat message without content but with an attachment" do
author = insert(:user)
recipient = insert(:user)
file = %Plug.Upload{
- content_type: "image/jpg",
+ content_type: "image/jpeg",
path: Path.absname("test/fixtures/image.jpg"),
filename: "an_image.jpg"
}
{:ok, upload} = ActivityPub.upload(file, actor: author.ap_id)
with_mocks([
{
Pleroma.Web.Streamer,
[],
[
stream: fn _, _ ->
nil
end
]
},
{
Pleroma.Web.Push,
[],
[
send: fn _ -> nil end
]
}
]) do
{:ok, activity} =
CommonAPI.post_chat_message(
author,
recipient,
nil,
media_id: upload.id
)
notification =
Notification.for_user_and_activity(recipient, activity)
|> Repo.preload(:activity)
assert called(Pleroma.Web.Push.send(notification))
assert called(Pleroma.Web.Streamer.stream(["user", "user:notification"], notification))
assert called(Pleroma.Web.Streamer.stream(["user", "user:pleroma_chat"], :_))
assert activity
end
end
test "it adds html newlines" do
author = insert(:user)
recipient = insert(:user)
other_user = insert(:user)
{:ok, activity} =
CommonAPI.post_chat_message(
author,
recipient,
"uguu\nuguuu"
)
assert other_user.ap_id not in activity.recipients
object = Object.normalize(activity, false)
assert object.data["content"] == "uguu<br/>uguuu"
end
test "it linkifies" do
author = insert(:user)
recipient = insert(:user)
other_user = insert(:user)
{:ok, activity} =
CommonAPI.post_chat_message(
author,
recipient,
"https://example.org is the site of @#{other_user.nickname} #2hu"
)
assert other_user.ap_id not in activity.recipients
object = Object.normalize(activity, false)
assert object.data["content"] ==
"<a href=\"https://example.org\" rel=\"ugc\">https://example.org</a> is the site of <span class=\"h-card\"><a class=\"u-url mention\" data-user=\"#{
other_user.id
}\" href=\"#{other_user.ap_id}\" rel=\"ugc\">@<span>#{other_user.nickname}</span></a></span> <a class=\"hashtag\" data-tag=\"2hu\" href=\"http://localhost:4001/tag/2hu\">#2hu</a>"
end
test "it posts a chat message" do
author = insert(:user)
recipient = insert(:user)
{:ok, activity} =
CommonAPI.post_chat_message(
author,
recipient,
"a test message <script>alert('uuu')</script> :firefox:"
)
assert activity.data["type"] == "Create"
assert activity.local
object = Object.normalize(activity)
assert object.data["type"] == "ChatMessage"
assert object.data["to"] == [recipient.ap_id]
assert object.data["content"] ==
"a test message &lt;script&gt;alert(&#39;uuu&#39;)&lt;/script&gt; :firefox:"
assert object.data["emoji"] == %{
"firefox" => "http://localhost:4001/emoji/Firefox.gif"
}
assert Chat.get(author.id, recipient.ap_id)
assert Chat.get(recipient.id, author.ap_id)
assert :ok == Pleroma.Web.Federator.perform(:publish, activity)
end
test "it reject messages over the local limit" do
Pleroma.Config.put([:instance, :chat_limit], 2)
author = insert(:user)
recipient = insert(:user)
{:error, message} =
CommonAPI.post_chat_message(
author,
recipient,
"123"
)
assert message == :content_too_long
end
test "it reject messages via MRF" do
clear_config([:mrf_keyword, :reject], ["GNO"])
clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.KeywordPolicy])
author = insert(:user)
recipient = insert(:user)
assert {:reject, "[KeywordPolicy] Matches with rejected keyword"} ==
CommonAPI.post_chat_message(author, recipient, "GNO/Linux")
end
end
describe "unblocking" do
test "it works even without an existing block activity" do
blocked = insert(:user)
blocker = insert(:user)
User.block(blocker, blocked)
assert User.blocks?(blocker, blocked)
assert {:ok, :no_activity} == CommonAPI.unblock(blocker, blocked)
refute User.blocks?(blocker, blocked)
end
end
describe "deletion" do
test "it works with pruned objects" do
user = insert(:user)
{:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
clear_config([:instance, :federating], true)
Object.normalize(post, false)
|> Object.prune()
with_mock Pleroma.Web.Federator,
publish: fn _ -> nil end do
assert {:ok, delete} = CommonAPI.delete(post.id, user)
assert delete.local
assert called(Pleroma.Web.Federator.publish(delete))
end
refute Activity.get_by_id(post.id)
end
test "it allows users to delete their posts" do
user = insert(:user)
{:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
clear_config([:instance, :federating], true)
with_mock Pleroma.Web.Federator,
publish: fn _ -> nil end do
assert {:ok, delete} = CommonAPI.delete(post.id, user)
assert delete.local
assert called(Pleroma.Web.Federator.publish(delete))
end
refute Activity.get_by_id(post.id)
end
test "it does not allow a user to delete their posts" do
user = insert(:user)
other_user = insert(:user)
{:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
assert {:error, "Could not delete"} = CommonAPI.delete(post.id, other_user)
assert Activity.get_by_id(post.id)
end
test "it allows moderators to delete other user's posts" do
user = insert(:user)
moderator = insert(:user, is_moderator: true)
{:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
assert {:ok, delete} = CommonAPI.delete(post.id, moderator)
assert delete.local
refute Activity.get_by_id(post.id)
end
test "it allows admins to delete other user's posts" do
user = insert(:user)
moderator = insert(:user, is_admin: true)
{:ok, post} = CommonAPI.post(user, %{status: "namu amida butsu"})
assert {:ok, delete} = CommonAPI.delete(post.id, moderator)
assert delete.local
refute Activity.get_by_id(post.id)
end
test "superusers deleting non-local posts won't federate the delete" do
# This is the user of the ingested activity
_user =
insert(:user,
local: false,
ap_id: "http://mastodon.example.org/users/admin",
last_refreshed_at: NaiveDateTime.utc_now()
)
moderator = insert(:user, is_admin: true)
data =
File.read!("test/fixtures/mastodon-post-activity.json")
|> Jason.decode!()
{:ok, post} = Transmogrifier.handle_incoming(data)
with_mock Pleroma.Web.Federator,
publish: fn _ -> nil end do
assert {:ok, delete} = CommonAPI.delete(post.id, moderator)
assert delete.local
refute called(Pleroma.Web.Federator.publish(:_))
end
refute Activity.get_by_id(post.id)
end
end
test "favoriting race condition" do
user = insert(:user)
users_serial = insert_list(10, :user)
users = insert_list(10, :user)
{:ok, activity} = CommonAPI.post(user, %{status: "."})
users_serial
|> Enum.map(fn user ->
CommonAPI.favorite(user, activity.id)
end)
object = Object.get_by_ap_id(activity.data["object"])
assert object.data["like_count"] == 10
users
|> Enum.map(fn user ->
Task.async(fn ->
CommonAPI.favorite(user, activity.id)
end)
end)
|> Enum.map(&Task.await/1)
object = Object.get_by_ap_id(activity.data["object"])
assert object.data["like_count"] == 20
end
test "repeating race condition" do
user = insert(:user)
users_serial = insert_list(10, :user)
users = insert_list(10, :user)
{:ok, activity} = CommonAPI.post(user, %{status: "."})
users_serial
|> Enum.map(fn user ->
CommonAPI.repeat(activity.id, user)
end)
object = Object.get_by_ap_id(activity.data["object"])
assert object.data["announcement_count"] == 10
users
|> Enum.map(fn user ->
Task.async(fn ->
CommonAPI.repeat(activity.id, user)
end)
end)
|> Enum.map(&Task.await/1)
object = Object.get_by_ap_id(activity.data["object"])
assert object.data["announcement_count"] == 20
end
test "when replying to a conversation / participation, it will set the correct context id even if no explicit reply_to is given" do
user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"})
[participation] = Participation.for_user(user)
{:ok, convo_reply} =
CommonAPI.post(user, %{status: ".", in_reply_to_conversation_id: participation.id})
assert Visibility.is_direct?(convo_reply)
assert activity.data["context"] == convo_reply.data["context"]
end
test "when replying to a conversation / participation, it only mentions the recipients explicitly declared in the participation" do
har = insert(:user)
jafnhar = insert(:user)
tridi = insert(:user)
{:ok, activity} =
CommonAPI.post(har, %{
status: "@#{jafnhar.nickname} hey",
visibility: "direct"
})
assert har.ap_id in activity.recipients
assert jafnhar.ap_id in activity.recipients
[participation] = Participation.for_user(har)
{:ok, activity} =
CommonAPI.post(har, %{
status: "I don't really like @#{tridi.nickname}",
visibility: "direct",
in_reply_to_status_id: activity.id,
in_reply_to_conversation_id: participation.id
})
assert har.ap_id in activity.recipients
assert jafnhar.ap_id in activity.recipients
refute tridi.ap_id in activity.recipients
end
test "with the safe_dm_mention option set, it does not mention people beyond the initial tags" do
har = insert(:user)
jafnhar = insert(:user)
tridi = insert(:user)
Pleroma.Config.put([:instance, :safe_dm_mentions], true)
{:ok, activity} =
CommonAPI.post(har, %{
status: "@#{jafnhar.nickname} hey, i never want to see @#{tridi.nickname} again",
visibility: "direct"
})
refute tridi.ap_id in activity.recipients
assert jafnhar.ap_id in activity.recipients
end
test "it de-duplicates tags" do
user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{status: "#2hu #2HU"})
object = Object.normalize(activity)
assert object.data["tag"] == ["2hu"]
end
test "it adds emoji in the object" do
user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{status: ":firefox:"})
assert Object.normalize(activity).data["emoji"]["firefox"]
end
describe "posting" do
test "deactivated users can't post" do
user = insert(:user, deactivated: true)
assert {:error, _} = CommonAPI.post(user, %{status: "ye"})
end
test "it supports explicit addressing" do
user = insert(:user)
user_two = insert(:user)
user_three = insert(:user)
user_four = insert(:user)
{:ok, activity} =
CommonAPI.post(user, %{
status:
"Hey, I think @#{user_three.nickname} is ugly. @#{user_four.nickname} is alright though.",
to: [user_two.nickname, user_four.nickname, "nonexistent"]
})
assert user.ap_id in activity.recipients
assert user_two.ap_id in activity.recipients
assert user_four.ap_id in activity.recipients
refute user_three.ap_id in activity.recipients
end
test "it filters out obviously bad tags when accepting a post as HTML" do
user = insert(:user)
post = "<p><b>2hu</b></p><script>alert('xss')</script>"
{:ok, activity} =
CommonAPI.post(user, %{
status: post,
content_type: "text/html"
})
object = Object.normalize(activity)
assert object.data["content"] == "<p><b>2hu</b></p>alert(&#39;xss&#39;)"
assert object.data["source"] == post
end
test "it filters out obviously bad tags when accepting a post as Markdown" do
user = insert(:user)
post = "<p><b>2hu</b></p><script>alert('xss')</script>"
{:ok, activity} =
CommonAPI.post(user, %{
status: post,
content_type: "text/markdown"
})
object = Object.normalize(activity)
assert object.data["content"] == "<p><b>2hu</b></p>alert(&#39;xss&#39;)"
assert object.data["source"] == post
end
test "it does not allow replies to direct messages that are not direct messages themselves" do
user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{status: "suya..", visibility: "direct"})
assert {:ok, _} =
CommonAPI.post(user, %{
status: "suya..",
visibility: "direct",
in_reply_to_status_id: activity.id
})
Enum.each(["public", "private", "unlisted"], fn visibility ->
assert {:error, "The message visibility must be direct"} =
CommonAPI.post(user, %{
status: "suya..",
visibility: visibility,
in_reply_to_status_id: activity.id
})
end)
end
test "replying with a direct message will NOT auto-add the author of the reply to the recipient list" do
user = insert(:user)
other_user = insert(:user)
third_user = insert(:user)
{:ok, post} = CommonAPI.post(user, %{status: "I'm stupid"})
{:ok, open_answer} =
CommonAPI.post(other_user, %{status: "No ur smart", in_reply_to_status_id: post.id})
# The OP is implicitly added
assert user.ap_id in open_answer.recipients
{:ok, secret_answer} =
CommonAPI.post(other_user, %{
status: "lol, that guy really is stupid, right, @#{third_user.nickname}?",
in_reply_to_status_id: post.id,
visibility: "direct"
})
assert third_user.ap_id in secret_answer.recipients
# The OP is not added
refute user.ap_id in secret_answer.recipients
end
test "it allows to address a list" do
user = insert(:user)
{:ok, list} = Pleroma.List.create("foo", user)
{:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})
assert activity.data["bcc"] == [list.ap_id]
assert activity.recipients == [list.ap_id, user.ap_id]
assert activity.data["listMessage"] == list.ap_id
end
test "it returns error when status is empty and no attachments" do
user = insert(:user)
assert {:error, "Cannot post an empty status without attachments"} =
CommonAPI.post(user, %{status: ""})
end
test "it validates character limits are correctly enforced" do
Pleroma.Config.put([:instance, :limit], 5)
user = insert(:user)
assert {:error, "The status is over the character limit"} =
CommonAPI.post(user, %{status: "foobar"})
assert {:ok, activity} = CommonAPI.post(user, %{status: "12345"})
end
test "it can handle activities that expire" do
user = insert(:user)
expires_at = DateTime.add(DateTime.utc_now(), 1_000_000)
assert {:ok, activity} = CommonAPI.post(user, %{status: "chai", expires_in: 1_000_000})
assert_enqueued(
worker: Pleroma.Workers.PurgeExpiredActivity,
args: %{activity_id: activity.id},
scheduled_at: expires_at
)
end
end
describe "reactions" do
test "reacting to a status with an emoji" do
user = insert(:user)
other_user = insert(:user)
{:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
{:ok, reaction} = CommonAPI.react_with_emoji(activity.id, user, "👍")
assert reaction.data["actor"] == user.ap_id
assert reaction.data["content"] == "👍"
{:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
{:error, _} = CommonAPI.react_with_emoji(activity.id, user, ".")
end
test "unreacting to a status with an emoji" do
user = insert(:user)
other_user = insert(:user)
clear_config([:instance, :federating], true)
with_mock Pleroma.Web.Federator,
publish: fn _ -> nil end do
{:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
{:ok, reaction} = CommonAPI.react_with_emoji(activity.id, user, "👍")
{:ok, unreaction} = CommonAPI.unreact_with_emoji(activity.id, user, "👍")
assert unreaction.data["type"] == "Undo"
assert unreaction.data["object"] == reaction.data["id"]
assert unreaction.local
# On federation, it contains the undone (and deleted) object
unreaction_with_object = %{
unreaction
| data: Map.put(unreaction.data, "object", reaction.data)
}
assert called(Pleroma.Web.Federator.publish(unreaction_with_object))
end
end
test "repeating a status" do
user = insert(:user)
other_user = insert(:user)
{:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
{:ok, %Activity{} = announce_activity} = CommonAPI.repeat(activity.id, user)
assert Visibility.is_public?(announce_activity)
end
test "can't repeat a repeat" do
user = insert(:user)
other_user = insert(:user)
{:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
{:ok, %Activity{} = announce} = CommonAPI.repeat(activity.id, other_user)
refute match?({:ok, %Activity{}}, CommonAPI.repeat(announce.id, user))
end
test "repeating a status privately" do
user = insert(:user)
other_user = insert(:user)
{:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
{:ok, %Activity{} = announce_activity} =
CommonAPI.repeat(activity.id, user, %{visibility: "private"})
assert Visibility.is_private?(announce_activity)
refute Visibility.visible_for_user?(announce_activity, nil)
end
test "favoriting a status" do
user = insert(:user)
other_user = insert(:user)
{:ok, post_activity} = CommonAPI.post(other_user, %{status: "cofe"})
{:ok, %Activity{data: data}} = CommonAPI.favorite(user, post_activity.id)
assert data["type"] == "Like"
assert data["actor"] == user.ap_id
assert data["object"] == post_activity.data["object"]
end
test "retweeting a status twice returns the status" do
user = insert(:user)
other_user = insert(:user)
{:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
{:ok, %Activity{} = announce} = CommonAPI.repeat(activity.id, user)
{:ok, ^announce} = CommonAPI.repeat(activity.id, user)
end
test "favoriting a status twice returns ok, but without the like activity" do
user = insert(:user)
other_user = insert(:user)
{:ok, activity} = CommonAPI.post(other_user, %{status: "cofe"})
{:ok, %Activity{}} = CommonAPI.favorite(user, activity.id)
assert {:ok, :already_liked} = CommonAPI.favorite(user, activity.id)
end
end
describe "pinned statuses" do
setup do
Pleroma.Config.put([:instance, :max_pinned_statuses], 1)
user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{status: "HI!!!"})
[user: user, activity: activity]
end
test "pin status", %{user: user, activity: activity} do
assert {:ok, ^activity} = CommonAPI.pin(activity.id, user)
id = activity.id
user = refresh_record(user)
assert %User{pinned_activities: [^id]} = user
end
test "pin poll", %{user: user} do
{:ok, activity} =
CommonAPI.post(user, %{
status: "How is fediverse today?",
poll: %{options: ["Absolutely outstanding", "Not good"], expires_in: 20}
})
assert {:ok, ^activity} = CommonAPI.pin(activity.id, user)
id = activity.id
user = refresh_record(user)
assert %User{pinned_activities: [^id]} = user
end
test "unlisted statuses can be pinned", %{user: user} do
{:ok, activity} = CommonAPI.post(user, %{status: "HI!!!", visibility: "unlisted"})
assert {:ok, ^activity} = CommonAPI.pin(activity.id, user)
end
test "only self-authored can be pinned", %{activity: activity} do
user = insert(:user)
assert {:error, "Could not pin"} = CommonAPI.pin(activity.id, user)
end
test "max pinned statuses", %{user: user, activity: activity_one} do
{:ok, activity_two} = CommonAPI.post(user, %{status: "HI!!!"})
assert {:ok, ^activity_one} = CommonAPI.pin(activity_one.id, user)
user = refresh_record(user)
assert {:error, "You have already pinned the maximum number of statuses"} =
CommonAPI.pin(activity_two.id, user)
end
test "unpin status", %{user: user, activity: activity} do
{:ok, activity} = CommonAPI.pin(activity.id, user)
user = refresh_record(user)
id = activity.id
assert match?({:ok, %{id: ^id}}, CommonAPI.unpin(activity.id, user))
user = refresh_record(user)
assert %User{pinned_activities: []} = user
end
test "should unpin when deleting a status", %{user: user, activity: activity} do
{:ok, activity} = CommonAPI.pin(activity.id, user)
user = refresh_record(user)
assert {:ok, _} = CommonAPI.delete(activity.id, user)
user = refresh_record(user)
assert %User{pinned_activities: []} = user
end
end
describe "mute tests" do
setup do
user = insert(:user)
activity = insert(:note_activity)
[user: user, activity: activity]
end
test "marks notifications as read after mute" do
author = insert(:user)
activity = insert(:note_activity, user: author)
friend1 = insert(:user)
friend2 = insert(:user)
{:ok, reply_activity} =
CommonAPI.post(
friend2,
%{
status: "@#{author.nickname} @#{friend1.nickname} test reply",
in_reply_to_status_id: activity.id
}
)
{:ok, favorite_activity} = CommonAPI.favorite(friend2, activity.id)
{:ok, repeat_activity} = CommonAPI.repeat(activity.id, friend1)
assert Repo.aggregate(
from(n in Notification, where: n.seen == false and n.user_id == ^friend1.id),
:count
) == 1
unread_notifications =
Repo.all(from(n in Notification, where: n.seen == false, where: n.user_id == ^author.id))
assert Enum.any?(unread_notifications, fn n ->
n.type == "favourite" && n.activity_id == favorite_activity.id
end)
assert Enum.any?(unread_notifications, fn n ->
n.type == "reblog" && n.activity_id == repeat_activity.id
end)
assert Enum.any?(unread_notifications, fn n ->
n.type == "mention" && n.activity_id == reply_activity.id
end)
{:ok, _} = CommonAPI.add_mute(author, activity)
assert CommonAPI.thread_muted?(author, activity)
assert Repo.aggregate(
from(n in Notification, where: n.seen == false and n.user_id == ^friend1.id),
:count
) == 1
read_notifications =
Repo.all(from(n in Notification, where: n.seen == true, where: n.user_id == ^author.id))
assert Enum.any?(read_notifications, fn n ->
n.type == "favourite" && n.activity_id == favorite_activity.id
end)
assert Enum.any?(read_notifications, fn n ->
n.type == "reblog" && n.activity_id == repeat_activity.id
end)
assert Enum.any?(read_notifications, fn n ->
n.type == "mention" && n.activity_id == reply_activity.id
end)
end
test "add mute", %{user: user, activity: activity} do
{:ok, _} = CommonAPI.add_mute(user, activity)
assert CommonAPI.thread_muted?(user, activity)
end
test "remove mute", %{user: user, activity: activity} do
CommonAPI.add_mute(user, activity)
{:ok, _} = CommonAPI.remove_mute(user, activity)
refute CommonAPI.thread_muted?(user, activity)
end
test "check that mutes can't be duplicate", %{user: user, activity: activity} do
CommonAPI.add_mute(user, activity)
{:error, _} = CommonAPI.add_mute(user, activity)
end
end
describe "reports" do
test "creates a report" do
reporter = insert(:user)
target_user = insert(:user)
{:ok, activity} = CommonAPI.post(target_user, %{status: "foobar"})
reporter_ap_id = reporter.ap_id
target_ap_id = target_user.ap_id
activity_ap_id = activity.data["id"]
comment = "foobar"
report_data = %{
account_id: target_user.id,
comment: comment,
status_ids: [activity.id]
}
note_obj = %{
"type" => "Note",
"id" => activity_ap_id,
"content" => "foobar",
"published" => activity.object.data["published"],
"actor" => AccountView.render("show.json", %{user: target_user})
}
assert {:ok, flag_activity} = CommonAPI.report(reporter, report_data)
assert %Activity{
actor: ^reporter_ap_id,
data: %{
"type" => "Flag",
"content" => ^comment,
"object" => [^target_ap_id, ^note_obj],
"state" => "open"
}
} = flag_activity
end
test "updates report state" do
[reporter, target_user] = insert_pair(:user)
activity = insert(:note_activity, user: target_user)
{:ok, %Activity{id: report_id}} =
CommonAPI.report(reporter, %{
account_id: target_user.id,
comment: "I feel offended",
status_ids: [activity.id]
})
{:ok, report} = CommonAPI.update_report_state(report_id, "resolved")
assert report.data["state"] == "resolved"
[reported_user, activity_id] = report.data["object"]
assert reported_user == target_user.ap_id
assert activity_id == activity.data["id"]
end
test "does not update report state when state is unsupported" do
[reporter, target_user] = insert_pair(:user)
activity = insert(:note_activity, user: target_user)
{:ok, %Activity{id: report_id}} =
CommonAPI.report(reporter, %{
account_id: target_user.id,
comment: "I feel offended",
status_ids: [activity.id]
})
assert CommonAPI.update_report_state(report_id, "test") == {:error, "Unsupported state"}
end
test "updates state of multiple reports" do
[reporter, target_user] = insert_pair(:user)
activity = insert(:note_activity, user: target_user)
{:ok, %Activity{id: first_report_id}} =
CommonAPI.report(reporter, %{
account_id: target_user.id,
comment: "I feel offended",
status_ids: [activity.id]
})
{:ok, %Activity{id: second_report_id}} =
CommonAPI.report(reporter, %{
account_id: target_user.id,
comment: "I feel very offended!",
status_ids: [activity.id]
})
{:ok, report_ids} =
CommonAPI.update_report_state([first_report_id, second_report_id], "resolved")
first_report = Activity.get_by_id(first_report_id)
second_report = Activity.get_by_id(second_report_id)
assert report_ids -- [first_report_id, second_report_id] == []
assert first_report.data["state"] == "resolved"
assert second_report.data["state"] == "resolved"
end
end
describe "reblog muting" do
setup do
muter = insert(:user)
muted = insert(:user)
[muter: muter, muted: muted]
end
test "add a reblog mute", %{muter: muter, muted: muted} do
{:ok, _reblog_mute} = CommonAPI.hide_reblogs(muter, muted)
assert User.showing_reblogs?(muter, muted) == false
end
test "remove a reblog mute", %{muter: muter, muted: muted} do
{:ok, _reblog_mute} = CommonAPI.hide_reblogs(muter, muted)
{:ok, _reblog_mute} = CommonAPI.show_reblogs(muter, muted)
assert User.showing_reblogs?(muter, muted) == true
end
end
describe "follow/2" do
test "directly follows a non-locked local user" do
[follower, followed] = insert_pair(:user)
{:ok, follower, followed, _} = CommonAPI.follow(follower, followed)
assert User.following?(follower, followed)
end
end
describe "unfollow/2" do
test "also unsubscribes a user" do
[follower, followed] = insert_pair(:user)
{:ok, follower, followed, _} = CommonAPI.follow(follower, followed)
{:ok, _subscription} = User.subscribe(follower, followed)
assert User.subscribed_to?(follower, followed)
{:ok, follower} = CommonAPI.unfollow(follower, followed)
refute User.subscribed_to?(follower, followed)
end
test "cancels a pending follow for a local user" do
follower = insert(:user)
followed = insert(:user, locked: true)
assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} =
CommonAPI.follow(follower, followed)
assert User.get_follow_state(follower, followed) == :follow_pending
assert {:ok, follower} = CommonAPI.unfollow(follower, followed)
assert User.get_follow_state(follower, followed) == nil
assert %{id: ^activity_id, data: %{"state" => "cancelled"}} =
Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(follower, followed)
assert %{
data: %{
"type" => "Undo",
"object" => %{"type" => "Follow", "state" => "cancelled"}
}
} = Pleroma.Web.ActivityPub.Utils.fetch_latest_undo(follower)
end
test "cancels a pending follow for a remote user" do
follower = insert(:user)
followed = insert(:user, locked: true, local: false, ap_enabled: true)
assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} =
CommonAPI.follow(follower, followed)
assert User.get_follow_state(follower, followed) == :follow_pending
assert {:ok, follower} = CommonAPI.unfollow(follower, followed)
assert User.get_follow_state(follower, followed) == nil
assert %{id: ^activity_id, data: %{"state" => "cancelled"}} =
Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(follower, followed)
assert %{
data: %{
"type" => "Undo",
"object" => %{"type" => "Follow", "state" => "cancelled"}
}
} = Pleroma.Web.ActivityPub.Utils.fetch_latest_undo(follower)
end
end
describe "accept_follow_request/2" do
test "after acceptance, it sets all existing pending follow request states to 'accept'" do
user = insert(:user, locked: true)
follower = insert(:user)
follower_two = insert(:user)
{:ok, _, _, follow_activity} = CommonAPI.follow(follower, user)
{:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user)
{:ok, _, _, follow_activity_three} = CommonAPI.follow(follower_two, user)
assert follow_activity.data["state"] == "pending"
assert follow_activity_two.data["state"] == "pending"
assert follow_activity_three.data["state"] == "pending"
{:ok, _follower} = CommonAPI.accept_follow_request(follower, user)
assert Repo.get(Activity, follow_activity.id).data["state"] == "accept"
assert Repo.get(Activity, follow_activity_two.id).data["state"] == "accept"
assert Repo.get(Activity, follow_activity_three.id).data["state"] == "pending"
end
test "after rejection, it sets all existing pending follow request states to 'reject'" do
user = insert(:user, locked: true)
follower = insert(:user)
follower_two = insert(:user)
{:ok, _, _, follow_activity} = CommonAPI.follow(follower, user)
{:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user)
{:ok, _, _, follow_activity_three} = CommonAPI.follow(follower_two, user)
assert follow_activity.data["state"] == "pending"
assert follow_activity_two.data["state"] == "pending"
assert follow_activity_three.data["state"] == "pending"
{:ok, _follower} = CommonAPI.reject_follow_request(follower, user)
assert Repo.get(Activity, follow_activity.id).data["state"] == "reject"
assert Repo.get(Activity, follow_activity_two.id).data["state"] == "reject"
assert Repo.get(Activity, follow_activity_three.id).data["state"] == "pending"
end
test "doesn't create a following relationship if the corresponding follow request doesn't exist" do
user = insert(:user, locked: true)
not_follower = insert(:user)
CommonAPI.accept_follow_request(not_follower, user)
assert Pleroma.FollowingRelationship.following?(not_follower, user) == false
end
end
describe "vote/3" do
test "does not allow to vote twice" do
user = insert(:user)
other_user = insert(:user)
{:ok, activity} =
CommonAPI.post(user, %{
status: "Am I cute?",
poll: %{options: ["Yes", "No"], expires_in: 20}
})
object = Object.normalize(activity)
{:ok, _, object} = CommonAPI.vote(other_user, object, [0])
assert {:error, "Already voted"} == CommonAPI.vote(other_user, object, [1])
end
end
describe "listen/2" do
test "returns a valid activity" do
user = insert(:user)
{:ok, activity} =
CommonAPI.listen(user, %{
title: "lain radio episode 1",
album: "lain radio",
artist: "lain",
length: 180_000
})
object = Object.normalize(activity)
assert object.data["title"] == "lain radio episode 1"
assert Visibility.get_visibility(activity) == "public"
end
test "respects visibility=private" do
user = insert(:user)
{:ok, activity} =
CommonAPI.listen(user, %{
title: "lain radio episode 1",
album: "lain radio",
artist: "lain",
length: 180_000,
visibility: "private"
})
object = Object.normalize(activity)
assert object.data["title"] == "lain radio episode 1"
assert Visibility.get_visibility(activity) == "private"
end
end
describe "get_user/1" do
test "gets user by ap_id" do
user = insert(:user)
assert CommonAPI.get_user(user.ap_id) == user
end
test "gets user by guessed nickname" do
user = insert(:user, ap_id: "", nickname: "mario@mushroom.kingdom")
assert CommonAPI.get_user("https://mushroom.kingdom/users/mario") == user
end
test "fallback" do
assert %User{
name: "",
ap_id: "",
nickname: "erroruser@example.com"
} = CommonAPI.get_user("")
end
end
end
diff --git a/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs
index f7f1369e4..4ef404541 100644
--- a/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs
+++ b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs
@@ -1,1536 +1,1536 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
use Pleroma.Web.ConnCase
alias Pleroma.Repo
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.InternalFetchActor
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.OAuth.Token
import Pleroma.Factory
describe "account fetching" do
test "works by id" do
%User{id: user_id} = insert(:user)
assert %{"id" => ^user_id} =
build_conn()
|> get("/api/v1/accounts/#{user_id}")
|> json_response_and_validate_schema(200)
assert %{"error" => "Can't find user"} =
build_conn()
|> get("/api/v1/accounts/-1")
|> json_response_and_validate_schema(404)
end
test "works by nickname" do
user = insert(:user)
assert %{"id" => user_id} =
build_conn()
|> get("/api/v1/accounts/#{user.nickname}")
|> json_response_and_validate_schema(200)
end
test "works by nickname for remote users" do
clear_config([:instance, :limit_to_local_content], false)
user = insert(:user, nickname: "user@example.com", local: false)
assert %{"id" => user_id} =
build_conn()
|> get("/api/v1/accounts/#{user.nickname}")
|> json_response_and_validate_schema(200)
end
test "respects limit_to_local_content == :all for remote user nicknames" do
clear_config([:instance, :limit_to_local_content], :all)
user = insert(:user, nickname: "user@example.com", local: false)
assert build_conn()
|> get("/api/v1/accounts/#{user.nickname}")
|> json_response_and_validate_schema(404)
end
test "respects limit_to_local_content == :unauthenticated for remote user nicknames" do
clear_config([:instance, :limit_to_local_content], :unauthenticated)
user = insert(:user, nickname: "user@example.com", local: false)
reading_user = insert(:user)
conn =
build_conn()
|> get("/api/v1/accounts/#{user.nickname}")
assert json_response_and_validate_schema(conn, 404)
conn =
build_conn()
|> assign(:user, reading_user)
|> assign(:token, insert(:oauth_token, user: reading_user, scopes: ["read:accounts"]))
|> get("/api/v1/accounts/#{user.nickname}")
assert %{"id" => id} = json_response_and_validate_schema(conn, 200)
assert id == user.id
end
test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
# Need to set an old-style integer ID to reproduce the problem
# (these are no longer assigned to new accounts but were preserved
# for existing accounts during the migration to flakeIDs)
user_one = insert(:user, %{id: 1212})
user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
acc_one =
conn
|> get("/api/v1/accounts/#{user_one.id}")
|> json_response_and_validate_schema(:ok)
acc_two =
conn
|> get("/api/v1/accounts/#{user_two.nickname}")
|> json_response_and_validate_schema(:ok)
acc_three =
conn
|> get("/api/v1/accounts/#{user_two.id}")
|> json_response_and_validate_schema(:ok)
refute acc_one == acc_two
assert acc_two == acc_three
end
test "returns 404 when user is invisible", %{conn: conn} do
user = insert(:user, %{invisible: true})
assert %{"error" => "Can't find user"} =
conn
|> get("/api/v1/accounts/#{user.nickname}")
|> json_response_and_validate_schema(404)
end
test "returns 404 for internal.fetch actor", %{conn: conn} do
%User{nickname: "internal.fetch"} = InternalFetchActor.get_actor()
assert %{"error" => "Can't find user"} =
conn
|> get("/api/v1/accounts/internal.fetch")
|> json_response_and_validate_schema(404)
end
test "returns 404 for deactivated user", %{conn: conn} do
user = insert(:user, deactivated: true)
assert %{"error" => "Can't find user"} =
conn
|> get("/api/v1/accounts/#{user.id}")
|> json_response_and_validate_schema(:not_found)
end
end
defp local_and_remote_users do
local = insert(:user)
remote = insert(:user, local: false)
{:ok, local: local, remote: remote}
end
describe "user fetching with restrict unauthenticated profiles for local and remote" do
setup do: local_and_remote_users()
setup do: clear_config([:restrict_unauthenticated, :profiles, :local], true)
setup do: clear_config([:restrict_unauthenticated, :profiles, :remote], true)
test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
assert %{"error" => "This API requires an authenticated user"} ==
conn
|> get("/api/v1/accounts/#{local.id}")
|> json_response_and_validate_schema(:unauthorized)
assert %{"error" => "This API requires an authenticated user"} ==
conn
|> get("/api/v1/accounts/#{remote.id}")
|> json_response_and_validate_schema(:unauthorized)
end
test "if user is authenticated", %{local: local, remote: remote} do
%{conn: conn} = oauth_access(["read"])
res_conn = get(conn, "/api/v1/accounts/#{local.id}")
assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
end
end
describe "user fetching with restrict unauthenticated profiles for local" do
setup do: local_and_remote_users()
setup do: clear_config([:restrict_unauthenticated, :profiles, :local], true)
test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
res_conn = get(conn, "/api/v1/accounts/#{local.id}")
assert json_response_and_validate_schema(res_conn, :unauthorized) == %{
"error" => "This API requires an authenticated user"
}
res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
end
test "if user is authenticated", %{local: local, remote: remote} do
%{conn: conn} = oauth_access(["read"])
res_conn = get(conn, "/api/v1/accounts/#{local.id}")
assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
end
end
describe "user fetching with restrict unauthenticated profiles for remote" do
setup do: local_and_remote_users()
setup do: clear_config([:restrict_unauthenticated, :profiles, :remote], true)
test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
res_conn = get(conn, "/api/v1/accounts/#{local.id}")
assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
assert json_response_and_validate_schema(res_conn, :unauthorized) == %{
"error" => "This API requires an authenticated user"
}
end
test "if user is authenticated", %{local: local, remote: remote} do
%{conn: conn} = oauth_access(["read"])
res_conn = get(conn, "/api/v1/accounts/#{local.id}")
assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
end
end
describe "user timelines" do
setup do: oauth_access(["read:statuses"])
test "works with announces that are just addressed to public", %{conn: conn} do
user = insert(:user, ap_id: "https://honktest/u/test", local: false)
other_user = insert(:user)
{:ok, post} = CommonAPI.post(other_user, %{status: "bonkeronk"})
{:ok, announce, _} =
%{
"@context" => "https://www.w3.org/ns/activitystreams",
"actor" => "https://honktest/u/test",
"id" => "https://honktest/u/test/bonk/1793M7B9MQ48847vdx",
"object" => post.data["object"],
"published" => "2019-06-25T19:33:58Z",
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
"type" => "Announce"
}
|> ActivityPub.persist(local: false)
assert resp =
conn
|> get("/api/v1/accounts/#{user.id}/statuses")
|> json_response_and_validate_schema(200)
assert [%{"id" => id}] = resp
assert id == announce.id
end
test "deactivated user", %{conn: conn} do
user = insert(:user, deactivated: true)
assert %{"error" => "Can't find user"} ==
conn
|> get("/api/v1/accounts/#{user.id}/statuses")
|> json_response_and_validate_schema(:not_found)
end
test "returns 404 when user is invisible", %{conn: conn} do
user = insert(:user, %{invisible: true})
assert %{"error" => "Can't find user"} =
conn
|> get("/api/v1/accounts/#{user.id}")
|> json_response_and_validate_schema(404)
end
test "respects blocks", %{user: user_one, conn: conn} do
user_two = insert(:user)
user_three = insert(:user)
User.block(user_one, user_two)
{:ok, activity} = CommonAPI.post(user_two, %{status: "User one sux0rz"})
{:ok, repeat} = CommonAPI.repeat(activity.id, user_three)
assert resp =
conn
|> get("/api/v1/accounts/#{user_two.id}/statuses")
|> json_response_and_validate_schema(200)
assert [%{"id" => id}] = resp
assert id == activity.id
# Even a blocked user will deliver the full user timeline, there would be
# no point in looking at a blocked users timeline otherwise
assert resp =
conn
|> get("/api/v1/accounts/#{user_two.id}/statuses")
|> json_response_and_validate_schema(200)
assert [%{"id" => id}] = resp
assert id == activity.id
# Third user's timeline includes the repeat when viewed by unauthenticated user
resp =
build_conn()
|> get("/api/v1/accounts/#{user_three.id}/statuses")
|> json_response_and_validate_schema(200)
assert [%{"id" => id}] = resp
assert id == repeat.id
# When viewing a third user's timeline, the blocked users' statuses will NOT be shown
resp = get(conn, "/api/v1/accounts/#{user_three.id}/statuses")
assert [] == json_response_and_validate_schema(resp, 200)
end
test "gets users statuses", %{conn: conn} do
user_one = insert(:user)
user_two = insert(:user)
user_three = insert(:user)
{:ok, _user_three} = User.follow(user_three, user_one)
{:ok, activity} = CommonAPI.post(user_one, %{status: "HI!!!"})
{:ok, direct_activity} =
CommonAPI.post(user_one, %{
status: "Hi, @#{user_two.nickname}.",
visibility: "direct"
})
{:ok, private_activity} =
CommonAPI.post(user_one, %{status: "private", visibility: "private"})
# TODO!!!
resp =
conn
|> get("/api/v1/accounts/#{user_one.id}/statuses")
|> json_response_and_validate_schema(200)
assert [%{"id" => id}] = resp
assert id == to_string(activity.id)
resp =
conn
|> assign(:user, user_two)
|> assign(:token, insert(:oauth_token, user: user_two, scopes: ["read:statuses"]))
|> get("/api/v1/accounts/#{user_one.id}/statuses")
|> json_response_and_validate_schema(200)
assert [%{"id" => id_one}, %{"id" => id_two}] = resp
assert id_one == to_string(direct_activity.id)
assert id_two == to_string(activity.id)
resp =
conn
|> assign(:user, user_three)
|> assign(:token, insert(:oauth_token, user: user_three, scopes: ["read:statuses"]))
|> get("/api/v1/accounts/#{user_one.id}/statuses")
|> json_response_and_validate_schema(200)
assert [%{"id" => id_one}, %{"id" => id_two}] = resp
assert id_one == to_string(private_activity.id)
assert id_two == to_string(activity.id)
end
test "unimplemented pinned statuses feature", %{conn: conn} do
note = insert(:note_activity)
user = User.get_cached_by_ap_id(note.data["actor"])
conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?pinned=true")
assert json_response_and_validate_schema(conn, 200) == []
end
test "gets an users media, excludes reblogs", %{conn: conn} do
note = insert(:note_activity)
user = User.get_cached_by_ap_id(note.data["actor"])
other_user = insert(:user)
file = %Plug.Upload{
- content_type: "image/jpg",
+ content_type: "image/jpeg",
path: Path.absname("test/fixtures/image.jpg"),
filename: "an_image.jpg"
}
{:ok, %{id: media_id}} = ActivityPub.upload(file, actor: user.ap_id)
{:ok, %{id: image_post_id}} = CommonAPI.post(user, %{status: "cofe", media_ids: [media_id]})
{:ok, %{id: media_id}} = ActivityPub.upload(file, actor: other_user.ap_id)
{:ok, %{id: other_image_post_id}} =
CommonAPI.post(other_user, %{status: "cofe2", media_ids: [media_id]})
{:ok, _announce} = CommonAPI.repeat(other_image_post_id, user)
conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?only_media=true")
assert [%{"id" => ^image_post_id}] = json_response_and_validate_schema(conn, 200)
conn = get(build_conn(), "/api/v1/accounts/#{user.id}/statuses?only_media=1")
assert [%{"id" => ^image_post_id}] = json_response_and_validate_schema(conn, 200)
end
test "gets a user's statuses without reblogs", %{user: user, conn: conn} do
{:ok, %{id: post_id}} = CommonAPI.post(user, %{status: "HI!!!"})
{:ok, _} = CommonAPI.repeat(post_id, user)
conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?exclude_reblogs=true")
assert [%{"id" => ^post_id}] = json_response_and_validate_schema(conn, 200)
conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?exclude_reblogs=1")
assert [%{"id" => ^post_id}] = json_response_and_validate_schema(conn, 200)
end
test "filters user's statuses by a hashtag", %{user: user, conn: conn} do
{:ok, %{id: post_id}} = CommonAPI.post(user, %{status: "#hashtag"})
{:ok, _post} = CommonAPI.post(user, %{status: "hashtag"})
conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?tagged=hashtag")
assert [%{"id" => ^post_id}] = json_response_and_validate_schema(conn, 200)
end
test "the user views their own timelines and excludes direct messages", %{
user: user,
conn: conn
} do
{:ok, %{id: public_activity_id}} =
CommonAPI.post(user, %{status: ".", visibility: "public"})
{:ok, _direct_activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"})
conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?exclude_visibilities[]=direct")
assert [%{"id" => ^public_activity_id}] = json_response_and_validate_schema(conn, 200)
end
end
defp local_and_remote_activities(%{local: local, remote: remote}) do
insert(:note_activity, user: local)
insert(:note_activity, user: remote, local: false)
:ok
end
describe "statuses with restrict unauthenticated profiles for local and remote" do
setup do: local_and_remote_users()
setup :local_and_remote_activities
setup do: clear_config([:restrict_unauthenticated, :profiles, :local], true)
setup do: clear_config([:restrict_unauthenticated, :profiles, :remote], true)
test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
assert %{"error" => "This API requires an authenticated user"} ==
conn
|> get("/api/v1/accounts/#{local.id}/statuses")
|> json_response_and_validate_schema(:unauthorized)
assert %{"error" => "This API requires an authenticated user"} ==
conn
|> get("/api/v1/accounts/#{remote.id}/statuses")
|> json_response_and_validate_schema(:unauthorized)
end
test "if user is authenticated", %{local: local, remote: remote} do
%{conn: conn} = oauth_access(["read"])
res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses")
assert length(json_response_and_validate_schema(res_conn, 200)) == 1
res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses")
assert length(json_response_and_validate_schema(res_conn, 200)) == 1
end
end
describe "statuses with restrict unauthenticated profiles for local" do
setup do: local_and_remote_users()
setup :local_and_remote_activities
setup do: clear_config([:restrict_unauthenticated, :profiles, :local], true)
test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
assert %{"error" => "This API requires an authenticated user"} ==
conn
|> get("/api/v1/accounts/#{local.id}/statuses")
|> json_response_and_validate_schema(:unauthorized)
res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses")
assert length(json_response_and_validate_schema(res_conn, 200)) == 1
end
test "if user is authenticated", %{local: local, remote: remote} do
%{conn: conn} = oauth_access(["read"])
res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses")
assert length(json_response_and_validate_schema(res_conn, 200)) == 1
res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses")
assert length(json_response_and_validate_schema(res_conn, 200)) == 1
end
end
describe "statuses with restrict unauthenticated profiles for remote" do
setup do: local_and_remote_users()
setup :local_and_remote_activities
setup do: clear_config([:restrict_unauthenticated, :profiles, :remote], true)
test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses")
assert length(json_response_and_validate_schema(res_conn, 200)) == 1
assert %{"error" => "This API requires an authenticated user"} ==
conn
|> get("/api/v1/accounts/#{remote.id}/statuses")
|> json_response_and_validate_schema(:unauthorized)
end
test "if user is authenticated", %{local: local, remote: remote} do
%{conn: conn} = oauth_access(["read"])
res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses")
assert length(json_response_and_validate_schema(res_conn, 200)) == 1
res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses")
assert length(json_response_and_validate_schema(res_conn, 200)) == 1
end
end
describe "followers" do
setup do: oauth_access(["read:accounts"])
test "getting followers", %{user: user, conn: conn} do
other_user = insert(:user)
{:ok, %{id: user_id}} = User.follow(user, other_user)
conn = get(conn, "/api/v1/accounts/#{other_user.id}/followers")
assert [%{"id" => ^user_id}] = json_response_and_validate_schema(conn, 200)
end
test "getting followers, hide_followers", %{user: user, conn: conn} do
other_user = insert(:user, hide_followers: true)
{:ok, _user} = User.follow(user, other_user)
conn = get(conn, "/api/v1/accounts/#{other_user.id}/followers")
assert [] == json_response_and_validate_schema(conn, 200)
end
test "getting followers, hide_followers, same user requesting" do
user = insert(:user)
other_user = insert(:user, hide_followers: true)
{:ok, _user} = User.follow(user, other_user)
conn =
build_conn()
|> assign(:user, other_user)
|> assign(:token, insert(:oauth_token, user: other_user, scopes: ["read:accounts"]))
|> get("/api/v1/accounts/#{other_user.id}/followers")
refute [] == json_response_and_validate_schema(conn, 200)
end
test "getting followers, pagination", %{user: user, conn: conn} do
{:ok, %User{id: follower1_id}} = :user |> insert() |> User.follow(user)
{:ok, %User{id: follower2_id}} = :user |> insert() |> User.follow(user)
{:ok, %User{id: follower3_id}} = :user |> insert() |> User.follow(user)
assert [%{"id" => ^follower3_id}, %{"id" => ^follower2_id}] =
conn
|> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1_id}")
|> json_response_and_validate_schema(200)
assert [%{"id" => ^follower2_id}, %{"id" => ^follower1_id}] =
conn
|> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3_id}")
|> json_response_and_validate_schema(200)
assert [%{"id" => ^follower2_id}, %{"id" => ^follower1_id}] =
conn
|> get(
"/api/v1/accounts/#{user.id}/followers?id=#{user.id}&limit=20&max_id=#{
follower3_id
}"
)
|> json_response_and_validate_schema(200)
res_conn = get(conn, "/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3_id}")
assert [%{"id" => ^follower2_id}] = json_response_and_validate_schema(res_conn, 200)
assert [link_header] = get_resp_header(res_conn, "link")
assert link_header =~ ~r/min_id=#{follower2_id}/
assert link_header =~ ~r/max_id=#{follower2_id}/
end
end
describe "following" do
setup do: oauth_access(["read:accounts"])
test "getting following", %{user: user, conn: conn} do
other_user = insert(:user)
{:ok, user} = User.follow(user, other_user)
conn = get(conn, "/api/v1/accounts/#{user.id}/following")
assert [%{"id" => id}] = json_response_and_validate_schema(conn, 200)
assert id == to_string(other_user.id)
end
test "getting following, hide_follows, other user requesting" do
user = insert(:user, hide_follows: true)
other_user = insert(:user)
{:ok, user} = User.follow(user, other_user)
conn =
build_conn()
|> assign(:user, other_user)
|> assign(:token, insert(:oauth_token, user: other_user, scopes: ["read:accounts"]))
|> get("/api/v1/accounts/#{user.id}/following")
assert [] == json_response_and_validate_schema(conn, 200)
end
test "getting following, hide_follows, same user requesting" do
user = insert(:user, hide_follows: true)
other_user = insert(:user)
{:ok, user} = User.follow(user, other_user)
conn =
build_conn()
|> assign(:user, user)
|> assign(:token, insert(:oauth_token, user: user, scopes: ["read:accounts"]))
|> get("/api/v1/accounts/#{user.id}/following")
refute [] == json_response_and_validate_schema(conn, 200)
end
test "getting following, pagination", %{user: user, conn: conn} do
following1 = insert(:user)
following2 = insert(:user)
following3 = insert(:user)
{:ok, _} = User.follow(user, following1)
{:ok, _} = User.follow(user, following2)
{:ok, _} = User.follow(user, following3)
res_conn = get(conn, "/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
assert [%{"id" => id3}, %{"id" => id2}] = json_response_and_validate_schema(res_conn, 200)
assert id3 == following3.id
assert id2 == following2.id
res_conn = get(conn, "/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
assert [%{"id" => id2}, %{"id" => id1}] = json_response_and_validate_schema(res_conn, 200)
assert id2 == following2.id
assert id1 == following1.id
res_conn =
get(
conn,
"/api/v1/accounts/#{user.id}/following?id=#{user.id}&limit=20&max_id=#{following3.id}"
)
assert [%{"id" => id2}, %{"id" => id1}] = json_response_and_validate_schema(res_conn, 200)
assert id2 == following2.id
assert id1 == following1.id
res_conn =
get(conn, "/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
assert [%{"id" => id2}] = json_response_and_validate_schema(res_conn, 200)
assert id2 == following2.id
assert [link_header] = get_resp_header(res_conn, "link")
assert link_header =~ ~r/min_id=#{following2.id}/
assert link_header =~ ~r/max_id=#{following2.id}/
end
end
describe "follow/unfollow" do
setup do: oauth_access(["follow"])
test "following / unfollowing a user", %{conn: conn} do
%{id: other_user_id, nickname: other_user_nickname} = insert(:user)
assert %{"id" => _id, "following" => true} =
conn
|> post("/api/v1/accounts/#{other_user_id}/follow")
|> json_response_and_validate_schema(200)
assert %{"id" => _id, "following" => false} =
conn
|> post("/api/v1/accounts/#{other_user_id}/unfollow")
|> json_response_and_validate_schema(200)
assert %{"id" => ^other_user_id} =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/follows", %{"uri" => other_user_nickname})
|> json_response_and_validate_schema(200)
end
test "cancelling follow request", %{conn: conn} do
%{id: other_user_id} = insert(:user, %{locked: true})
assert %{"id" => ^other_user_id, "following" => false, "requested" => true} =
conn
|> post("/api/v1/accounts/#{other_user_id}/follow")
|> json_response_and_validate_schema(:ok)
assert %{"id" => ^other_user_id, "following" => false, "requested" => false} =
conn
|> post("/api/v1/accounts/#{other_user_id}/unfollow")
|> json_response_and_validate_schema(:ok)
end
test "following without reblogs" do
%{conn: conn} = oauth_access(["follow", "read:statuses"])
followed = insert(:user)
other_user = insert(:user)
ret_conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/accounts/#{followed.id}/follow", %{reblogs: false})
assert %{"showing_reblogs" => false} = json_response_and_validate_schema(ret_conn, 200)
{:ok, activity} = CommonAPI.post(other_user, %{status: "hey"})
{:ok, %{id: reblog_id}} = CommonAPI.repeat(activity.id, followed)
assert [] ==
conn
|> get("/api/v1/timelines/home")
|> json_response(200)
assert %{"showing_reblogs" => true} =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/accounts/#{followed.id}/follow", %{reblogs: true})
|> json_response_and_validate_schema(200)
assert [%{"id" => ^reblog_id}] =
conn
|> get("/api/v1/timelines/home")
|> json_response(200)
end
test "following with reblogs" do
%{conn: conn} = oauth_access(["follow", "read:statuses"])
followed = insert(:user)
other_user = insert(:user)
ret_conn = post(conn, "/api/v1/accounts/#{followed.id}/follow")
assert %{"showing_reblogs" => true} = json_response_and_validate_schema(ret_conn, 200)
{:ok, activity} = CommonAPI.post(other_user, %{status: "hey"})
{:ok, %{id: reblog_id}} = CommonAPI.repeat(activity.id, followed)
assert [%{"id" => ^reblog_id}] =
conn
|> get("/api/v1/timelines/home")
|> json_response(200)
assert %{"showing_reblogs" => false} =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/accounts/#{followed.id}/follow", %{reblogs: false})
|> json_response_and_validate_schema(200)
assert [] ==
conn
|> get("/api/v1/timelines/home")
|> json_response(200)
end
test "following / unfollowing errors", %{user: user, conn: conn} do
# self follow
conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
assert %{"error" => "Can not follow yourself"} =
json_response_and_validate_schema(conn_res, 400)
# self unfollow
user = User.get_cached_by_id(user.id)
conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
assert %{"error" => "Can not unfollow yourself"} =
json_response_and_validate_schema(conn_res, 400)
# self follow via uri
user = User.get_cached_by_id(user.id)
assert %{"error" => "Can not follow yourself"} =
conn
|> put_req_header("content-type", "multipart/form-data")
|> post("/api/v1/follows", %{"uri" => user.nickname})
|> json_response_and_validate_schema(400)
# follow non existing user
conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn_res, 404)
# follow non existing user via uri
conn_res =
conn
|> put_req_header("content-type", "multipart/form-data")
|> post("/api/v1/follows", %{"uri" => "doesntexist"})
assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn_res, 404)
# unfollow non existing user
conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn_res, 404)
end
end
describe "mute/unmute" do
setup do: oauth_access(["write:mutes"])
test "with notifications", %{conn: conn} do
other_user = insert(:user)
assert %{"id" => _id, "muting" => true, "muting_notifications" => true} =
conn
|> post("/api/v1/accounts/#{other_user.id}/mute")
|> json_response_and_validate_schema(200)
conn = post(conn, "/api/v1/accounts/#{other_user.id}/unmute")
assert %{"id" => _id, "muting" => false, "muting_notifications" => false} =
json_response_and_validate_schema(conn, 200)
end
test "without notifications", %{conn: conn} do
other_user = insert(:user)
ret_conn =
conn
|> put_req_header("content-type", "multipart/form-data")
|> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
assert %{"id" => _id, "muting" => true, "muting_notifications" => false} =
json_response_and_validate_schema(ret_conn, 200)
conn = post(conn, "/api/v1/accounts/#{other_user.id}/unmute")
assert %{"id" => _id, "muting" => false, "muting_notifications" => false} =
json_response_and_validate_schema(conn, 200)
end
end
describe "pinned statuses" do
setup do
user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{status: "HI!!!"})
%{conn: conn} = oauth_access(["read:statuses"], user: user)
[conn: conn, user: user, activity: activity]
end
test "returns pinned statuses", %{conn: conn, user: user, activity: %{id: activity_id}} do
{:ok, _} = CommonAPI.pin(activity_id, user)
assert [%{"id" => ^activity_id, "pinned" => true}] =
conn
|> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
|> json_response_and_validate_schema(200)
end
end
test "blocking / unblocking a user" do
%{conn: conn} = oauth_access(["follow"])
other_user = insert(:user)
ret_conn = post(conn, "/api/v1/accounts/#{other_user.id}/block")
assert %{"id" => _id, "blocking" => true} = json_response_and_validate_schema(ret_conn, 200)
conn = post(conn, "/api/v1/accounts/#{other_user.id}/unblock")
assert %{"id" => _id, "blocking" => false} = json_response_and_validate_schema(conn, 200)
end
describe "create account by app" do
setup do
valid_params = %{
username: "lain",
email: "lain@example.org",
password: "PlzDontHackLain",
agreement: true
}
[valid_params: valid_params]
end
test "registers and logs in without :account_activation_required / :account_approval_required",
%{conn: conn} do
clear_config([:instance, :account_activation_required], false)
clear_config([:instance, :account_approval_required], false)
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/apps", %{
client_name: "client_name",
redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
scopes: "read, write, follow"
})
assert %{
"client_id" => client_id,
"client_secret" => client_secret,
"id" => _,
"name" => "client_name",
"redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
"vapid_key" => _,
"website" => nil
} = json_response_and_validate_schema(conn, 200)
conn =
post(conn, "/oauth/token", %{
grant_type: "client_credentials",
client_id: client_id,
client_secret: client_secret
})
assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
json_response(conn, 200)
assert token
token_from_db = Repo.get_by(Token, token: token)
assert token_from_db
assert refresh
assert scope == "read write follow"
clear_config([User, :email_blacklist], ["example.org"])
params = %{
username: "lain",
email: "lain@example.org",
password: "PlzDontHackLain",
bio: "Test Bio",
agreement: true
}
conn =
build_conn()
|> put_req_header("content-type", "multipart/form-data")
|> put_req_header("authorization", "Bearer " <> token)
|> post("/api/v1/accounts", params)
assert %{"error" => "{\"email\":[\"Invalid email\"]}"} =
json_response_and_validate_schema(conn, 400)
Pleroma.Config.put([User, :email_blacklist], [])
conn =
build_conn()
|> put_req_header("content-type", "multipart/form-data")
|> put_req_header("authorization", "Bearer " <> token)
|> post("/api/v1/accounts", params)
%{
"access_token" => token,
"created_at" => _created_at,
"scope" => ^scope,
"token_type" => "Bearer"
} = json_response_and_validate_schema(conn, 200)
token_from_db = Repo.get_by(Token, token: token)
assert token_from_db
user = Repo.preload(token_from_db, :user).user
assert user
refute user.confirmation_pending
refute user.approval_pending
end
test "registers but does not log in with :account_activation_required", %{conn: conn} do
clear_config([:instance, :account_activation_required], true)
clear_config([:instance, :account_approval_required], false)
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/apps", %{
client_name: "client_name",
redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
scopes: "read, write, follow"
})
assert %{
"client_id" => client_id,
"client_secret" => client_secret,
"id" => _,
"name" => "client_name",
"redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
"vapid_key" => _,
"website" => nil
} = json_response_and_validate_schema(conn, 200)
conn =
post(conn, "/oauth/token", %{
grant_type: "client_credentials",
client_id: client_id,
client_secret: client_secret
})
assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
json_response(conn, 200)
assert token
token_from_db = Repo.get_by(Token, token: token)
assert token_from_db
assert refresh
assert scope == "read write follow"
conn =
build_conn()
|> put_req_header("content-type", "multipart/form-data")
|> put_req_header("authorization", "Bearer " <> token)
|> post("/api/v1/accounts", %{
username: "lain",
email: "lain@example.org",
password: "PlzDontHackLain",
bio: "Test Bio",
agreement: true
})
response = json_response_and_validate_schema(conn, 200)
assert %{"identifier" => "missing_confirmed_email"} = response
refute response["access_token"]
refute response["token_type"]
user = Repo.get_by(User, email: "lain@example.org")
assert user.confirmation_pending
end
test "registers but does not log in with :account_approval_required", %{conn: conn} do
clear_config([:instance, :account_approval_required], true)
clear_config([:instance, :account_activation_required], false)
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/apps", %{
client_name: "client_name",
redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
scopes: "read, write, follow"
})
assert %{
"client_id" => client_id,
"client_secret" => client_secret,
"id" => _,
"name" => "client_name",
"redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
"vapid_key" => _,
"website" => nil
} = json_response_and_validate_schema(conn, 200)
conn =
post(conn, "/oauth/token", %{
grant_type: "client_credentials",
client_id: client_id,
client_secret: client_secret
})
assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
json_response(conn, 200)
assert token
token_from_db = Repo.get_by(Token, token: token)
assert token_from_db
assert refresh
assert scope == "read write follow"
conn =
build_conn()
|> put_req_header("content-type", "multipart/form-data")
|> put_req_header("authorization", "Bearer " <> token)
|> post("/api/v1/accounts", %{
username: "lain",
email: "lain@example.org",
password: "PlzDontHackLain",
bio: "Test Bio",
agreement: true,
reason: "I'm a cool dude, bro"
})
response = json_response_and_validate_schema(conn, 200)
assert %{"identifier" => "awaiting_approval"} = response
refute response["access_token"]
refute response["token_type"]
user = Repo.get_by(User, email: "lain@example.org")
assert user.approval_pending
assert user.registration_reason == "I'm a cool dude, bro"
end
test "returns error when user already registred", %{conn: conn, valid_params: valid_params} do
_user = insert(:user, email: "lain@example.org")
app_token = insert(:oauth_token, user: nil)
res =
conn
|> put_req_header("authorization", "Bearer " <> app_token.token)
|> put_req_header("content-type", "application/json")
|> post("/api/v1/accounts", valid_params)
assert json_response_and_validate_schema(res, 400) == %{
"error" => "{\"email\":[\"has already been taken\"]}"
}
end
test "returns bad_request if missing required params", %{
conn: conn,
valid_params: valid_params
} do
app_token = insert(:oauth_token, user: nil)
conn =
conn
|> put_req_header("authorization", "Bearer " <> app_token.token)
|> put_req_header("content-type", "application/json")
res = post(conn, "/api/v1/accounts", valid_params)
assert json_response_and_validate_schema(res, 200)
[{127, 0, 0, 1}, {127, 0, 0, 2}, {127, 0, 0, 3}, {127, 0, 0, 4}]
|> Stream.zip(Map.delete(valid_params, :email))
|> Enum.each(fn {ip, {attr, _}} ->
res =
conn
|> Map.put(:remote_ip, ip)
|> post("/api/v1/accounts", Map.delete(valid_params, attr))
|> json_response_and_validate_schema(400)
assert res == %{
"error" => "Missing field: #{attr}.",
"errors" => [
%{
"message" => "Missing field: #{attr}",
"source" => %{"pointer" => "/#{attr}"},
"title" => "Invalid value"
}
]
}
end)
end
test "returns bad_request if missing email params when :account_activation_required is enabled",
%{conn: conn, valid_params: valid_params} do
clear_config([:instance, :account_activation_required], true)
app_token = insert(:oauth_token, user: nil)
conn =
conn
|> put_req_header("authorization", "Bearer " <> app_token.token)
|> put_req_header("content-type", "application/json")
res =
conn
|> Map.put(:remote_ip, {127, 0, 0, 5})
|> post("/api/v1/accounts", Map.delete(valid_params, :email))
assert json_response_and_validate_schema(res, 400) ==
%{"error" => "Missing parameter: email"}
res =
conn
|> Map.put(:remote_ip, {127, 0, 0, 6})
|> post("/api/v1/accounts", Map.put(valid_params, :email, ""))
assert json_response_and_validate_schema(res, 400) == %{
"error" => "{\"email\":[\"can't be blank\"]}"
}
end
test "allow registration without an email", %{conn: conn, valid_params: valid_params} do
app_token = insert(:oauth_token, user: nil)
conn = put_req_header(conn, "authorization", "Bearer " <> app_token.token)
res =
conn
|> put_req_header("content-type", "application/json")
|> Map.put(:remote_ip, {127, 0, 0, 7})
|> post("/api/v1/accounts", Map.delete(valid_params, :email))
assert json_response_and_validate_schema(res, 200)
end
test "allow registration with an empty email", %{conn: conn, valid_params: valid_params} do
app_token = insert(:oauth_token, user: nil)
conn = put_req_header(conn, "authorization", "Bearer " <> app_token.token)
res =
conn
|> put_req_header("content-type", "application/json")
|> Map.put(:remote_ip, {127, 0, 0, 8})
|> post("/api/v1/accounts", Map.put(valid_params, :email, ""))
assert json_response_and_validate_schema(res, 200)
end
test "returns forbidden if token is invalid", %{conn: conn, valid_params: valid_params} do
res =
conn
|> put_req_header("authorization", "Bearer " <> "invalid-token")
|> put_req_header("content-type", "multipart/form-data")
|> post("/api/v1/accounts", valid_params)
assert json_response_and_validate_schema(res, 403) == %{"error" => "Invalid credentials"}
end
test "registration from trusted app" do
clear_config([Pleroma.Captcha, :enabled], true)
app = insert(:oauth_app, trusted: true, scopes: ["read", "write", "follow", "push"])
conn =
build_conn()
|> post("/oauth/token", %{
"grant_type" => "client_credentials",
"client_id" => app.client_id,
"client_secret" => app.client_secret
})
assert %{"access_token" => token, "token_type" => "Bearer"} = json_response(conn, 200)
response =
build_conn()
|> Plug.Conn.put_req_header("authorization", "Bearer " <> token)
|> put_req_header("content-type", "multipart/form-data")
|> post("/api/v1/accounts", %{
nickname: "nickanme",
agreement: true,
email: "email@example.com",
fullname: "Lain",
username: "Lain",
password: "some_password",
confirm: "some_password"
})
|> json_response_and_validate_schema(200)
assert %{
"access_token" => access_token,
"created_at" => _,
"scope" => "read write follow push",
"token_type" => "Bearer"
} = response
response =
build_conn()
|> Plug.Conn.put_req_header("authorization", "Bearer " <> access_token)
|> get("/api/v1/accounts/verify_credentials")
|> json_response_and_validate_schema(200)
assert %{
"acct" => "Lain",
"bot" => false,
"display_name" => "Lain",
"follow_requests_count" => 0,
"followers_count" => 0,
"following_count" => 0,
"locked" => false,
"note" => "",
"source" => %{
"fields" => [],
"note" => "",
"pleroma" => %{
"actor_type" => "Person",
"discoverable" => false,
"no_rich_text" => false,
"show_role" => true
},
"privacy" => "public",
"sensitive" => false
},
"statuses_count" => 0,
"username" => "Lain"
} = response
end
end
describe "create account by app / rate limit" do
setup do: clear_config([:rate_limit, :app_account_creation], {10_000, 2})
test "respects rate limit setting", %{conn: conn} do
app_token = insert(:oauth_token, user: nil)
conn =
conn
|> put_req_header("authorization", "Bearer " <> app_token.token)
|> Map.put(:remote_ip, {15, 15, 15, 15})
|> put_req_header("content-type", "multipart/form-data")
for i <- 1..2 do
conn =
conn
|> post("/api/v1/accounts", %{
username: "#{i}lain",
email: "#{i}lain@example.org",
password: "PlzDontHackLain",
agreement: true
})
%{
"access_token" => token,
"created_at" => _created_at,
"scope" => _scope,
"token_type" => "Bearer"
} = json_response_and_validate_schema(conn, 200)
token_from_db = Repo.get_by(Token, token: token)
assert token_from_db
token_from_db = Repo.preload(token_from_db, :user)
assert token_from_db.user
end
conn =
post(conn, "/api/v1/accounts", %{
username: "6lain",
email: "6lain@example.org",
password: "PlzDontHackLain",
agreement: true
})
assert json_response_and_validate_schema(conn, :too_many_requests) == %{
"error" => "Throttled"
}
end
end
describe "create account with enabled captcha" do
setup %{conn: conn} do
app_token = insert(:oauth_token, user: nil)
conn =
conn
|> put_req_header("authorization", "Bearer " <> app_token.token)
|> put_req_header("content-type", "multipart/form-data")
[conn: conn]
end
setup do: clear_config([Pleroma.Captcha, :enabled], true)
test "creates an account and returns 200 if captcha is valid", %{conn: conn} do
%{token: token, answer_data: answer_data} = Pleroma.Captcha.new()
params = %{
username: "lain",
email: "lain@example.org",
password: "PlzDontHackLain",
agreement: true,
captcha_solution: Pleroma.Captcha.Mock.solution(),
captcha_token: token,
captcha_answer_data: answer_data
}
assert %{
"access_token" => access_token,
"created_at" => _,
"scope" => "read",
"token_type" => "Bearer"
} =
conn
|> post("/api/v1/accounts", params)
|> json_response_and_validate_schema(:ok)
assert Token |> Repo.get_by(token: access_token) |> Repo.preload(:user) |> Map.get(:user)
Cachex.del(:used_captcha_cache, token)
end
test "returns 400 if any captcha field is not provided", %{conn: conn} do
captcha_fields = [:captcha_solution, :captcha_token, :captcha_answer_data]
valid_params = %{
username: "lain",
email: "lain@example.org",
password: "PlzDontHackLain",
agreement: true,
captcha_solution: "xx",
captcha_token: "xx",
captcha_answer_data: "xx"
}
for field <- captcha_fields do
expected = %{
"error" => "{\"captcha\":[\"Invalid CAPTCHA (Missing parameter: #{field})\"]}"
}
assert expected ==
conn
|> post("/api/v1/accounts", Map.delete(valid_params, field))
|> json_response_and_validate_schema(:bad_request)
end
end
test "returns an error if captcha is invalid", %{conn: conn} do
params = %{
username: "lain",
email: "lain@example.org",
password: "PlzDontHackLain",
agreement: true,
captcha_solution: "cofe",
captcha_token: "cofe",
captcha_answer_data: "cofe"
}
assert %{"error" => "{\"captcha\":[\"Invalid answer data\"]}"} ==
conn
|> post("/api/v1/accounts", params)
|> json_response_and_validate_schema(:bad_request)
end
end
describe "GET /api/v1/accounts/:id/lists - account_lists" do
test "returns lists to which the account belongs" do
%{user: user, conn: conn} = oauth_access(["read:lists"])
other_user = insert(:user)
assert {:ok, %Pleroma.List{id: list_id} = list} = Pleroma.List.create("Test List", user)
{:ok, %{following: _following}} = Pleroma.List.follow(list, other_user)
assert [%{"id" => list_id, "title" => "Test List"}] =
conn
|> get("/api/v1/accounts/#{other_user.id}/lists")
|> json_response_and_validate_schema(200)
end
end
describe "verify_credentials" do
test "verify_credentials" do
%{user: user, conn: conn} = oauth_access(["read:accounts"])
[notification | _] =
insert_list(7, :notification, user: user, activity: insert(:note_activity))
Pleroma.Notification.set_read_up_to(user, notification.id)
conn = get(conn, "/api/v1/accounts/verify_credentials")
response = json_response_and_validate_schema(conn, 200)
assert %{"id" => id, "source" => %{"privacy" => "public"}} = response
assert response["pleroma"]["chat_token"]
assert response["pleroma"]["unread_notifications_count"] == 6
assert id == to_string(user.id)
end
test "verify_credentials default scope unlisted" do
user = insert(:user, default_scope: "unlisted")
%{conn: conn} = oauth_access(["read:accounts"], user: user)
conn = get(conn, "/api/v1/accounts/verify_credentials")
assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} =
json_response_and_validate_schema(conn, 200)
assert id == to_string(user.id)
end
test "locked accounts" do
user = insert(:user, default_scope: "private")
%{conn: conn} = oauth_access(["read:accounts"], user: user)
conn = get(conn, "/api/v1/accounts/verify_credentials")
assert %{"id" => id, "source" => %{"privacy" => "private"}} =
json_response_and_validate_schema(conn, 200)
assert id == to_string(user.id)
end
end
describe "user relationships" do
setup do: oauth_access(["read:follows"])
test "returns the relationships for the current user", %{user: user, conn: conn} do
%{id: other_user_id} = other_user = insert(:user)
{:ok, _user} = User.follow(user, other_user)
assert [%{"id" => ^other_user_id}] =
conn
|> get("/api/v1/accounts/relationships?id=#{other_user.id}")
|> json_response_and_validate_schema(200)
assert [%{"id" => ^other_user_id}] =
conn
|> get("/api/v1/accounts/relationships?id[]=#{other_user.id}")
|> json_response_and_validate_schema(200)
end
test "returns an empty list on a bad request", %{conn: conn} do
conn = get(conn, "/api/v1/accounts/relationships", %{})
assert [] = json_response_and_validate_schema(conn, 200)
end
end
test "getting a list of mutes" do
%{user: user, conn: conn} = oauth_access(["read:mutes"])
other_user = insert(:user)
{:ok, _user_relationships} = User.mute(user, other_user)
conn = get(conn, "/api/v1/mutes")
other_user_id = to_string(other_user.id)
assert [%{"id" => ^other_user_id}] = json_response_and_validate_schema(conn, 200)
end
test "getting a list of blocks" do
%{user: user, conn: conn} = oauth_access(["read:blocks"])
other_user = insert(:user)
{:ok, _user_relationship} = User.block(user, other_user)
conn =
conn
|> assign(:user, user)
|> get("/api/v1/blocks")
other_user_id = to_string(other_user.id)
assert [%{"id" => ^other_user_id}] = json_response_and_validate_schema(conn, 200)
end
end
diff --git a/test/pleroma/web/mastodon_api/controllers/media_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/media_controller_test.exs
index 906fd940f..d2bd57515 100644
--- a/test/pleroma/web/mastodon_api/controllers/media_controller_test.exs
+++ b/test/pleroma/web/mastodon_api/controllers/media_controller_test.exs
@@ -1,146 +1,146 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.MediaControllerTest do
use Pleroma.Web.ConnCase
alias Pleroma.Object
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
describe "Upload media" do
setup do: oauth_access(["write:media"])
setup do
image = %Plug.Upload{
- content_type: "image/jpg",
+ content_type: "image/jpeg",
path: Path.absname("test/fixtures/image.jpg"),
filename: "an_image.jpg"
}
[image: image]
end
setup do: clear_config([:media_proxy])
setup do: clear_config([Pleroma.Upload])
test "/api/v1/media", %{conn: conn, image: image} do
desc = "Description of the image"
media =
conn
|> put_req_header("content-type", "multipart/form-data")
|> post("/api/v1/media", %{"file" => image, "description" => desc})
|> json_response_and_validate_schema(:ok)
assert media["type"] == "image"
assert media["description"] == desc
assert media["id"]
object = Object.get_by_id(media["id"])
assert object.data["actor"] == User.ap_id(conn.assigns[:user])
end
test "/api/v2/media", %{conn: conn, user: user, image: image} do
desc = "Description of the image"
response =
conn
|> put_req_header("content-type", "multipart/form-data")
|> post("/api/v2/media", %{"file" => image, "description" => desc})
|> json_response_and_validate_schema(202)
assert media_id = response["id"]
%{conn: conn} = oauth_access(["read:media"], user: user)
media =
conn
|> get("/api/v1/media/#{media_id}")
|> json_response_and_validate_schema(200)
assert media["type"] == "image"
assert media["description"] == desc
assert media["id"]
object = Object.get_by_id(media["id"])
assert object.data["actor"] == user.ap_id
end
end
describe "Update media description" do
setup do: oauth_access(["write:media"])
setup %{user: actor} do
file = %Plug.Upload{
- content_type: "image/jpg",
+ content_type: "image/jpeg",
path: Path.absname("test/fixtures/image.jpg"),
filename: "an_image.jpg"
}
{:ok, %Object{} = object} =
ActivityPub.upload(
file,
actor: User.ap_id(actor),
description: "test-m"
)
[object: object]
end
test "/api/v1/media/:id good request", %{conn: conn, object: object} do
media =
conn
|> put_req_header("content-type", "multipart/form-data")
|> put("/api/v1/media/#{object.id}", %{"description" => "test-media"})
|> json_response_and_validate_schema(:ok)
assert media["description"] == "test-media"
assert refresh_record(object).data["name"] == "test-media"
end
end
describe "Get media by id (/api/v1/media/:id)" do
setup do: oauth_access(["read:media"])
setup %{user: actor} do
file = %Plug.Upload{
- content_type: "image/jpg",
+ content_type: "image/jpeg",
path: Path.absname("test/fixtures/image.jpg"),
filename: "an_image.jpg"
}
{:ok, %Object{} = object} =
ActivityPub.upload(
file,
actor: User.ap_id(actor),
description: "test-media"
)
[object: object]
end
test "it returns media object when requested by owner", %{conn: conn, object: object} do
media =
conn
|> get("/api/v1/media/#{object.id}")
|> json_response_and_validate_schema(:ok)
assert media["description"] == "test-media"
assert media["type"] == "image"
assert media["id"]
end
test "it returns 403 if media object requested by non-owner", %{object: object, user: user} do
%{conn: conn, user: other_user} = oauth_access(["read:media"])
assert object.data["actor"] == user.ap_id
refute user.id == other_user.id
conn
|> get("/api/v1/media/#{object.id}")
|> json_response(403)
end
end
end
diff --git a/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs
index 633a25e50..61359214a 100644
--- a/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs
+++ b/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs
@@ -1,1743 +1,1743 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
use Pleroma.Web.ConnCase
use Oban.Testing, repo: Pleroma.Repo
alias Pleroma.Activity
alias Pleroma.Config
alias Pleroma.Conversation.Participation
alias Pleroma.Object
alias Pleroma.Repo
alias Pleroma.ScheduledActivity
alias Pleroma.Tests.ObanHelpers
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.CommonAPI
import Pleroma.Factory
setup do: clear_config([:instance, :federating])
setup do: clear_config([:instance, :allow_relay])
setup do: clear_config([:rich_media, :enabled])
setup do: clear_config([:mrf, :policies])
setup do: clear_config([:mrf_keyword, :reject])
describe "posting statuses" do
setup do: oauth_access(["write:statuses"])
test "posting a status does not increment reblog_count when relaying", %{conn: conn} do
Config.put([:instance, :federating], true)
Config.get([:instance, :allow_relay], true)
response =
conn
|> put_req_header("content-type", "application/json")
|> post("api/v1/statuses", %{
"content_type" => "text/plain",
"source" => "Pleroma FE",
"status" => "Hello world",
"visibility" => "public"
})
|> json_response_and_validate_schema(200)
assert response["reblogs_count"] == 0
ObanHelpers.perform_all()
response =
conn
|> get("api/v1/statuses/#{response["id"]}", %{})
|> json_response_and_validate_schema(200)
assert response["reblogs_count"] == 0
end
test "posting a status", %{conn: conn} do
idempotency_key = "Pikachu rocks!"
conn_one =
conn
|> put_req_header("content-type", "application/json")
|> put_req_header("idempotency-key", idempotency_key)
|> post("/api/v1/statuses", %{
"status" => "cofe",
"spoiler_text" => "2hu",
"sensitive" => "0"
})
{:ok, ttl} = Cachex.ttl(:idempotency_cache, idempotency_key)
# Six hours
assert ttl > :timer.seconds(6 * 60 * 60 - 1)
assert %{"content" => "cofe", "id" => id, "spoiler_text" => "2hu", "sensitive" => false} =
json_response_and_validate_schema(conn_one, 200)
assert Activity.get_by_id(id)
conn_two =
conn
|> put_req_header("content-type", "application/json")
|> put_req_header("idempotency-key", idempotency_key)
|> post("/api/v1/statuses", %{
"status" => "cofe",
"spoiler_text" => "2hu",
"sensitive" => 0
})
assert %{"id" => second_id} = json_response(conn_two, 200)
assert id == second_id
conn_three =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses", %{
"status" => "cofe",
"spoiler_text" => "2hu",
"sensitive" => "False"
})
assert %{"id" => third_id} = json_response_and_validate_schema(conn_three, 200)
refute id == third_id
# An activity that will expire:
# 2 hours
expires_in = 2 * 60 * 60
expires_at = DateTime.add(DateTime.utc_now(), expires_in)
conn_four =
conn
|> put_req_header("content-type", "application/json")
|> post("api/v1/statuses", %{
"status" => "oolong",
"expires_in" => expires_in
})
assert %{"id" => fourth_id} = json_response_and_validate_schema(conn_four, 200)
assert Activity.get_by_id(fourth_id)
assert_enqueued(
worker: Pleroma.Workers.PurgeExpiredActivity,
args: %{activity_id: fourth_id},
scheduled_at: expires_at
)
end
test "it fails to create a status if `expires_in` is less or equal than an hour", %{
conn: conn
} do
# 1 minute
expires_in = 1 * 60
assert %{"error" => "Expiry date is too soon"} =
conn
|> put_req_header("content-type", "application/json")
|> post("api/v1/statuses", %{
"status" => "oolong",
"expires_in" => expires_in
})
|> json_response_and_validate_schema(422)
# 5 minutes
expires_in = 5 * 60
assert %{"error" => "Expiry date is too soon"} =
conn
|> put_req_header("content-type", "application/json")
|> post("api/v1/statuses", %{
"status" => "oolong",
"expires_in" => expires_in
})
|> json_response_and_validate_schema(422)
end
test "Get MRF reason when posting a status is rejected by one", %{conn: conn} do
Config.put([:mrf_keyword, :reject], ["GNO"])
Config.put([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.KeywordPolicy])
assert %{"error" => "[KeywordPolicy] Matches with rejected keyword"} =
conn
|> put_req_header("content-type", "application/json")
|> post("api/v1/statuses", %{"status" => "GNO/Linux"})
|> json_response_and_validate_schema(422)
end
test "posting an undefined status with an attachment", %{user: user, conn: conn} do
file = %Plug.Upload{
- content_type: "image/jpg",
+ content_type: "image/jpeg",
path: Path.absname("test/fixtures/image.jpg"),
filename: "an_image.jpg"
}
{:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses", %{
"media_ids" => [to_string(upload.id)]
})
assert json_response_and_validate_schema(conn, 200)
end
test "replying to a status", %{user: user, conn: conn} do
{:ok, replied_to} = CommonAPI.post(user, %{status: "cofe"})
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
assert %{"content" => "xD", "id" => id} = json_response_and_validate_schema(conn, 200)
activity = Activity.get_by_id(id)
assert activity.data["context"] == replied_to.data["context"]
assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
end
test "replying to a direct message with visibility other than direct", %{
user: user,
conn: conn
} do
{:ok, replied_to} = CommonAPI.post(user, %{status: "suya..", visibility: "direct"})
Enum.each(["public", "private", "unlisted"], fn visibility ->
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses", %{
"status" => "@#{user.nickname} hey",
"in_reply_to_id" => replied_to.id,
"visibility" => visibility
})
assert json_response_and_validate_schema(conn, 422) == %{
"error" => "The message visibility must be direct"
}
end)
end
test "posting a status with an invalid in_reply_to_id", %{conn: conn} do
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""})
assert %{"content" => "xD", "id" => id} = json_response_and_validate_schema(conn, 200)
assert Activity.get_by_id(id)
end
test "posting a sensitive status", %{conn: conn} do
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true})
assert %{"content" => "cofe", "id" => id, "sensitive" => true} =
json_response_and_validate_schema(conn, 200)
assert Activity.get_by_id(id)
end
test "posting a fake status", %{conn: conn} do
real_conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses", %{
"status" =>
"\"Tenshi Eating a Corndog\" is a much discussed concept on /jp/. The significance of it is disputed, so I will focus on one core concept: the symbolism behind it"
})
real_status = json_response_and_validate_schema(real_conn, 200)
assert real_status
assert Object.get_by_ap_id(real_status["uri"])
real_status =
real_status
|> Map.put("id", nil)
|> Map.put("url", nil)
|> Map.put("uri", nil)
|> Map.put("created_at", nil)
|> Kernel.put_in(["pleroma", "conversation_id"], nil)
fake_conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses", %{
"status" =>
"\"Tenshi Eating a Corndog\" is a much discussed concept on /jp/. The significance of it is disputed, so I will focus on one core concept: the symbolism behind it",
"preview" => true
})
fake_status = json_response_and_validate_schema(fake_conn, 200)
assert fake_status
refute Object.get_by_ap_id(fake_status["uri"])
fake_status =
fake_status
|> Map.put("id", nil)
|> Map.put("url", nil)
|> Map.put("uri", nil)
|> Map.put("created_at", nil)
|> Kernel.put_in(["pleroma", "conversation_id"], nil)
assert real_status == fake_status
end
test "fake statuses' preview card is not cached", %{conn: conn} do
clear_config([:rich_media, :enabled], true)
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(fn env -> apply(HttpRequestMock, :request, [env]) end)
clear_config([:rich_media, :enabled], true)
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}"
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("api/v1/statuses", %{"status" => content, "visibility" => "direct"})
assert %{"id" => id} = response = json_response_and_validate_schema(conn, 200)
assert response["visibility"] == "direct"
assert response["pleroma"]["direct_conversation_id"]
assert activity = Activity.get_by_id(id)
assert activity.recipients == [user2.ap_id, conn.assigns[:user].ap_id]
assert activity.data["to"] == [user2.ap_id]
assert activity.data["cc"] == []
end
end
describe "posting scheduled statuses" do
setup do: oauth_access(["write:statuses"])
test "creates a scheduled activity", %{conn: conn} do
scheduled_at =
NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
|> NaiveDateTime.to_iso8601()
|> Kernel.<>("Z")
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses", %{
"status" => "scheduled",
"scheduled_at" => scheduled_at
})
assert %{"scheduled_at" => expected_scheduled_at} =
json_response_and_validate_schema(conn, 200)
assert expected_scheduled_at == CommonAPI.Utils.to_masto_date(scheduled_at)
assert [] == Repo.all(Activity)
end
test "ignores nil values", %{conn: conn} do
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses", %{
"status" => "not scheduled",
"scheduled_at" => nil
})
assert result = json_response_and_validate_schema(conn, 200)
assert Activity.get_by_id(result["id"])
end
test "creates a scheduled activity with a media attachment", %{user: user, conn: conn} do
scheduled_at =
NaiveDateTime.utc_now()
|> NaiveDateTime.add(:timer.minutes(120), :millisecond)
|> NaiveDateTime.to_iso8601()
|> Kernel.<>("Z")
file = %Plug.Upload{
- content_type: "image/jpg",
+ content_type: "image/jpeg",
path: Path.absname("test/fixtures/image.jpg"),
filename: "an_image.jpg"
}
{:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses", %{
"media_ids" => [to_string(upload.id)],
"status" => "scheduled",
"scheduled_at" => scheduled_at
})
assert %{"media_attachments" => [media_attachment]} =
json_response_and_validate_schema(conn, 200)
assert %{"type" => "image"} = media_attachment
end
test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
%{conn: conn} do
scheduled_at =
NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
|> NaiveDateTime.to_iso8601()
|> Kernel.<>("Z")
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses", %{
"status" => "not scheduled",
"scheduled_at" => scheduled_at
})
assert %{"content" => "not scheduled"} = json_response_and_validate_schema(conn, 200)
assert [] == Repo.all(ScheduledActivity)
end
test "returns error when daily user limit is exceeded", %{user: user, conn: conn} do
today =
NaiveDateTime.utc_now()
|> NaiveDateTime.add(:timer.minutes(6), :millisecond)
|> NaiveDateTime.to_iso8601()
# TODO
|> Kernel.<>("Z")
attrs = %{params: %{}, scheduled_at: today}
{:ok, _} = ScheduledActivity.create(user, attrs)
{:ok, _} = ScheduledActivity.create(user, attrs)
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
assert %{"error" => "daily limit exceeded"} == json_response_and_validate_schema(conn, 422)
end
test "returns error when total user limit is exceeded", %{user: user, conn: conn} do
today =
NaiveDateTime.utc_now()
|> NaiveDateTime.add(:timer.minutes(6), :millisecond)
|> NaiveDateTime.to_iso8601()
|> Kernel.<>("Z")
tomorrow =
NaiveDateTime.utc_now()
|> NaiveDateTime.add(:timer.hours(36), :millisecond)
|> NaiveDateTime.to_iso8601()
|> Kernel.<>("Z")
attrs = %{params: %{}, scheduled_at: today}
{:ok, _} = ScheduledActivity.create(user, attrs)
{:ok, _} = ScheduledActivity.create(user, attrs)
{:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
assert %{"error" => "total limit exceeded"} == json_response_and_validate_schema(conn, 422)
end
end
describe "posting polls" do
setup do: oauth_access(["write:statuses"])
test "posting a poll", %{conn: conn} do
time = NaiveDateTime.utc_now()
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses", %{
"status" => "Who is the #bestgrill?",
"poll" => %{
"options" => ["Rei", "Asuka", "Misato"],
"expires_in" => 420
}
})
response = json_response_and_validate_schema(conn, 200)
assert Enum.all?(response["poll"]["options"], fn %{"title" => title} ->
title in ["Rei", "Asuka", "Misato"]
end)
assert NaiveDateTime.diff(NaiveDateTime.from_iso8601!(response["poll"]["expires_at"]), time) in 420..430
refute response["poll"]["expred"]
question = Object.get_by_id(response["poll"]["id"])
# closed contains utc timezone
assert question.data["closed"] =~ "Z"
end
test "option limit is enforced", %{conn: conn} do
limit = Config.get([:instance, :poll_limits, :max_options])
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses", %{
"status" => "desu~",
"poll" => %{"options" => Enum.map(0..limit, fn _ -> "desu" end), "expires_in" => 1}
})
%{"error" => error} = json_response_and_validate_schema(conn, 422)
assert error == "Poll can't contain more than #{limit} options"
end
test "option character limit is enforced", %{conn: conn} do
limit = Config.get([:instance, :poll_limits, :max_option_chars])
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses", %{
"status" => "...",
"poll" => %{
"options" => [Enum.reduce(0..limit, "", fn _, acc -> acc <> "." end)],
"expires_in" => 1
}
})
%{"error" => error} = json_response_and_validate_schema(conn, 422)
assert error == "Poll options cannot be longer than #{limit} characters each"
end
test "minimal date limit is enforced", %{conn: conn} do
limit = Config.get([:instance, :poll_limits, :min_expiration])
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses", %{
"status" => "imagine arbitrary limits",
"poll" => %{
"options" => ["this post was made by pleroma gang"],
"expires_in" => limit - 1
}
})
%{"error" => error} = json_response_and_validate_schema(conn, 422)
assert error == "Expiration date is too soon"
end
test "maximum date limit is enforced", %{conn: conn} do
limit = Config.get([:instance, :poll_limits, :max_expiration])
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses", %{
"status" => "imagine arbitrary limits",
"poll" => %{
"options" => ["this post was made by pleroma gang"],
"expires_in" => limit + 1
}
})
%{"error" => error} = json_response_and_validate_schema(conn, 422)
assert error == "Expiration date is too far in the future"
end
end
test "get a status" do
%{conn: conn} = oauth_access(["read:statuses"])
activity = insert(:note_activity)
conn = get(conn, "/api/v1/statuses/#{activity.id}")
assert %{"id" => id} = json_response_and_validate_schema(conn, 200)
assert id == to_string(activity.id)
end
defp local_and_remote_activities do
local = insert(:note_activity)
remote = insert(:note_activity, local: false)
{:ok, local: local, remote: remote}
end
describe "status with restrict unauthenticated activities for local and remote" do
setup do: local_and_remote_activities()
setup do: clear_config([:restrict_unauthenticated, :activities, :local], true)
setup do: clear_config([:restrict_unauthenticated, :activities, :remote], true)
test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
res_conn = get(conn, "/api/v1/statuses/#{local.id}")
assert json_response_and_validate_schema(res_conn, :not_found) == %{
"error" => "Record not found"
}
res_conn = get(conn, "/api/v1/statuses/#{remote.id}")
assert json_response_and_validate_schema(res_conn, :not_found) == %{
"error" => "Record not found"
}
end
test "if user is authenticated", %{local: local, remote: remote} do
%{conn: conn} = oauth_access(["read"])
res_conn = get(conn, "/api/v1/statuses/#{local.id}")
assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
res_conn = get(conn, "/api/v1/statuses/#{remote.id}")
assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
end
end
describe "status with restrict unauthenticated activities for local" do
setup do: local_and_remote_activities()
setup do: clear_config([:restrict_unauthenticated, :activities, :local], true)
test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
res_conn = get(conn, "/api/v1/statuses/#{local.id}")
assert json_response_and_validate_schema(res_conn, :not_found) == %{
"error" => "Record not found"
}
res_conn = get(conn, "/api/v1/statuses/#{remote.id}")
assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
end
test "if user is authenticated", %{local: local, remote: remote} do
%{conn: conn} = oauth_access(["read"])
res_conn = get(conn, "/api/v1/statuses/#{local.id}")
assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
res_conn = get(conn, "/api/v1/statuses/#{remote.id}")
assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
end
end
describe "status with restrict unauthenticated activities for remote" do
setup do: local_and_remote_activities()
setup do: clear_config([:restrict_unauthenticated, :activities, :remote], true)
test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
res_conn = get(conn, "/api/v1/statuses/#{local.id}")
assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
res_conn = get(conn, "/api/v1/statuses/#{remote.id}")
assert json_response_and_validate_schema(res_conn, :not_found) == %{
"error" => "Record not found"
}
end
test "if user is authenticated", %{local: local, remote: remote} do
%{conn: conn} = oauth_access(["read"])
res_conn = get(conn, "/api/v1/statuses/#{local.id}")
assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
res_conn = get(conn, "/api/v1/statuses/#{remote.id}")
assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
end
end
test "getting a status that doesn't exist returns 404" do
%{conn: conn} = oauth_access(["read:statuses"])
activity = insert(:note_activity)
conn = get(conn, "/api/v1/statuses/#{String.downcase(activity.id)}")
assert json_response_and_validate_schema(conn, 404) == %{"error" => "Record not found"}
end
test "get a direct status" do
%{user: user, conn: conn} = oauth_access(["read:statuses"])
other_user = insert(:user)
{:ok, activity} =
CommonAPI.post(user, %{status: "@#{other_user.nickname}", visibility: "direct"})
conn =
conn
|> assign(:user, user)
|> get("/api/v1/statuses/#{activity.id}")
[participation] = Participation.for_user(user)
res = json_response_and_validate_schema(conn, 200)
assert res["pleroma"]["direct_conversation_id"] == participation.id
end
test "get statuses by IDs" do
%{conn: conn} = oauth_access(["read:statuses"])
%{id: id1} = insert(:note_activity)
%{id: id2} = insert(:note_activity)
query_string = "ids[]=#{id1}&ids[]=#{id2}"
conn = get(conn, "/api/v1/statuses/?#{query_string}")
assert [%{"id" => ^id1}, %{"id" => ^id2}] =
Enum.sort_by(json_response_and_validate_schema(conn, :ok), & &1["id"])
end
describe "getting statuses by ids with restricted unauthenticated for local and remote" do
setup do: local_and_remote_activities()
setup do: clear_config([:restrict_unauthenticated, :activities, :local], true)
setup do: clear_config([:restrict_unauthenticated, :activities, :remote], true)
test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
res_conn = get(conn, "/api/v1/statuses?ids[]=#{local.id}&ids[]=#{remote.id}")
assert json_response_and_validate_schema(res_conn, 200) == []
end
test "if user is authenticated", %{local: local, remote: remote} do
%{conn: conn} = oauth_access(["read"])
res_conn = get(conn, "/api/v1/statuses?ids[]=#{local.id}&ids[]=#{remote.id}")
assert length(json_response_and_validate_schema(res_conn, 200)) == 2
end
end
describe "getting statuses by ids with restricted unauthenticated for local" do
setup do: local_and_remote_activities()
setup do: clear_config([:restrict_unauthenticated, :activities, :local], true)
test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
res_conn = get(conn, "/api/v1/statuses?ids[]=#{local.id}&ids[]=#{remote.id}")
remote_id = remote.id
assert [%{"id" => ^remote_id}] = json_response_and_validate_schema(res_conn, 200)
end
test "if user is authenticated", %{local: local, remote: remote} do
%{conn: conn} = oauth_access(["read"])
res_conn = get(conn, "/api/v1/statuses?ids[]=#{local.id}&ids[]=#{remote.id}")
assert length(json_response_and_validate_schema(res_conn, 200)) == 2
end
end
describe "getting statuses by ids with restricted unauthenticated for remote" do
setup do: local_and_remote_activities()
setup do: clear_config([:restrict_unauthenticated, :activities, :remote], true)
test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
res_conn = get(conn, "/api/v1/statuses?ids[]=#{local.id}&ids[]=#{remote.id}")
local_id = local.id
assert [%{"id" => ^local_id}] = json_response_and_validate_schema(res_conn, 200)
end
test "if user is authenticated", %{local: local, remote: remote} do
%{conn: conn} = oauth_access(["read"])
res_conn = get(conn, "/api/v1/statuses?ids[]=#{local.id}&ids[]=#{remote.id}")
assert length(json_response_and_validate_schema(res_conn, 200)) == 2
end
end
describe "deleting a status" do
test "when you created it" do
%{user: author, conn: conn} = oauth_access(["write:statuses"])
activity = insert(:note_activity, user: author)
object = Object.normalize(activity)
content = object.data["content"]
source = object.data["source"]
result =
conn
|> assign(:user, author)
|> delete("/api/v1/statuses/#{activity.id}")
|> json_response_and_validate_schema(200)
assert match?(%{"content" => ^content, "text" => ^source}, result)
refute Activity.get_by_id(activity.id)
end
test "when it doesn't exist" do
%{user: author, conn: conn} = oauth_access(["write:statuses"])
activity = insert(:note_activity, user: author)
conn =
conn
|> assign(:user, author)
|> delete("/api/v1/statuses/#{String.downcase(activity.id)}")
assert %{"error" => "Record not found"} == json_response_and_validate_schema(conn, 404)
end
test "when you didn't create it" do
%{conn: conn} = oauth_access(["write:statuses"])
activity = insert(:note_activity)
conn = delete(conn, "/api/v1/statuses/#{activity.id}")
assert %{"error" => "Record not found"} == json_response_and_validate_schema(conn, 404)
assert Activity.get_by_id(activity.id) == activity
end
test "when you're an admin or moderator", %{conn: conn} do
activity1 = insert(:note_activity)
activity2 = insert(:note_activity)
admin = insert(:user, is_admin: true)
moderator = insert(:user, is_moderator: true)
res_conn =
conn
|> assign(:user, admin)
|> assign(:token, insert(:oauth_token, user: admin, scopes: ["write:statuses"]))
|> delete("/api/v1/statuses/#{activity1.id}")
assert %{} = json_response_and_validate_schema(res_conn, 200)
res_conn =
conn
|> assign(:user, moderator)
|> assign(:token, insert(:oauth_token, user: moderator, scopes: ["write:statuses"]))
|> delete("/api/v1/statuses/#{activity2.id}")
assert %{} = json_response_and_validate_schema(res_conn, 200)
refute Activity.get_by_id(activity1.id)
refute Activity.get_by_id(activity2.id)
end
end
describe "reblogging" do
setup do: oauth_access(["write:statuses"])
test "reblogs and returns the reblogged status", %{conn: conn} do
activity = insert(:note_activity)
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses/#{activity.id}/reblog")
assert %{
"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1},
"reblogged" => true
} = json_response_and_validate_schema(conn, 200)
assert to_string(activity.id) == id
end
test "returns 404 if the reblogged status doesn't exist", %{conn: conn} do
activity = insert(:note_activity)
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses/#{String.downcase(activity.id)}/reblog")
assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn, 404)
end
test "reblogs privately and returns the reblogged status", %{conn: conn} do
activity = insert(:note_activity)
conn =
conn
|> put_req_header("content-type", "application/json")
|> post(
"/api/v1/statuses/#{activity.id}/reblog",
%{"visibility" => "private"}
)
assert %{
"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1},
"reblogged" => true,
"visibility" => "private"
} = json_response_and_validate_schema(conn, 200)
assert to_string(activity.id) == id
end
test "reblogged status for another user" do
activity = insert(:note_activity)
user1 = insert(:user)
user2 = insert(:user)
user3 = insert(:user)
{:ok, _} = CommonAPI.favorite(user2, activity.id)
{:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
{:ok, reblog_activity1} = CommonAPI.repeat(activity.id, user1)
{:ok, _} = CommonAPI.repeat(activity.id, user2)
conn_res =
build_conn()
|> assign(:user, user3)
|> assign(:token, insert(:oauth_token, user: user3, scopes: ["read:statuses"]))
|> get("/api/v1/statuses/#{reblog_activity1.id}")
assert %{
"reblog" => %{"id" => id, "reblogged" => false, "reblogs_count" => 2},
"reblogged" => false,
"favourited" => false,
"bookmarked" => false
} = json_response_and_validate_schema(conn_res, 200)
conn_res =
build_conn()
|> assign(:user, user2)
|> assign(:token, insert(:oauth_token, user: user2, scopes: ["read:statuses"]))
|> get("/api/v1/statuses/#{reblog_activity1.id}")
assert %{
"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 2},
"reblogged" => true,
"favourited" => true,
"bookmarked" => true
} = json_response_and_validate_schema(conn_res, 200)
assert to_string(activity.id) == id
end
end
describe "unreblogging" do
setup do: oauth_access(["write:statuses"])
test "unreblogs and returns the unreblogged status", %{user: user, conn: conn} do
activity = insert(:note_activity)
{:ok, _} = CommonAPI.repeat(activity.id, user)
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses/#{activity.id}/unreblog")
assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} =
json_response_and_validate_schema(conn, 200)
assert to_string(activity.id) == id
end
test "returns 404 error when activity does not exist", %{conn: conn} do
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses/foo/unreblog")
assert json_response_and_validate_schema(conn, 404) == %{"error" => "Record not found"}
end
end
describe "favoriting" do
setup do: oauth_access(["write:favourites"])
test "favs a status and returns it", %{conn: conn} do
activity = insert(:note_activity)
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses/#{activity.id}/favourite")
assert %{"id" => id, "favourites_count" => 1, "favourited" => true} =
json_response_and_validate_schema(conn, 200)
assert to_string(activity.id) == id
end
test "favoriting twice will just return 200", %{conn: conn} do
activity = insert(:note_activity)
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses/#{activity.id}/favourite")
assert conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses/#{activity.id}/favourite")
|> json_response_and_validate_schema(200)
end
test "returns 404 error for a wrong id", %{conn: conn} do
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses/1/favourite")
assert json_response_and_validate_schema(conn, 404) == %{"error" => "Record not found"}
end
end
describe "unfavoriting" do
setup do: oauth_access(["write:favourites"])
test "unfavorites a status and returns it", %{user: user, conn: conn} do
activity = insert(:note_activity)
{:ok, _} = CommonAPI.favorite(user, activity.id)
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses/#{activity.id}/unfavourite")
assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
json_response_and_validate_schema(conn, 200)
assert to_string(activity.id) == id
end
test "returns 404 error for a wrong id", %{conn: conn} do
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses/1/unfavourite")
assert json_response_and_validate_schema(conn, 404) == %{"error" => "Record not found"}
end
end
describe "pinned statuses" do
setup do: oauth_access(["write:accounts"])
setup %{user: user} do
{:ok, activity} = CommonAPI.post(user, %{status: "HI!!!"})
%{activity: activity}
end
setup do: clear_config([:instance, :max_pinned_statuses], 1)
test "pin status", %{conn: conn, user: user, activity: activity} do
id_str = to_string(activity.id)
assert %{"id" => ^id_str, "pinned" => true} =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses/#{activity.id}/pin")
|> json_response_and_validate_schema(200)
assert [%{"id" => ^id_str, "pinned" => true}] =
conn
|> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
|> json_response_and_validate_schema(200)
end
test "/pin: returns 400 error when activity is not public", %{conn: conn, user: user} do
{:ok, dm} = CommonAPI.post(user, %{status: "test", visibility: "direct"})
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses/#{dm.id}/pin")
assert json_response_and_validate_schema(conn, 400) == %{"error" => "Could not pin"}
end
test "unpin status", %{conn: conn, user: user, activity: activity} do
{:ok, _} = CommonAPI.pin(activity.id, user)
user = refresh_record(user)
id_str = to_string(activity.id)
assert %{"id" => ^id_str, "pinned" => false} =
conn
|> assign(:user, user)
|> post("/api/v1/statuses/#{activity.id}/unpin")
|> json_response_and_validate_schema(200)
assert [] =
conn
|> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
|> json_response_and_validate_schema(200)
end
test "/unpin: returns 400 error when activity is not exist", %{conn: conn} do
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses/1/unpin")
assert json_response_and_validate_schema(conn, 400) == %{"error" => "Could not unpin"}
end
test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
{:ok, activity_two} = CommonAPI.post(user, %{status: "HI!!!"})
id_str_one = to_string(activity_one.id)
assert %{"id" => ^id_str_one, "pinned" => true} =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses/#{id_str_one}/pin")
|> json_response_and_validate_schema(200)
user = refresh_record(user)
assert %{"error" => "You have already pinned the maximum number of statuses"} =
conn
|> assign(:user, user)
|> post("/api/v1/statuses/#{activity_two.id}/pin")
|> json_response_and_validate_schema(400)
end
test "on pin removes deletion job, on unpin reschedule deletion" do
%{conn: conn} = oauth_access(["write:accounts", "write:statuses"])
expires_in = 2 * 60 * 60
expires_at = DateTime.add(DateTime.utc_now(), expires_in)
assert %{"id" => id} =
conn
|> put_req_header("content-type", "application/json")
|> post("api/v1/statuses", %{
"status" => "oolong",
"expires_in" => expires_in
})
|> json_response_and_validate_schema(200)
assert_enqueued(
worker: Pleroma.Workers.PurgeExpiredActivity,
args: %{activity_id: id},
scheduled_at: expires_at
)
assert %{"id" => ^id, "pinned" => true} =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses/#{id}/pin")
|> json_response_and_validate_schema(200)
refute_enqueued(
worker: Pleroma.Workers.PurgeExpiredActivity,
args: %{activity_id: id},
scheduled_at: expires_at
)
assert %{"id" => ^id, "pinned" => false} =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses/#{id}/unpin")
|> json_response_and_validate_schema(200)
assert_enqueued(
worker: Pleroma.Workers.PurgeExpiredActivity,
args: %{activity_id: id},
scheduled_at: expires_at
)
end
end
describe "cards" do
setup do
Config.put([:rich_media, :enabled], true)
oauth_access(["read:statuses"])
end
test "returns rich-media card", %{conn: conn, user: user} do
Tesla.Mock.mock(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(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"
%{conn: conn} = oauth_access(["write:bookmarks", "read:bookmarks"])
author = insert(:user)
{:ok, activity1} = CommonAPI.post(author, %{status: "heweoo?"})
{:ok, activity2} = CommonAPI.post(author, %{status: "heweoo!"})
response1 =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses/#{activity1.id}/bookmark")
assert json_response_and_validate_schema(response1, 200)["bookmarked"] == true
response2 =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses/#{activity2.id}/bookmark")
assert json_response_and_validate_schema(response2, 200)["bookmarked"] == true
bookmarks = get(conn, bookmarks_uri)
assert [
json_response_and_validate_schema(response2, 200),
json_response_and_validate_schema(response1, 200)
] ==
json_response_and_validate_schema(bookmarks, 200)
response1 =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses/#{activity1.id}/unbookmark")
assert json_response_and_validate_schema(response1, 200)["bookmarked"] == false
bookmarks = get(conn, bookmarks_uri)
assert [json_response_and_validate_schema(response2, 200)] ==
json_response_and_validate_schema(bookmarks, 200)
end
describe "conversation muting" do
setup do: oauth_access(["write:mutes"])
setup do
post_user = insert(:user)
{:ok, activity} = CommonAPI.post(post_user, %{status: "HIE"})
%{activity: activity}
end
test "mute conversation", %{conn: conn, activity: activity} do
id_str = to_string(activity.id)
assert %{"id" => ^id_str, "muted" => true} =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses/#{activity.id}/mute")
|> json_response_and_validate_schema(200)
end
test "cannot mute already muted conversation", %{conn: conn, user: user, activity: activity} do
{:ok, _} = CommonAPI.add_mute(user, activity)
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses/#{activity.id}/mute")
assert json_response_and_validate_schema(conn, 400) == %{
"error" => "conversation is already muted"
}
end
test "unmute conversation", %{conn: conn, user: user, activity: activity} do
{:ok, _} = CommonAPI.add_mute(user, activity)
id_str = to_string(activity.id)
assert %{"id" => ^id_str, "muted" => false} =
conn
# |> assign(:user, user)
|> post("/api/v1/statuses/#{activity.id}/unmute")
|> json_response_and_validate_schema(200)
end
end
test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
user1 = insert(:user)
user2 = insert(:user)
user3 = insert(:user)
{:ok, replied_to} = CommonAPI.post(user1, %{status: "cofe"})
# Reply to status from another user
conn1 =
conn
|> assign(:user, user2)
|> assign(:token, insert(:oauth_token, user: user2, scopes: ["write:statuses"]))
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
assert %{"content" => "xD", "id" => id} = json_response_and_validate_schema(conn1, 200)
activity = Activity.get_by_id_with_object(id)
assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
# Reblog from the third user
conn2 =
conn
|> assign(:user, user3)
|> assign(:token, insert(:oauth_token, user: user3, scopes: ["write:statuses"]))
|> put_req_header("content-type", "application/json")
|> post("/api/v1/statuses/#{activity.id}/reblog")
assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
json_response_and_validate_schema(conn2, 200)
assert to_string(activity.id) == id
# Getting third user status
conn3 =
conn
|> assign(:user, user3)
|> assign(:token, insert(:oauth_token, user: user3, scopes: ["read:statuses"]))
|> get("api/v1/timelines/home")
[reblogged_activity] = json_response(conn3, 200)
assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
end
describe "GET /api/v1/statuses/:id/favourited_by" do
setup do: oauth_access(["read:accounts"])
setup %{user: user} do
{:ok, activity} = CommonAPI.post(user, %{status: "test"})
%{activity: activity}
end
test "returns users who have favorited the status", %{conn: conn, activity: activity} do
other_user = insert(:user)
{:ok, _} = CommonAPI.favorite(other_user, activity.id)
response =
conn
|> get("/api/v1/statuses/#{activity.id}/favourited_by")
|> json_response_and_validate_schema(:ok)
[%{"id" => id}] = response
assert id == other_user.id
end
test "returns empty array when status has not been favorited yet", %{
conn: conn,
activity: activity
} do
response =
conn
|> get("/api/v1/statuses/#{activity.id}/favourited_by")
|> json_response_and_validate_schema(:ok)
assert Enum.empty?(response)
end
test "does not return users who have favorited the status but are blocked", %{
conn: %{assigns: %{user: user}} = conn,
activity: activity
} do
other_user = insert(:user)
{:ok, _user_relationship} = User.block(user, other_user)
{:ok, _} = CommonAPI.favorite(other_user, activity.id)
response =
conn
|> get("/api/v1/statuses/#{activity.id}/favourited_by")
|> json_response_and_validate_schema(:ok)
assert Enum.empty?(response)
end
test "does not fail on an unauthenticated request", %{activity: activity} do
other_user = insert(:user)
{:ok, _} = CommonAPI.favorite(other_user, activity.id)
response =
build_conn()
|> get("/api/v1/statuses/#{activity.id}/favourited_by")
|> json_response_and_validate_schema(:ok)
[%{"id" => id}] = response
assert id == other_user.id
end
test "requires authentication for private posts", %{user: user} do
other_user = insert(:user)
{:ok, activity} =
CommonAPI.post(user, %{
status: "@#{other_user.nickname} wanna get some #cofe together?",
visibility: "direct"
})
{:ok, _} = CommonAPI.favorite(other_user, activity.id)
favourited_by_url = "/api/v1/statuses/#{activity.id}/favourited_by"
build_conn()
|> get(favourited_by_url)
|> json_response_and_validate_schema(404)
conn =
build_conn()
|> assign(:user, other_user)
|> assign(:token, insert(:oauth_token, user: other_user, scopes: ["read:accounts"]))
conn
|> assign(:token, nil)
|> get(favourited_by_url)
|> json_response_and_validate_schema(404)
response =
conn
|> get(favourited_by_url)
|> json_response_and_validate_schema(200)
[%{"id" => id}] = response
assert id == other_user.id
end
test "returns empty array when :show_reactions is disabled", %{conn: conn, activity: activity} do
clear_config([:instance, :show_reactions], false)
other_user = insert(:user)
{:ok, _} = CommonAPI.favorite(other_user, activity.id)
response =
conn
|> get("/api/v1/statuses/#{activity.id}/favourited_by")
|> json_response_and_validate_schema(:ok)
assert Enum.empty?(response)
end
end
describe "GET /api/v1/statuses/:id/reblogged_by" do
setup do: oauth_access(["read:accounts"])
setup %{user: user} do
{:ok, activity} = CommonAPI.post(user, %{status: "test"})
%{activity: activity}
end
test "returns users who have reblogged the status", %{conn: conn, activity: activity} do
other_user = insert(:user)
{:ok, _} = CommonAPI.repeat(activity.id, other_user)
response =
conn
|> get("/api/v1/statuses/#{activity.id}/reblogged_by")
|> json_response_and_validate_schema(:ok)
[%{"id" => id}] = response
assert id == other_user.id
end
test "returns empty array when status has not been reblogged yet", %{
conn: conn,
activity: activity
} do
response =
conn
|> get("/api/v1/statuses/#{activity.id}/reblogged_by")
|> json_response_and_validate_schema(:ok)
assert Enum.empty?(response)
end
test "does not return users who have reblogged the status but are blocked", %{
conn: %{assigns: %{user: user}} = conn,
activity: activity
} do
other_user = insert(:user)
{:ok, _user_relationship} = User.block(user, other_user)
{:ok, _} = CommonAPI.repeat(activity.id, other_user)
response =
conn
|> get("/api/v1/statuses/#{activity.id}/reblogged_by")
|> json_response_and_validate_schema(:ok)
assert Enum.empty?(response)
end
test "does not return users who have reblogged the status privately", %{
conn: conn
} do
other_user = insert(:user)
{:ok, activity} = CommonAPI.post(other_user, %{status: "my secret post"})
{:ok, _} = CommonAPI.repeat(activity.id, other_user, %{visibility: "private"})
response =
conn
|> get("/api/v1/statuses/#{activity.id}/reblogged_by")
|> json_response_and_validate_schema(:ok)
assert Enum.empty?(response)
end
test "does not fail on an unauthenticated request", %{activity: activity} do
other_user = insert(:user)
{:ok, _} = CommonAPI.repeat(activity.id, other_user)
response =
build_conn()
|> get("/api/v1/statuses/#{activity.id}/reblogged_by")
|> json_response_and_validate_schema(:ok)
[%{"id" => id}] = response
assert id == other_user.id
end
test "requires authentication for private posts", %{user: user} do
other_user = insert(:user)
{:ok, activity} =
CommonAPI.post(user, %{
status: "@#{other_user.nickname} wanna get some #cofe together?",
visibility: "direct"
})
build_conn()
|> get("/api/v1/statuses/#{activity.id}/reblogged_by")
|> json_response_and_validate_schema(404)
response =
build_conn()
|> assign(:user, other_user)
|> assign(:token, insert(:oauth_token, user: other_user, scopes: ["read:accounts"]))
|> get("/api/v1/statuses/#{activity.id}/reblogged_by")
|> json_response_and_validate_schema(200)
assert [] == response
end
end
test "context" do
user = insert(:user)
{:ok, %{id: id1}} = CommonAPI.post(user, %{status: "1"})
{:ok, %{id: id2}} = CommonAPI.post(user, %{status: "2", in_reply_to_status_id: id1})
{:ok, %{id: id3}} = CommonAPI.post(user, %{status: "3", in_reply_to_status_id: id2})
{:ok, %{id: id4}} = CommonAPI.post(user, %{status: "4", in_reply_to_status_id: id3})
{:ok, %{id: id5}} = CommonAPI.post(user, %{status: "5", in_reply_to_status_id: id4})
response =
build_conn()
|> get("/api/v1/statuses/#{id3}/context")
|> json_response_and_validate_schema(:ok)
assert %{
"ancestors" => [%{"id" => ^id1}, %{"id" => ^id2}],
"descendants" => [%{"id" => ^id4}, %{"id" => ^id5}]
} = response
end
test "favorites paginate correctly" do
%{user: user, conn: conn} = oauth_access(["read:favourites"])
other_user = insert(:user)
{:ok, first_post} = CommonAPI.post(other_user, %{status: "bla"})
{:ok, second_post} = CommonAPI.post(other_user, %{status: "bla"})
{:ok, third_post} = CommonAPI.post(other_user, %{status: "bla"})
{:ok, _first_favorite} = CommonAPI.favorite(user, third_post.id)
{:ok, _second_favorite} = CommonAPI.favorite(user, first_post.id)
{:ok, third_favorite} = CommonAPI.favorite(user, second_post.id)
result =
conn
|> get("/api/v1/favourites?limit=1")
assert [%{"id" => post_id}] = json_response_and_validate_schema(result, 200)
assert post_id == second_post.id
# Using the header for pagination works correctly
[next, _] = get_resp_header(result, "link") |> hd() |> String.split(", ")
[_, max_id] = Regex.run(~r/max_id=([^&]+)/, next)
assert max_id == third_favorite.id
result =
conn
|> get("/api/v1/favourites?max_id=#{max_id}")
assert [%{"id" => first_post_id}, %{"id" => third_post_id}] =
json_response_and_validate_schema(result, 200)
assert first_post_id == first_post.id
assert third_post_id == third_post.id
end
test "returns the favorites of a user" do
%{user: user, conn: conn} = oauth_access(["read:favourites"])
other_user = insert(:user)
{:ok, _} = CommonAPI.post(other_user, %{status: "bla"})
{:ok, activity} = CommonAPI.post(other_user, %{status: "trees are happy"})
{:ok, last_like} = CommonAPI.favorite(user, activity.id)
first_conn = get(conn, "/api/v1/favourites")
assert [status] = json_response_and_validate_schema(first_conn, 200)
assert status["id"] == to_string(activity.id)
assert [{"link", _link_header}] =
Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
# Honours query params
{:ok, second_activity} =
CommonAPI.post(other_user, %{
status: "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
})
{:ok, _} = CommonAPI.favorite(user, second_activity.id)
second_conn = get(conn, "/api/v1/favourites?since_id=#{last_like.id}")
assert [second_status] = json_response_and_validate_schema(second_conn, 200)
assert second_status["id"] == to_string(second_activity.id)
third_conn = get(conn, "/api/v1/favourites?limit=0")
assert [] = json_response_and_validate_schema(third_conn, 200)
end
test "expires_at is nil for another user" do
%{conn: conn, user: user} = oauth_access(["read:statuses"])
expires_at = DateTime.add(DateTime.utc_now(), 1_000_000)
{:ok, activity} = CommonAPI.post(user, %{status: "foobar", expires_in: 1_000_000})
assert %{"pleroma" => %{"expires_at" => a_expires_at}} =
conn
|> get("/api/v1/statuses/#{activity.id}")
|> json_response_and_validate_schema(:ok)
{:ok, a_expires_at, 0} = DateTime.from_iso8601(a_expires_at)
assert DateTime.diff(expires_at, a_expires_at) == 0
%{conn: conn} = oauth_access(["read:statuses"])
assert %{"pleroma" => %{"expires_at" => nil}} =
conn
|> get("/api/v1/statuses/#{activity.id}")
|> json_response_and_validate_schema(:ok)
end
end
diff --git a/test/pleroma/web/mastodon_api/update_credentials_test.exs b/test/pleroma/web/mastodon_api/update_credentials_test.exs
index fe462caa3..ed1921c91 100644
--- a/test/pleroma/web/mastodon_api/update_credentials_test.exs
+++ b/test/pleroma/web/mastodon_api/update_credentials_test.exs
@@ -1,529 +1,529 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.UpdateCredentialsTest do
alias Pleroma.Repo
alias Pleroma.User
use Pleroma.Web.ConnCase
import Mock
import Pleroma.Factory
setup do: clear_config([:instance, :max_account_fields])
describe "updating credentials" do
setup do: oauth_access(["write:accounts"])
setup :request_content_type
test "sets user settings in a generic way", %{conn: conn} do
res_conn =
patch(conn, "/api/v1/accounts/update_credentials", %{
"pleroma_settings_store" => %{
pleroma_fe: %{
theme: "bla"
}
}
})
assert user_data = json_response_and_validate_schema(res_conn, 200)
assert user_data["pleroma"]["settings_store"] == %{"pleroma_fe" => %{"theme" => "bla"}}
user = Repo.get(User, user_data["id"])
res_conn =
conn
|> assign(:user, user)
|> patch("/api/v1/accounts/update_credentials", %{
"pleroma_settings_store" => %{
masto_fe: %{
theme: "bla"
}
}
})
assert user_data = json_response_and_validate_schema(res_conn, 200)
assert user_data["pleroma"]["settings_store"] ==
%{
"pleroma_fe" => %{"theme" => "bla"},
"masto_fe" => %{"theme" => "bla"}
}
user = Repo.get(User, user_data["id"])
clear_config([:instance, :federating], true)
with_mock Pleroma.Web.Federator,
publish: fn _activity -> :ok end do
res_conn =
conn
|> assign(:user, user)
|> patch("/api/v1/accounts/update_credentials", %{
"pleroma_settings_store" => %{
masto_fe: %{
theme: "blub"
}
}
})
assert user_data = json_response_and_validate_schema(res_conn, 200)
assert user_data["pleroma"]["settings_store"] ==
%{
"pleroma_fe" => %{"theme" => "bla"},
"masto_fe" => %{"theme" => "blub"}
}
assert_called(Pleroma.Web.Federator.publish(:_))
end
end
test "updates the user's bio", %{conn: conn} do
user2 = insert(:user)
raw_bio = "I drink #cofe with @#{user2.nickname}\n\nsuya.."
conn = patch(conn, "/api/v1/accounts/update_credentials", %{"note" => raw_bio})
assert user_data = json_response_and_validate_schema(conn, 200)
assert user_data["note"] ==
~s(I drink <a class="hashtag" data-tag="cofe" href="http://localhost:4001/tag/cofe">#cofe</a> with <span class="h-card"><a class="u-url mention" data-user="#{
user2.id
}" href="#{user2.ap_id}" rel="ugc">@<span>#{user2.nickname}</span></a></span><br/><br/>suya..)
assert user_data["source"]["note"] == raw_bio
user = Repo.get(User, user_data["id"])
assert user.raw_bio == raw_bio
end
test "updates the user's locking status", %{conn: conn} do
conn = patch(conn, "/api/v1/accounts/update_credentials", %{locked: "true"})
assert user_data = json_response_and_validate_schema(conn, 200)
assert user_data["locked"] == true
end
test "updates the user's chat acceptance status", %{conn: conn} do
conn = patch(conn, "/api/v1/accounts/update_credentials", %{accepts_chat_messages: "false"})
assert user_data = json_response_and_validate_schema(conn, 200)
assert user_data["pleroma"]["accepts_chat_messages"] == false
end
test "updates the user's allow_following_move", %{user: user, conn: conn} do
assert user.allow_following_move == true
conn = patch(conn, "/api/v1/accounts/update_credentials", %{allow_following_move: "false"})
assert refresh_record(user).allow_following_move == false
assert user_data = json_response_and_validate_schema(conn, 200)
assert user_data["pleroma"]["allow_following_move"] == false
end
test "updates the user's default scope", %{conn: conn} do
conn = patch(conn, "/api/v1/accounts/update_credentials", %{default_scope: "unlisted"})
assert user_data = json_response_and_validate_schema(conn, 200)
assert user_data["source"]["privacy"] == "unlisted"
end
test "updates the user's privacy", %{conn: conn} do
conn = patch(conn, "/api/v1/accounts/update_credentials", %{source: %{privacy: "unlisted"}})
assert user_data = json_response_and_validate_schema(conn, 200)
assert user_data["source"]["privacy"] == "unlisted"
end
test "updates the user's hide_followers status", %{conn: conn} do
conn = patch(conn, "/api/v1/accounts/update_credentials", %{hide_followers: "true"})
assert user_data = json_response_and_validate_schema(conn, 200)
assert user_data["pleroma"]["hide_followers"] == true
end
test "updates the user's discoverable status", %{conn: conn} do
assert %{"source" => %{"pleroma" => %{"discoverable" => true}}} =
conn
|> patch("/api/v1/accounts/update_credentials", %{discoverable: "true"})
|> json_response_and_validate_schema(:ok)
assert %{"source" => %{"pleroma" => %{"discoverable" => false}}} =
conn
|> patch("/api/v1/accounts/update_credentials", %{discoverable: "false"})
|> json_response_and_validate_schema(:ok)
end
test "updates the user's hide_followers_count and hide_follows_count", %{conn: conn} do
conn =
patch(conn, "/api/v1/accounts/update_credentials", %{
hide_followers_count: "true",
hide_follows_count: "true"
})
assert user_data = json_response_and_validate_schema(conn, 200)
assert user_data["pleroma"]["hide_followers_count"] == true
assert user_data["pleroma"]["hide_follows_count"] == true
end
test "updates the user's skip_thread_containment option", %{user: user, conn: conn} do
response =
conn
|> patch("/api/v1/accounts/update_credentials", %{skip_thread_containment: "true"})
|> json_response_and_validate_schema(200)
assert response["pleroma"]["skip_thread_containment"] == true
assert refresh_record(user).skip_thread_containment
end
test "updates the user's hide_follows status", %{conn: conn} do
conn = patch(conn, "/api/v1/accounts/update_credentials", %{hide_follows: "true"})
assert user_data = json_response_and_validate_schema(conn, 200)
assert user_data["pleroma"]["hide_follows"] == true
end
test "updates the user's hide_favorites status", %{conn: conn} do
conn = patch(conn, "/api/v1/accounts/update_credentials", %{hide_favorites: "true"})
assert user_data = json_response_and_validate_schema(conn, 200)
assert user_data["pleroma"]["hide_favorites"] == true
end
test "updates the user's show_role status", %{conn: conn} do
conn = patch(conn, "/api/v1/accounts/update_credentials", %{show_role: "false"})
assert user_data = json_response_and_validate_schema(conn, 200)
assert user_data["source"]["pleroma"]["show_role"] == false
end
test "updates the user's no_rich_text status", %{conn: conn} do
conn = patch(conn, "/api/v1/accounts/update_credentials", %{no_rich_text: "true"})
assert user_data = json_response_and_validate_schema(conn, 200)
assert user_data["source"]["pleroma"]["no_rich_text"] == true
end
test "updates the user's name", %{conn: conn} do
conn =
patch(conn, "/api/v1/accounts/update_credentials", %{"display_name" => "markorepairs"})
assert user_data = json_response_and_validate_schema(conn, 200)
assert user_data["display_name"] == "markorepairs"
update_activity = Repo.one(Pleroma.Activity)
assert update_activity.data["type"] == "Update"
assert update_activity.data["object"]["name"] == "markorepairs"
end
test "updates the user's avatar", %{user: user, conn: conn} do
new_avatar = %Plug.Upload{
- content_type: "image/jpg",
+ content_type: "image/jpeg",
path: Path.absname("test/fixtures/image.jpg"),
filename: "an_image.jpg"
}
assert user.avatar == %{}
res = patch(conn, "/api/v1/accounts/update_credentials", %{"avatar" => new_avatar})
assert user_response = json_response_and_validate_schema(res, 200)
assert user_response["avatar"] != User.avatar_url(user)
user = User.get_by_id(user.id)
refute user.avatar == %{}
# Also resets it
_res = patch(conn, "/api/v1/accounts/update_credentials", %{"avatar" => ""})
user = User.get_by_id(user.id)
assert user.avatar == nil
end
test "updates the user's banner", %{user: user, conn: conn} do
new_header = %Plug.Upload{
- content_type: "image/jpg",
+ content_type: "image/jpeg",
path: Path.absname("test/fixtures/image.jpg"),
filename: "an_image.jpg"
}
res = patch(conn, "/api/v1/accounts/update_credentials", %{"header" => new_header})
assert user_response = json_response_and_validate_schema(res, 200)
assert user_response["header"] != User.banner_url(user)
# Also resets it
_res = patch(conn, "/api/v1/accounts/update_credentials", %{"header" => ""})
user = User.get_by_id(user.id)
assert user.banner == nil
end
test "updates the user's background", %{conn: conn, user: user} do
new_header = %Plug.Upload{
- content_type: "image/jpg",
+ content_type: "image/jpeg",
path: Path.absname("test/fixtures/image.jpg"),
filename: "an_image.jpg"
}
res =
patch(conn, "/api/v1/accounts/update_credentials", %{
"pleroma_background_image" => new_header
})
assert user_response = json_response_and_validate_schema(res, 200)
assert user_response["pleroma"]["background_image"]
#
# Also resets it
_res =
patch(conn, "/api/v1/accounts/update_credentials", %{"pleroma_background_image" => ""})
user = User.get_by_id(user.id)
assert user.background == nil
end
test "requires 'write:accounts' permission" do
token1 = insert(:oauth_token, scopes: ["read"])
token2 = insert(:oauth_token, scopes: ["write", "follow"])
for token <- [token1, token2] do
conn =
build_conn()
|> put_req_header("content-type", "multipart/form-data")
|> put_req_header("authorization", "Bearer #{token.token}")
|> patch("/api/v1/accounts/update_credentials", %{})
if token == token1 do
assert %{"error" => "Insufficient permissions: write:accounts."} ==
json_response_and_validate_schema(conn, 403)
else
assert json_response_and_validate_schema(conn, 200)
end
end
end
test "updates profile emojos", %{user: user, conn: conn} do
note = "*sips :blank:*"
name = "I am :firefox:"
ret_conn =
patch(conn, "/api/v1/accounts/update_credentials", %{
"note" => note,
"display_name" => name
})
assert json_response_and_validate_schema(ret_conn, 200)
conn = get(conn, "/api/v1/accounts/#{user.id}")
assert user_data = json_response_and_validate_schema(conn, 200)
assert user_data["note"] == note
assert user_data["display_name"] == name
assert [%{"shortcode" => "blank"}, %{"shortcode" => "firefox"}] = user_data["emojis"]
end
test "update fields", %{conn: conn} do
fields = [
%{"name" => "<a href=\"http://google.com\">foo</a>", "value" => "<script>bar</script>"},
%{"name" => "link.io", "value" => "cofe.io"}
]
account_data =
conn
|> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields})
|> json_response_and_validate_schema(200)
assert account_data["fields"] == [
%{"name" => "<a href=\"http://google.com\">foo</a>", "value" => "bar"},
%{
"name" => "link.io",
"value" => ~S(<a href="http://cofe.io" rel="ugc">cofe.io</a>)
}
]
assert account_data["source"]["fields"] == [
%{
"name" => "<a href=\"http://google.com\">foo</a>",
"value" => "<script>bar</script>"
},
%{"name" => "link.io", "value" => "cofe.io"}
]
end
test "emojis in fields labels", %{conn: conn} do
fields = [
%{"name" => ":firefox:", "value" => "is best 2hu"},
%{"name" => "they wins", "value" => ":blank:"}
]
account_data =
conn
|> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields})
|> json_response_and_validate_schema(200)
assert account_data["fields"] == [
%{"name" => ":firefox:", "value" => "is best 2hu"},
%{"name" => "they wins", "value" => ":blank:"}
]
assert account_data["source"]["fields"] == [
%{"name" => ":firefox:", "value" => "is best 2hu"},
%{"name" => "they wins", "value" => ":blank:"}
]
assert [%{"shortcode" => "blank"}, %{"shortcode" => "firefox"}] = account_data["emojis"]
end
test "update fields via x-www-form-urlencoded", %{conn: conn} do
fields =
[
"fields_attributes[1][name]=link",
"fields_attributes[1][value]=http://cofe.io",
"fields_attributes[0][name]=foo",
"fields_attributes[0][value]=bar"
]
|> Enum.join("&")
account =
conn
|> put_req_header("content-type", "application/x-www-form-urlencoded")
|> patch("/api/v1/accounts/update_credentials", fields)
|> json_response_and_validate_schema(200)
assert account["fields"] == [
%{"name" => "foo", "value" => "bar"},
%{
"name" => "link",
"value" => ~S(<a href="http://cofe.io" rel="ugc">http://cofe.io</a>)
}
]
assert account["source"]["fields"] == [
%{"name" => "foo", "value" => "bar"},
%{"name" => "link", "value" => "http://cofe.io"}
]
end
test "update fields with empty name", %{conn: conn} do
fields = [
%{"name" => "foo", "value" => ""},
%{"name" => "", "value" => "bar"}
]
account =
conn
|> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields})
|> json_response_and_validate_schema(200)
assert account["fields"] == [
%{"name" => "foo", "value" => ""}
]
end
test "update fields when invalid request", %{conn: conn} do
name_limit = Pleroma.Config.get([:instance, :account_field_name_length])
value_limit = Pleroma.Config.get([:instance, :account_field_value_length])
long_name = Enum.map(0..name_limit, fn _ -> "x" end) |> Enum.join()
long_value = Enum.map(0..value_limit, fn _ -> "x" end) |> Enum.join()
fields = [%{"name" => "foo", "value" => long_value}]
assert %{"error" => "Invalid request"} ==
conn
|> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields})
|> json_response_and_validate_schema(403)
fields = [%{"name" => long_name, "value" => "bar"}]
assert %{"error" => "Invalid request"} ==
conn
|> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields})
|> json_response_and_validate_schema(403)
Pleroma.Config.put([:instance, :max_account_fields], 1)
fields = [
%{"name" => "foo", "value" => "bar"},
%{"name" => "link", "value" => "cofe.io"}
]
assert %{"error" => "Invalid request"} ==
conn
|> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields})
|> json_response_and_validate_schema(403)
end
end
describe "Mark account as bot" do
setup do: oauth_access(["write:accounts"])
setup :request_content_type
test "changing actor_type to Service makes account a bot", %{conn: conn} do
account =
conn
|> patch("/api/v1/accounts/update_credentials", %{actor_type: "Service"})
|> json_response_and_validate_schema(200)
assert account["bot"]
assert account["source"]["pleroma"]["actor_type"] == "Service"
end
test "changing actor_type to Person makes account a human", %{conn: conn} do
account =
conn
|> patch("/api/v1/accounts/update_credentials", %{actor_type: "Person"})
|> json_response_and_validate_schema(200)
refute account["bot"]
assert account["source"]["pleroma"]["actor_type"] == "Person"
end
test "changing actor_type to Application causes error", %{conn: conn} do
response =
conn
|> patch("/api/v1/accounts/update_credentials", %{actor_type: "Application"})
|> json_response_and_validate_schema(403)
assert %{"error" => "Invalid request"} == response
end
test "changing bot field to true changes actor_type to Service", %{conn: conn} do
account =
conn
|> patch("/api/v1/accounts/update_credentials", %{bot: "true"})
|> json_response_and_validate_schema(200)
assert account["bot"]
assert account["source"]["pleroma"]["actor_type"] == "Service"
end
test "changing bot field to false changes actor_type to Person", %{conn: conn} do
account =
conn
|> patch("/api/v1/accounts/update_credentials", %{bot: "false"})
|> json_response_and_validate_schema(200)
refute account["bot"]
assert account["source"]["pleroma"]["actor_type"] == "Person"
end
test "actor_type field has a higher priority than bot", %{conn: conn} do
account =
conn
|> patch("/api/v1/accounts/update_credentials", %{
actor_type: "Person",
bot: "true"
})
|> json_response_and_validate_schema(200)
refute account["bot"]
assert account["source"]["pleroma"]["actor_type"] == "Person"
end
end
end
diff --git a/test/pleroma/web/mastodon_api/views/scheduled_activity_view_test.exs b/test/pleroma/web/mastodon_api/views/scheduled_activity_view_test.exs
index fbfd873ef..04f73f5a0 100644
--- a/test/pleroma/web/mastodon_api/views/scheduled_activity_view_test.exs
+++ b/test/pleroma/web/mastodon_api/views/scheduled_activity_view_test.exs
@@ -1,68 +1,68 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.ScheduledActivityViewTest do
use Pleroma.DataCase
alias Pleroma.ScheduledActivity
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.CommonAPI.Utils
alias Pleroma.Web.MastodonAPI.ScheduledActivityView
alias Pleroma.Web.MastodonAPI.StatusView
import Pleroma.Factory
test "A scheduled activity with a media attachment" do
user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{status: "hi"})
scheduled_at =
NaiveDateTime.utc_now()
|> NaiveDateTime.add(:timer.minutes(10), :millisecond)
|> NaiveDateTime.to_iso8601()
file = %Plug.Upload{
- content_type: "image/jpg",
+ content_type: "image/jpeg",
path: Path.absname("test/fixtures/image.jpg"),
filename: "an_image.jpg"
}
{:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
attrs = %{
params: %{
"media_ids" => [upload.id],
"status" => "hi",
"sensitive" => true,
"spoiler_text" => "spoiler",
"visibility" => "unlisted",
"in_reply_to_id" => to_string(activity.id)
},
scheduled_at: scheduled_at
}
{:ok, scheduled_activity} = ScheduledActivity.create(user, attrs)
result = ScheduledActivityView.render("show.json", %{scheduled_activity: scheduled_activity})
expected = %{
id: to_string(scheduled_activity.id),
media_attachments:
%{media_ids: [upload.id]}
|> Utils.attachments_from_ids()
|> Enum.map(&StatusView.render("attachment.json", %{attachment: &1})),
params: %{
in_reply_to_id: to_string(activity.id),
media_ids: [upload.id],
poll: nil,
scheduled_at: nil,
sensitive: true,
spoiler_text: "spoiler",
text: "hi",
visibility: "unlisted"
},
scheduled_at: Utils.to_masto_date(scheduled_activity.scheduled_at)
}
assert expected == result
end
end
diff --git a/test/pleroma/web/pleroma_api/controllers/chat_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/chat_controller_test.exs
index 11d5ba373..6381f9757 100644
--- a/test/pleroma/web/pleroma_api/controllers/chat_controller_test.exs
+++ b/test/pleroma/web/pleroma_api/controllers/chat_controller_test.exs
@@ -1,410 +1,410 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.PleromaAPI.ChatControllerTest do
use Pleroma.Web.ConnCase
alias Pleroma.Chat
alias Pleroma.Chat.MessageReference
alias Pleroma.Object
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.CommonAPI
import Pleroma.Factory
describe "POST /api/v1/pleroma/chats/:id/messages/:message_id/read" do
setup do: oauth_access(["write:chats"])
test "it marks one message as read", %{conn: conn, user: user} do
other_user = insert(:user)
{:ok, create} = CommonAPI.post_chat_message(other_user, user, "sup")
{:ok, _create} = CommonAPI.post_chat_message(other_user, user, "sup part 2")
{:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id)
object = Object.normalize(create, false)
cm_ref = MessageReference.for_chat_and_object(chat, object)
assert cm_ref.unread == true
result =
conn
|> post("/api/v1/pleroma/chats/#{chat.id}/messages/#{cm_ref.id}/read")
|> json_response_and_validate_schema(200)
assert result["unread"] == false
cm_ref = MessageReference.for_chat_and_object(chat, object)
assert cm_ref.unread == false
end
end
describe "POST /api/v1/pleroma/chats/:id/read" do
setup do: oauth_access(["write:chats"])
test "given a `last_read_id`, it marks everything until then as read", %{
conn: conn,
user: user
} do
other_user = insert(:user)
{:ok, create} = CommonAPI.post_chat_message(other_user, user, "sup")
{:ok, _create} = CommonAPI.post_chat_message(other_user, user, "sup part 2")
{:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id)
object = Object.normalize(create, false)
cm_ref = MessageReference.for_chat_and_object(chat, object)
assert cm_ref.unread == true
result =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/pleroma/chats/#{chat.id}/read", %{"last_read_id" => cm_ref.id})
|> json_response_and_validate_schema(200)
assert result["unread"] == 1
cm_ref = MessageReference.for_chat_and_object(chat, object)
assert cm_ref.unread == false
end
end
describe "POST /api/v1/pleroma/chats/:id/messages" do
setup do: oauth_access(["write:chats"])
test "it posts a message to the chat", %{conn: conn, user: user} do
other_user = insert(:user)
{:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id)
result =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/pleroma/chats/#{chat.id}/messages", %{"content" => "Hallo!!"})
|> json_response_and_validate_schema(200)
assert result["content"] == "Hallo!!"
assert result["chat_id"] == chat.id |> to_string()
end
test "it fails if there is no content", %{conn: conn, user: user} do
other_user = insert(:user)
{:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id)
result =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/pleroma/chats/#{chat.id}/messages")
|> json_response_and_validate_schema(400)
assert %{"error" => "no_content"} == result
end
test "it works with an attachment", %{conn: conn, user: user} do
file = %Plug.Upload{
- content_type: "image/jpg",
+ content_type: "image/jpeg",
path: Path.absname("test/fixtures/image.jpg"),
filename: "an_image.jpg"
}
{:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
other_user = insert(:user)
{:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id)
result =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/pleroma/chats/#{chat.id}/messages", %{
"media_id" => to_string(upload.id)
})
|> json_response_and_validate_schema(200)
assert result["attachment"]
end
test "gets MRF reason when rejected", %{conn: conn, user: user} do
clear_config([:mrf_keyword, :reject], ["GNO"])
clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.KeywordPolicy])
other_user = insert(:user)
{:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id)
result =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/pleroma/chats/#{chat.id}/messages", %{"content" => "GNO/Linux"})
|> json_response_and_validate_schema(422)
assert %{"error" => "[KeywordPolicy] Matches with rejected keyword"} == result
end
end
describe "DELETE /api/v1/pleroma/chats/:id/messages/:message_id" do
setup do: oauth_access(["write:chats"])
test "it deletes a message from the chat", %{conn: conn, user: user} do
recipient = insert(:user)
{:ok, message} =
CommonAPI.post_chat_message(user, recipient, "Hello darkness my old friend")
{:ok, other_message} = CommonAPI.post_chat_message(recipient, user, "nico nico ni")
object = Object.normalize(message, false)
chat = Chat.get(user.id, recipient.ap_id)
cm_ref = MessageReference.for_chat_and_object(chat, object)
# Deleting your own message removes the message and the reference
result =
conn
|> put_req_header("content-type", "application/json")
|> delete("/api/v1/pleroma/chats/#{chat.id}/messages/#{cm_ref.id}")
|> json_response_and_validate_schema(200)
assert result["id"] == cm_ref.id
refute MessageReference.get_by_id(cm_ref.id)
assert %{data: %{"type" => "Tombstone"}} = Object.get_by_id(object.id)
# Deleting other people's messages just removes the reference
object = Object.normalize(other_message, false)
cm_ref = MessageReference.for_chat_and_object(chat, object)
result =
conn
|> put_req_header("content-type", "application/json")
|> delete("/api/v1/pleroma/chats/#{chat.id}/messages/#{cm_ref.id}")
|> json_response_and_validate_schema(200)
assert result["id"] == cm_ref.id
refute MessageReference.get_by_id(cm_ref.id)
assert Object.get_by_id(object.id)
end
end
describe "GET /api/v1/pleroma/chats/:id/messages" do
setup do: oauth_access(["read:chats"])
test "it paginates", %{conn: conn, user: user} do
recipient = insert(:user)
Enum.each(1..30, fn _ ->
{:ok, _} = CommonAPI.post_chat_message(user, recipient, "hey")
end)
chat = Chat.get(user.id, recipient.ap_id)
response = get(conn, "/api/v1/pleroma/chats/#{chat.id}/messages")
result = json_response_and_validate_schema(response, 200)
[next, prev] = get_resp_header(response, "link") |> hd() |> String.split(", ")
api_endpoint = "/api/v1/pleroma/chats/"
assert String.match?(
next,
~r(#{api_endpoint}.*/messages\?id=.*&limit=\d+&max_id=.*; rel=\"next\"$)
)
assert String.match?(
prev,
~r(#{api_endpoint}.*/messages\?id=.*&limit=\d+&min_id=.*; rel=\"prev\"$)
)
assert length(result) == 20
response =
get(conn, "/api/v1/pleroma/chats/#{chat.id}/messages?max_id=#{List.last(result)["id"]}")
result = json_response_and_validate_schema(response, 200)
[next, prev] = get_resp_header(response, "link") |> hd() |> String.split(", ")
assert String.match?(
next,
~r(#{api_endpoint}.*/messages\?id=.*&limit=\d+&max_id=.*; rel=\"next\"$)
)
assert String.match?(
prev,
~r(#{api_endpoint}.*/messages\?id=.*&limit=\d+&max_id=.*&min_id=.*; rel=\"prev\"$)
)
assert length(result) == 10
end
test "it returns the messages for a given chat", %{conn: conn, user: user} do
other_user = insert(:user)
third_user = insert(:user)
{:ok, _} = CommonAPI.post_chat_message(user, other_user, "hey")
{:ok, _} = CommonAPI.post_chat_message(user, third_user, "hey")
{:ok, _} = CommonAPI.post_chat_message(user, other_user, "how are you?")
{:ok, _} = CommonAPI.post_chat_message(other_user, user, "fine, how about you?")
chat = Chat.get(user.id, other_user.ap_id)
result =
conn
|> get("/api/v1/pleroma/chats/#{chat.id}/messages")
|> json_response_and_validate_schema(200)
result
|> Enum.each(fn message ->
assert message["chat_id"] == chat.id |> to_string()
end)
assert length(result) == 3
# Trying to get the chat of a different user
conn
|> assign(:user, other_user)
|> get("/api/v1/pleroma/chats/#{chat.id}/messages")
|> json_response_and_validate_schema(404)
end
end
describe "POST /api/v1/pleroma/chats/by-account-id/:id" do
setup do: oauth_access(["write:chats"])
test "it creates or returns a chat", %{conn: conn} do
other_user = insert(:user)
result =
conn
|> post("/api/v1/pleroma/chats/by-account-id/#{other_user.id}")
|> json_response_and_validate_schema(200)
assert result["id"]
end
end
describe "GET /api/v1/pleroma/chats/:id" do
setup do: oauth_access(["read:chats"])
test "it returns a chat", %{conn: conn, user: user} do
other_user = insert(:user)
{:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id)
result =
conn
|> get("/api/v1/pleroma/chats/#{chat.id}")
|> json_response_and_validate_schema(200)
assert result["id"] == to_string(chat.id)
end
end
describe "GET /api/v1/pleroma/chats" do
setup do: oauth_access(["read:chats"])
test "it does not return chats with deleted users", %{conn: conn, user: user} do
recipient = insert(:user)
{:ok, _} = Chat.get_or_create(user.id, recipient.ap_id)
Pleroma.Repo.delete(recipient)
User.invalidate_cache(recipient)
result =
conn
|> get("/api/v1/pleroma/chats")
|> json_response_and_validate_schema(200)
assert length(result) == 0
end
test "it does not return chats with users you blocked", %{conn: conn, user: user} do
recipient = insert(:user)
{:ok, _} = Chat.get_or_create(user.id, recipient.ap_id)
result =
conn
|> get("/api/v1/pleroma/chats")
|> json_response_and_validate_schema(200)
assert length(result) == 1
User.block(user, recipient)
result =
conn
|> get("/api/v1/pleroma/chats")
|> json_response_and_validate_schema(200)
assert length(result) == 0
end
test "it returns all chats", %{conn: conn, user: user} do
Enum.each(1..30, fn _ ->
recipient = insert(:user)
{:ok, _} = Chat.get_or_create(user.id, recipient.ap_id)
end)
result =
conn
|> get("/api/v1/pleroma/chats")
|> json_response_and_validate_schema(200)
assert length(result) == 30
end
test "it return a list of chats the current user is participating in, in descending order of updates",
%{conn: conn, user: user} do
har = insert(:user)
jafnhar = insert(:user)
tridi = insert(:user)
{:ok, chat_1} = Chat.get_or_create(user.id, har.ap_id)
:timer.sleep(1000)
{:ok, _chat_2} = Chat.get_or_create(user.id, jafnhar.ap_id)
:timer.sleep(1000)
{:ok, chat_3} = Chat.get_or_create(user.id, tridi.ap_id)
:timer.sleep(1000)
# bump the second one
{:ok, chat_2} = Chat.bump_or_create(user.id, jafnhar.ap_id)
result =
conn
|> get("/api/v1/pleroma/chats")
|> json_response_and_validate_schema(200)
ids = Enum.map(result, & &1["id"])
assert ids == [
chat_2.id |> to_string(),
chat_3.id |> to_string(),
chat_1.id |> to_string()
]
end
test "it is not affected by :restrict_unauthenticated setting (issue #1973)", %{
conn: conn,
user: user
} do
clear_config([:restrict_unauthenticated, :profiles, :local], true)
clear_config([:restrict_unauthenticated, :profiles, :remote], true)
user2 = insert(:user)
user3 = insert(:user, local: false)
{:ok, _chat_12} = Chat.get_or_create(user.id, user2.ap_id)
{:ok, _chat_13} = Chat.get_or_create(user.id, user3.ap_id)
result =
conn
|> get("/api/v1/pleroma/chats")
|> json_response_and_validate_schema(200)
account_ids = Enum.map(result, &get_in(&1, ["account", "id"]))
assert Enum.sort(account_ids) == Enum.sort([user2.id, user3.id])
end
end
end
diff --git a/test/pleroma/web/pleroma_api/controllers/mascot_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/mascot_controller_test.exs
index e2ead6e15..d6be92869 100644
--- a/test/pleroma/web/pleroma_api/controllers/mascot_controller_test.exs
+++ b/test/pleroma/web/pleroma_api/controllers/mascot_controller_test.exs
@@ -1,73 +1,73 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.PleromaAPI.MascotControllerTest do
use Pleroma.Web.ConnCase
alias Pleroma.User
test "mascot upload" do
%{conn: conn} = oauth_access(["write:accounts"])
non_image_file = %Plug.Upload{
content_type: "audio/mpeg",
path: Path.absname("test/fixtures/sound.mp3"),
filename: "sound.mp3"
}
ret_conn =
conn
|> put_req_header("content-type", "multipart/form-data")
|> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
assert json_response_and_validate_schema(ret_conn, 415)
file = %Plug.Upload{
- content_type: "image/jpg",
+ content_type: "image/jpeg",
path: Path.absname("test/fixtures/image.jpg"),
filename: "an_image.jpg"
}
conn =
conn
|> put_req_header("content-type", "multipart/form-data")
|> put("/api/v1/pleroma/mascot", %{"file" => file})
assert %{"id" => _, "type" => image} = json_response_and_validate_schema(conn, 200)
end
test "mascot retrieving" do
%{user: user, conn: conn} = oauth_access(["read:accounts", "write:accounts"])
# When user hasn't set a mascot, we should just get pleroma tan back
ret_conn = get(conn, "/api/v1/pleroma/mascot")
assert %{"url" => url} = json_response_and_validate_schema(ret_conn, 200)
assert url =~ "pleroma-fox-tan-smol"
# When a user sets their mascot, we should get that back
file = %Plug.Upload{
- content_type: "image/jpg",
+ content_type: "image/jpeg",
path: Path.absname("test/fixtures/image.jpg"),
filename: "an_image.jpg"
}
ret_conn =
conn
|> put_req_header("content-type", "multipart/form-data")
|> put("/api/v1/pleroma/mascot", %{"file" => file})
assert json_response_and_validate_schema(ret_conn, 200)
user = User.get_cached_by_id(user.id)
conn =
conn
|> assign(:user, user)
|> get("/api/v1/pleroma/mascot")
assert %{"url" => url, "type" => "image"} = json_response_and_validate_schema(conn, 200)
assert url =~ "an_image"
end
end
diff --git a/test/pleroma/web/pleroma_api/views/chat_message_reference_view_test.exs b/test/pleroma/web/pleroma_api/views/chat_message_reference_view_test.exs
index 26272c125..f171a1e55 100644
--- a/test/pleroma/web/pleroma_api/views/chat_message_reference_view_test.exs
+++ b/test/pleroma/web/pleroma_api/views/chat_message_reference_view_test.exs
@@ -1,72 +1,72 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.PleromaAPI.ChatMessageReferenceViewTest do
use Pleroma.DataCase
alias Pleroma.Chat
alias Pleroma.Chat.MessageReference
alias Pleroma.Object
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.PleromaAPI.Chat.MessageReferenceView
import Pleroma.Factory
test "it displays a chat message" do
user = insert(:user)
recipient = insert(:user)
file = %Plug.Upload{
- content_type: "image/jpg",
+ content_type: "image/jpeg",
path: Path.absname("test/fixtures/image.jpg"),
filename: "an_image.jpg"
}
{:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
{:ok, activity} = CommonAPI.post_chat_message(user, recipient, "kippis :firefox:")
chat = Chat.get(user.id, recipient.ap_id)
object = Object.normalize(activity)
cm_ref = MessageReference.for_chat_and_object(chat, object)
chat_message = MessageReferenceView.render("show.json", chat_message_reference: cm_ref)
assert chat_message[:id] == cm_ref.id
assert chat_message[:content] == "kippis :firefox:"
assert chat_message[:account_id] == user.id
assert chat_message[:chat_id]
assert chat_message[:created_at]
assert chat_message[:unread] == false
assert match?([%{shortcode: "firefox"}], chat_message[:emojis])
clear_config([:rich_media, :enabled], true)
Tesla.Mock.mock(fn
%{url: "https://example.com/ogp"} ->
%Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/ogp.html")}
end)
{:ok, activity} =
CommonAPI.post_chat_message(recipient, user, "gkgkgk https://example.com/ogp",
media_id: upload.id
)
object = Object.normalize(activity)
cm_ref = MessageReference.for_chat_and_object(chat, object)
chat_message_two = MessageReferenceView.render("show.json", chat_message_reference: cm_ref)
assert chat_message_two[:id] == cm_ref.id
assert chat_message_two[:content] == object.data["content"]
assert chat_message_two[:account_id] == recipient.id
assert chat_message_two[:chat_id] == chat_message[:chat_id]
assert chat_message_two[:attachment]
assert chat_message_two[:unread] == true
assert chat_message_two[:card]
end
end
diff --git a/test/pleroma/web/plugs/uploaded_media_plug_test.exs b/test/pleroma/web/plugs/uploaded_media_plug_test.exs
index 07f52c8cd..7c8313121 100644
--- a/test/pleroma/web/plugs/uploaded_media_plug_test.exs
+++ b/test/pleroma/web/plugs/uploaded_media_plug_test.exs
@@ -1,43 +1,43 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.UploadedMediaPlugTest do
use Pleroma.Web.ConnCase
alias Pleroma.Upload
defp upload_file(context) do
Pleroma.DataCase.ensure_local_uploader(context)
File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg")
file = %Plug.Upload{
- content_type: "image/jpg",
+ content_type: "image/jpeg",
path: Path.absname("test/fixtures/image_tmp.jpg"),
filename: "nice_tf.jpg"
}
{:ok, data} = Upload.store(file)
[%{"href" => attachment_url} | _] = data["url"]
[attachment_url: attachment_url]
end
setup_all :upload_file
test "does not send Content-Disposition header when name param is not set", %{
attachment_url: attachment_url
} do
conn = get(build_conn(), attachment_url)
refute Enum.any?(conn.resp_headers, &(elem(&1, 0) == "content-disposition"))
end
test "sends Content-Disposition header when name param is set", %{
attachment_url: attachment_url
} do
conn = get(build_conn(), attachment_url <> "?name=\"cofe\".gif")
assert Enum.any?(
conn.resp_headers,
&(&1 == {"content-disposition", "filename=\"\\\"cofe\\\".gif\""})
)
end
end
diff --git a/test/pleroma/web/push/impl_test.exs b/test/pleroma/web/push/impl_test.exs
index 6cab46696..7d8cc999a 100644
--- a/test/pleroma/web/push/impl_test.exs
+++ b/test/pleroma/web/push/impl_test.exs
@@ -1,344 +1,344 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Push.ImplTest do
use Pleroma.DataCase
import Pleroma.Factory
alias Pleroma.Notification
alias Pleroma.Object
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.Push.Impl
alias Pleroma.Web.Push.Subscription
setup do
Tesla.Mock.mock(fn
%{method: :post, url: "https://example.com/example/1234"} ->
%Tesla.Env{status: 200}
%{method: :post, url: "https://example.com/example/not_found"} ->
%Tesla.Env{status: 400}
%{method: :post, url: "https://example.com/example/bad"} ->
%Tesla.Env{status: 100}
end)
:ok
end
@sub %{
endpoint: "https://example.com/example/1234",
keys: %{
auth: "8eDyX_uCN0XRhSbY5hs7Hg==",
p256dh:
"BCIWgsnyXDv1VkhqL2P7YRBvdeuDnlwAPT2guNhdIoW3IP7GmHh1SMKPLxRf7x8vJy6ZFK3ol2ohgn_-0yP7QQA="
}
}
@api_key "BASgACIHpN1GYgzSRp"
@message "@Bob: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce sagittis fini..."
test "performs sending notifications" do
user = insert(:user)
user2 = insert(:user)
insert(:push_subscription, user: user, data: %{alerts: %{"mention" => true}})
insert(:push_subscription, user: user2, data: %{alerts: %{"mention" => true}})
insert(:push_subscription,
user: user,
data: %{alerts: %{"follow" => true, "mention" => true}}
)
insert(:push_subscription,
user: user,
data: %{alerts: %{"follow" => true, "mention" => false}}
)
{:ok, activity} = CommonAPI.post(user, %{status: "<Lorem ipsum dolor sit amet."})
notif =
insert(:notification,
user: user,
activity: activity,
type: "mention"
)
assert Impl.perform(notif) == {:ok, [:ok, :ok]}
end
@tag capture_log: true
test "returns error if notif does not match " do
assert Impl.perform(%{}) == {:error, :unknown_type}
end
test "successful message sending" do
assert Impl.push_message(@message, @sub, @api_key, %Subscription{}) == :ok
end
@tag capture_log: true
test "fail message sending" do
assert Impl.push_message(
@message,
Map.merge(@sub, %{endpoint: "https://example.com/example/bad"}),
@api_key,
%Subscription{}
) == :error
end
test "delete subscription if result send message between 400..500" do
subscription = insert(:push_subscription)
assert Impl.push_message(
@message,
Map.merge(@sub, %{endpoint: "https://example.com/example/not_found"}),
@api_key,
subscription
) == :ok
refute Pleroma.Repo.get(Subscription, subscription.id)
end
test "deletes subscription when token has been deleted" do
subscription = insert(:push_subscription)
Pleroma.Repo.delete(subscription.token)
refute Pleroma.Repo.get(Subscription, subscription.id)
end
test "renders title and body for create activity" do
user = insert(:user, nickname: "Bob")
{:ok, activity} =
CommonAPI.post(user, %{
status:
"<span>Lorem ipsum dolor sit amet</span>, consectetur :firefox: adipiscing elit. Fusce sagittis finibus turpis."
})
object = Object.normalize(activity)
assert Impl.format_body(
%{
activity: activity
},
user,
object
) ==
"@Bob: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce sagittis fini..."
assert Impl.format_title(%{activity: activity, type: "mention"}) ==
"New Mention"
end
test "renders title and body for follow activity" do
user = insert(:user, nickname: "Bob")
other_user = insert(:user)
{:ok, _, _, activity} = CommonAPI.follow(user, other_user)
object = Object.normalize(activity, false)
assert Impl.format_body(%{activity: activity, type: "follow"}, user, object) ==
"@Bob has followed you"
assert Impl.format_title(%{activity: activity, type: "follow"}) ==
"New Follower"
end
test "renders title and body for announce activity" do
user = insert(:user)
{:ok, activity} =
CommonAPI.post(user, %{
status:
"<span>Lorem ipsum dolor sit amet</span>, consectetur :firefox: adipiscing elit. Fusce sagittis finibus turpis."
})
{:ok, announce_activity} = CommonAPI.repeat(activity.id, user)
object = Object.normalize(activity)
assert Impl.format_body(%{activity: announce_activity}, user, object) ==
"@#{user.nickname} repeated: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce sagittis fini..."
assert Impl.format_title(%{activity: announce_activity, type: "reblog"}) ==
"New Repeat"
end
test "renders title and body for like activity" do
user = insert(:user, nickname: "Bob")
{:ok, activity} =
CommonAPI.post(user, %{
status:
"<span>Lorem ipsum dolor sit amet</span>, consectetur :firefox: adipiscing elit. Fusce sagittis finibus turpis."
})
{:ok, activity} = CommonAPI.favorite(user, activity.id)
object = Object.normalize(activity)
assert Impl.format_body(%{activity: activity, type: "favourite"}, user, object) ==
"@Bob has favorited your post"
assert Impl.format_title(%{activity: activity, type: "favourite"}) ==
"New Favorite"
end
test "renders title for create activity with direct visibility" do
user = insert(:user, nickname: "Bob")
{:ok, activity} =
CommonAPI.post(user, %{
visibility: "direct",
status: "This is just between you and me, pal"
})
assert Impl.format_title(%{activity: activity}) ==
"New Direct Message"
end
describe "build_content/3" do
test "builds content for chat messages" do
user = insert(:user)
recipient = insert(:user)
{:ok, chat} = CommonAPI.post_chat_message(user, recipient, "hey")
object = Object.normalize(chat, false)
[notification] = Notification.for_user(recipient)
res = Impl.build_content(notification, user, object)
assert res == %{
body: "@#{user.nickname}: hey",
title: "New Chat Message"
}
end
test "builds content for chat messages with no content" do
user = insert(:user)
recipient = insert(:user)
file = %Plug.Upload{
- content_type: "image/jpg",
+ content_type: "image/jpeg",
path: Path.absname("test/fixtures/image.jpg"),
filename: "an_image.jpg"
}
{:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
{:ok, chat} = CommonAPI.post_chat_message(user, recipient, nil, media_id: upload.id)
object = Object.normalize(chat, false)
[notification] = Notification.for_user(recipient)
res = Impl.build_content(notification, user, object)
assert res == %{
body: "@#{user.nickname}: (Attachment)",
title: "New Chat Message"
}
end
test "hides contents of notifications when option enabled" do
user = insert(:user, nickname: "Bob")
user2 =
insert(:user, nickname: "Rob", notification_settings: %{hide_notification_contents: true})
{:ok, activity} =
CommonAPI.post(user, %{
visibility: "direct",
status: "<Lorem ipsum dolor sit amet."
})
notif = insert(:notification, user: user2, activity: activity)
actor = User.get_cached_by_ap_id(notif.activity.data["actor"])
object = Object.normalize(activity)
assert Impl.build_content(notif, actor, object) == %{
body: "New Direct Message"
}
{:ok, activity} =
CommonAPI.post(user, %{
visibility: "public",
status: "<Lorem ipsum dolor sit amet."
})
notif = insert(:notification, user: user2, activity: activity, type: "mention")
actor = User.get_cached_by_ap_id(notif.activity.data["actor"])
object = Object.normalize(activity)
assert Impl.build_content(notif, actor, object) == %{
body: "New Mention"
}
{:ok, activity} = CommonAPI.favorite(user, activity.id)
notif = insert(:notification, user: user2, activity: activity, type: "favourite")
actor = User.get_cached_by_ap_id(notif.activity.data["actor"])
object = Object.normalize(activity)
assert Impl.build_content(notif, actor, object) == %{
body: "New Favorite"
}
end
test "returns regular content when hiding contents option disabled" do
user = insert(:user, nickname: "Bob")
user2 =
insert(:user, nickname: "Rob", notification_settings: %{hide_notification_contents: false})
{:ok, activity} =
CommonAPI.post(user, %{
visibility: "direct",
status:
"<span>Lorem ipsum dolor sit amet</span>, consectetur :firefox: adipiscing elit. Fusce sagittis finibus turpis."
})
notif = insert(:notification, user: user2, activity: activity)
actor = User.get_cached_by_ap_id(notif.activity.data["actor"])
object = Object.normalize(activity)
assert Impl.build_content(notif, actor, object) == %{
body:
"@Bob: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce sagittis fini...",
title: "New Direct Message"
}
{:ok, activity} =
CommonAPI.post(user, %{
visibility: "public",
status:
"<span>Lorem ipsum dolor sit amet</span>, consectetur :firefox: adipiscing elit. Fusce sagittis finibus turpis."
})
notif = insert(:notification, user: user2, activity: activity, type: "mention")
actor = User.get_cached_by_ap_id(notif.activity.data["actor"])
object = Object.normalize(activity)
assert Impl.build_content(notif, actor, object) == %{
body:
"@Bob: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce sagittis fini...",
title: "New Mention"
}
{:ok, activity} = CommonAPI.favorite(user, activity.id)
notif = insert(:notification, user: user2, activity: activity, type: "favourite")
actor = User.get_cached_by_ap_id(notif.activity.data["actor"])
object = Object.normalize(activity)
assert Impl.build_content(notif, actor, object) == %{
body: "@Bob has favorited your post",
title: "New Favorite"
}
end
end
end

File Metadata

Mime Type
text/x-diff
Expires
Sat, Oct 31, 9:07 PM (14 h, 14 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1801827
Default Alt Text
(329 KB)

Event Timeline